krixon / rules
将文本规则转换为规范对象
Requires
- php: >=7.2
- ext-ctype: *
- ext-intl: *
- ext-mbstring: *
Requires (Dev)
- ext-json: *
- php-coveralls/php-coveralls: ^2.1
- phpunit/phpunit: ^8.2
This package is auto-updated.
Last update: 2024-09-19 22:34:06 UTC
README
用于定义和构建规范模式对象的一种简单语言。
先决条件
- PHP 7.2+
安装
通过 composer 安装
要使用 Composer 安装此库,请运行以下命令
$ composer require krixon/rules
您可以在 Packagist 上找到此库。
从源代码安装
# HTTP $ git clone https://github.com/krixon/rules.git # SSH $ git clone git@github.com:krixon/rules.git
支持的语法
有关规则语法的详细信息,请参阅语法文档。
使用方法
使用此库的主要任务是实现 BaseCompiler::generate()
。此方法有以下签名
public function generate(ComparisonNode $comparison) : Specification
其任务是生成一个从 ComparisonNode
AST 对象生成的 Specification
对象。
ComparisonNode
由一个 IdentifierNode
组成,用于识别应该检查规范的数据,以及一个包含要比较的值的 LiteralNode
。它还包含有关比较类型(等于,大于等)的信息。
例如,假设您有一个可以应用于 User
对象的以下规范
class EmailAddressMatches implements Specification { private $email; public function __construct(string $email) { $this->email = $email; } public function isSatisfiedBy($value) : bool { return $value instanceof User && $value->hasEmailAddress($this->email); } }
您可以为此规范定义一个规则,例如 email is "karl.rixon@gmail.com"
。
在这个规则中,email
是一个标识符,它引用用户的电子邮件地址。如何解释给定的标识符取决于您。字符串值 email
在解析期间被转换为 IdentifierNode
AST 节点。此节点可以通过 ComparisonNode::identifier()
访问。
比较操作符是 is
,这意味着“等于”。您可以使用 ComparisonNode::isEquals()
、ComparisonNode::isLessThan()
等来确定比较类型。
最后,karl.rixon@gmail.com
在解析期间被转换为 StringNode
AST 节点。此节点可以通过 ComparisonNode::value()
访问。
基于以上内容,BaseCompiler::generate()
方法可能如下实现
class MyCompiler extends BaseCompiler { public function generate(ComparisonNode $comparison) : Specification { $identifier = $comparison->identifierFullName(); if (strtolower($indentifier) !== 'email') { throw CompilerError::unknownIdentifier($identifier); } if (!$comparison->isEquals()) { throw CompilerError::unsupportedComparisonType($comparison->type(), $identifier); } return new EmailAddressMatches($comparison->literalValue()); } }
委托生成任务
虽然简单情况下扩展 BaseCompiler
很方便,但当您需要支持许多规范时,它会变得复杂。在这种情况下,您可能希望将生成工作委托给专用服务。
为此提供了 DelegatingCompiler
类。要使用它,首先创建一个实现 SpecificationGenerator
接口的类,该接口定义了一个方法
public function attempt(ComparisonNode $comparison) : ?Specification;
这与 BaseCompiler::generate()
非常相似,但是返回 Specification
是可选的。
然后,将您的类的实例注册到 DelegatingCompiler
$generator = new EmailAddressGenerator(); $compiler = new DelegatingCompiler($generator);
当调用 DelegatingCompiler::compile()
时,DelegatingCompiler
将遍历所有已注册的生成器,并对每个 ComparisonNode
调用 SpecificationGenerator::attempt()
。
通过 DelegatingCompiler
的构造函数提供的所有 SpecificationGenerator
具有相同的 0
优先级,但它们也可以注册为具有显式优先级
$generator = new EmailAddressGenerator(); $compiler = new DelegatingCompiler(); $compiler->register($generator, 100); // Priority of 100.
具有更高优先级的 SpecificationGenerator
将首先被调用。
该库提供了一些内置规范和相应的生成器,如果需要可以使用。这些也易于通过自定义逻辑进行扩展。
否定比较
ComparisonNode
不暴露否定比较,如不等于
和不匹配
。然而,通过在比较运算符前添加not
,这种否定比较在语言中是受支持的。
email not is "karl.rixon@gmail.com"
address.county not matches "/(east|west)\s+sussex/i"
age not > 5
您无需编写任何代码来处理这些情况,因为编译器将根据非否定比较生成一个Specification
,然后将其结果包装在一个Not
规范中,该规范简单地反转被包装的Specification
返回的Specification::isSatisfiedBy
的结果。
可以使用简写语法not is
,只需省略is
即可。
email not "karl.rixon@gmail.com"