本篇文章给大家带来的内容是关于规则引擎RulerZ用法及实现原理(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
废话不多说,rulerz的官方地址是:https://github.com/K-Phoen/ru...
注意,本例中只拿普通数组做例子进行分析
1. 简介
RulerZ是一个用php实现的composer依赖包,目的是实现一个数据过滤规则引擎。RulerZ不仅支持数组过滤,也支持一些市面上常见的ORM,如Eloquent、Doctrine等,也支持Solr搜索引擎。
这是一个缺少中文官方文档的开源包,当然由于star数比较少,可能作者也觉得没必要。
2.安装
在你的项目composer.json所在目录下运行:composer require 'kphoen/rulerz'
3.使用 - 过滤
现有数组如下:$players = [
['pseudo' => 'Joe', 'fullname' => 'Joe la frite', 'gender' => 'M', 'points' => 2500],
['pseudo' => 'Moe', 'fullname' => 'Moe, from the bar!', 'gender' => 'M', 'points' => 1230],
['pseudo' => 'Alice', 'fullname' => 'Alice, from... you know.', 'gender' => 'F', 'points' => 9001],
];
初始化引擎:use RulerZ\Compiler\Compiler;
use RulerZ\Target;
use RulerZ\RulerZ;
// compiler
$compiler = Compiler::create();
// RulerZ engine
$rulerz = new RulerZ(
$compiler, [
new Target\Native\Native([ // 请注意,这里是添加目标编译器,处理数组类型的数据源时对应的是Native
'length' => 'strlen'
]),
]
);
创建一条规则:$rule = "gender = :gender and points > :min_points'
将参数和规则交给引擎分析。$parameters = [
'min_points' => 30,
'gender' => 'F',
];
$result = iterator_to_array(
$rulerz->filter($players, $rule, $parameters) // the parameters can be omitted if empty
);
// result 是一个过滤后的数组
array:1 [▼
0 => array:4 [▼
"pseudo" => "Alice"
"fullname" => "Alice, from... you know."
"gender" => "F"
"points" => 9001
]
]
4.使用 - 判断是否满足规则$rulerz->satisfies($player, $rule, $parameters);
// 返回布尔值,true表示满足
5.底层代码解读
下面,让我们看看从创建编译器开始,到最后出结果的过程中发生了什么。
1.Compiler::create();
这一步是实例化一个FileEvaluator类,这个类默认会将本地的系统临时目录当做下一步临时类文件读写所在目录,文件类里包含一个has()方法和一个write()方法。文件类如下:<?php
declare(strict_types=1);
namespace RulerZ\Compiler;
class NativeFilesystem implements Filesystem
{
public function has(string $filePath): bool
{
return file_exists($filePath);
}
public function write(string $filePath, string $content): void
{
file_put_contents($filePath, $content, LOCK_EX);
}
}
2.初始化RulerZ引擎,new RulerZ()
先看一下RulerZ的构建方法:public function __construct(Compiler $compiler, array $compilationTargets = [])
{
$this->compiler = $compiler;
foreach ($compilationTargets as $targetCompiler) {
$this->registerCompilationTarget($targetCompiler);
}
}
这里的第一个参数,就是刚刚的编译器类,第二个是目标编译器类(实际处理数据源的),因为我们选择的是数组,所以这里的目标编译器是Native,引擎会将这个目标编译类放到自己的属性$compilationTargets。public function registerCompilationTarget(CompilationTarget $compilationTarget): void
{
$this->compilationTargets[] = $compilationTarget;
}
3.运用filter或satisfies方法
这一点便是核心了。
以filter为例:public function filter($target, string $rule, array $parameters = [], array $executionContext = [])
{
$targetCompiler = $this->findTargetCompiler($target, CompilationTarget::MODE_FILTER);
$compilationContext = $targetCompiler->createCompilationContext($target);
$executor = $this->compiler->compile($rule, $targetCompiler, $compilationContext);
return $executor->filter($target, $parameters, $targetCompiler->getOperators()->getOperators(), new ExecutionContext($executionContext));
}
第一步会检查目标编译器是否支持筛选模式。
第二步创建编译上下文,这个一般统一是Context类实例public function createCompilationContext($target): Context
{
return new Context();
}
第三步,执行compiler的compile()方法public function compile(string $rule, CompilationTarget $target, Context $context): Executor
{
$context['rule_identifier'] = $this->getRuleIdentifier($target, $context, $rule);
$context['executor_classname'] = 'Executor_'.$context['rule_identifier'];
$context['executor_fqcn'] = '\RulerZ\Compiled\Executor\\Executor_'.$context['rule_identifier'];
if (!class_exists($context['executor_fqcn'], false)) {
$compiler = function () use ($rule, $target, $context) {
return $this->compileToSource($rule, $target, $context);
};
$this->evaluator->evaluate($context['rule_identifier'], $compiler);
}
return new $context['executor_fqcn']();<