本篇文章給大家?guī)淼膬?nèi)容是關(guān)于規(guī)則引擎RulerZ用法及實(shí)現(xiàn)原理(代碼示例),有一定的參考價(jià)值,有需要的朋友可以參考一下,希望對你有所幫助。
廢話不多說,rulerz的官方地址是:https://github.com/K-Phoen/ru...
注意,本例中只拿普通數(shù)組做例子進(jìn)行分析
1. 簡介
RulerZ是一個(gè)用php實(shí)現(xiàn)的composer依賴包,目的是實(shí)現(xiàn)一個(gè)數(shù)據(jù)過濾規(guī)則引擎。RulerZ不僅支持?jǐn)?shù)組過濾,也支持一些市面上常見的ORM,如Eloquent、Doctrine等,也支持Solr搜索引擎。
這是一個(gè)缺少中文官方文檔的開源包,當(dāng)然由于star數(shù)比較少,可能作者也覺得沒必要。
2.安裝
在你的項(xiàng)目composer.json所在目錄下運(yùn)行:
composer require 'kphoen/rulerz'
3.使用 - 過濾
現(xiàn)有數(shù)組如下:
- $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([ // 請注意,這里是添加目標(biāo)編譯器,處理數(shù)組類型的數(shù)據(jù)源時(shí)對應(yīng)的是Native
- 'length' => 'strlen'
- ]),
- ]
- );
創(chuàng)建一條規(guī)則:
$rule = "gender = :gender and points > :min_points'
將參數(shù)和規(guī)則交給引擎分析。
- $parameters = [
- 'min_points' => 30,
- 'gender' => 'F',
- ];
- $result = iterator_to_array(
- $rulerz->filter($players, $rule, $parameters) // the parameters can be omitted if empty
- );
- // result 是一個(gè)過濾后的數(shù)組
- array:1 [▼
- 0 => array:4 [▼
- "pseudo" => "Alice"
- "fullname" => "Alice, from... you know."
- //Vevb.com
- "gender" => "F"
- "points" => 9001
- ]
- ]
4.使用 - 判斷是否滿足規(guī)則
$rulerz->satisfies($player, $rule, $parameters);
// 返回布爾值,true表示滿足
5.底層代碼解讀
下面,讓我們看看從創(chuàng)建編譯器開始,到最后出結(jié)果的過程中發(fā)生了什么。
1.Compiler::create();
這一步是實(shí)例化一個(gè)FileEvaluator類,這個(gè)類默認(rèn)會將本地的系統(tǒng)臨時(shí)目錄當(dāng)做下一步臨時(shí)類文件讀寫所在目錄,文件類里包含一個(gè)has()方法和一個(gè)write()方法。文件類如下:
- declare(strict_types=1);
- namespace RulerZ/Compiler;
- class NativeFilesystem implements Filesystem
- {
- public function has(string $filePath): bool
- {
- return file_exists($filePath);
- }
- //Vevb.com
- public function write(string $filePath, string $content): void
- {
- file_put_contents($filePath, $content, LOCK_EX);
- }
- }
2.初始化RulerZ引擎,new RulerZ()
先看一下RulerZ的構(gòu)建方法:
- public function __construct(Compiler $compiler, array $compilationTargets = [])
- {
- $this->compiler = $compiler;
- foreach ($compilationTargets as $targetCompiler) {
- $this->registerCompilationTarget($targetCompiler);
- }
- }
這里的第一個(gè)參數(shù),就是剛剛的編譯器類,第二個(gè)是目標(biāo)編譯器類(實(shí)際處理數(shù)據(jù)源的),因?yàn)槲覀冞x擇的是數(shù)組,所以這里的目標(biāo)編譯器是Native,引擎會將這個(gè)目標(biāo)編譯類放到自己的屬性$compilationTargets。
- public function registerCompilationTarget(CompilationTarget $compilationTarget): void
- {
- $this->compilationTargets[] = $compilationTarget;
- }
3.運(yùn)用filter或satisfies方法
這一點(diǎn)便是核心了。
以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));
- }
第一步會檢查目標(biāo)編譯器是否支持篩選模式。
第二步創(chuàng)建編譯上下文,這個(gè)一般統(tǒng)一是Context類實(shí)例
- public function createCompilationContext($target): Context
- {
- return new Context();
- }
第三步,執(zhí)行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']();
- }
- protected function getRuleIdentifier(CompilationTarget $compilationTarget, Context $context, string $rule): string
- {
- return hash('crc32b', get_class($compilationTarget).$rule.$compilationTarget->getRuleIdentifierHint($rule, $context));
- }
- protected function compileToSource(string $rule, CompilationTarget $compilationTarget, Context $context): string
- {
- $ast = $this->parser->parse($rule);
- $executorModel = $compilationTarget->compile($ast, $context);
- $flattenedTraits = implode(PHP_EOL, array_map(function ($trait) {
- return "/t".'use //'.ltrim($trait, '//').';';
- }, $executorModel->getTraits()));
- $extraCode = '';
- foreach ($executorModel->getCompiledData() as $key => $value) {
- $extraCode .= sprintf('private $%s = %s;'.PHP_EOL, $key, var_export($value, true));
- }
- $commentedRule = str_replace(PHP_EOL, PHP_EOL.' // ', $rule);
- return <<<EXECUTOR
- namespace RulerZ/Compiled/Executor;
- use RulerZ/Executor/Executor;
- class {$context['executor_classname']} implements Executor
- {
- $flattenedTraits
- $extraCode
- // $commentedRule
- protected function execute(/$target, array /$operators, array /$parameters)
- {
- return {$executorModel->getCompiledRule()};
- }
- }
- EXECUTOR;
- }
這段代碼會依照crc13算法生成一個(gè)哈希串和Executor拼接作為執(zhí)行器臨時(shí)類的名稱,并將執(zhí)行器相關(guān)代碼寫進(jìn)上文提到的臨時(shí)目錄中去。生成的代碼如下:
- // /private/var/folders/w_/sh4r42wn4_b650l3pc__fh7h0000gp/T/rulerz_executor_ff2800e8
- <?php
- namespace RulerZ/Compiled/Executor;
- use RulerZ/Executor/Executor;
- class Executor_ff2800e8 implements Executor
- {
- use /RulerZ/Executor/ArrayTarget/FilterTrait;
- use /RulerZ/Executor/ArrayTarget/SatisfiesTrait;
- use /RulerZ/Executor/ArrayTarget/ArgumentUnwrappingTrait;
- // gender = :gender and points > :min_points and points > :min_points
- protected function execute($target, array $operators, array $parameters)
- {
- return ($this->unwrapArgument($target["gender"]) == $parameters["gender"] && ($this->unwrapArgument($target["points"]) > $parameters["min_points"] && $this->unwrapArgument($target["points"]) > $parameters["min_points"]));
- }
- }
這個(gè)臨時(shí)類文件就是最后要執(zhí)行過濾動作的類。
FilterTrait中的filter方法是首先被執(zhí)行的,里面會根據(jù)execute返回的布爾值來判斷,是否通過迭代器返回符合條件的行。
execute方法就是根據(jù)具體的參數(shù)和操作符挨個(gè)判斷每行中對應(yīng)的cell是否符合判斷來返回true/false。
- public function filter($target, array $parameters, array $operators, ExecutionContext $context)
- {
- return IteratorTools::fromGenerator(function () use ($target, $parameters, $operators) {
- foreach ($target as $row) {
- $targetRow = is_array($row) ? $row : new ObjectContext($row);
- if ($this->execute($targetRow, $operators, $parameters)) {
- yield $row;
- }
- }
- });
- }
satisfies和filter基本邏輯類似,只是最后satisfies是執(zhí)行單條判斷。
有一個(gè)問題,我們的編譯器是如何知道我們設(shè)立的操作規(guī)則$rule的具體含義的,如何parse的?
這就涉及另一個(gè)問題了,抽象語法樹(AST)。
Go further - 抽象語法樹
我們都知道php zend引擎在解讀代碼的過程中有一個(gè)過程是語法和詞法分析,這個(gè)過程叫做parser,中間會將代碼轉(zhuǎn)化為抽象語法樹,這是引擎能夠讀懂代碼的關(guān)鍵步驟。
同樣,我們在寫一條規(guī)則字符串的時(shí)候,代碼如何能夠明白我們寫的是什么呢?那就是抽象語法樹。
以上面的規(guī)則為例:
gender = :gender and points > :min_points
這里, =、and、>都是操作符,但是機(jī)器并不知道他們是操作符,也不知道其他字段是什么含義。
于是rulerz使用自己的語法模板。
首先是默認(rèn)定義了幾個(gè)操作符。
- <?php
- declare(strict_types=1);
- namespace RulerZ/Target/Native;
- use RulerZ/Target/Operators/Definitions;
- class NativeOperators
- {
- public static function create(Definitions $customOperators): Definitions
- {
- $defaultInlineOperators = [
- 'and' => function ($a, $b) {
- return sprintf('(%s && %s)', $a, $b);
- },
- 'or' => function ($a, $b) {
- return sprintf('(%s || %s)', $a, $b);
- },
- 'not' => function ($a) {
- return sprintf('!(%s)', $a);
- },
- '=' => function ($a, $b) {
- return sprintf('%s == %s', $a, $b);
- },
- 'is' => function ($a, $b) {
- return sprintf('%s === %s', $a, $b);
- },
- '!=' => function ($a, $b) {
- return sprintf('%s != %s', $a, $b);
- },
- '>' => function ($a, $b) {
- return sprintf('%s > %s', $a, $b);
- },
- '>=' => function ($a, $b) {
- return sprintf('%s >= %s', $a, $b);
- },
- '<' => function ($a, $b) {
- return sprintf('%s < %s', $a, $b);
- },
- '<=' => function ($a, $b) {
- return sprintf('%s <= %s', $a, $b);
- },
- 'in' => function ($a, $b) {
- return sprintf('in_array(%s, %s)', $a, $b);
- },
- ];
- $defaultOperators = [
- 'sum' => function () {
- return array_sum(func_get_args());
- },
- ];
- $definitions = new Definitions($defaultOperators, $defaultInlineOperators);
- return $definitions->mergeWith($customOperators);
- }
- }
在RulerZParserParser中,有如下方法:
- public function parse($rule)
- {
- if ($this->parser === null) {
- $this->parser = Compiler/Llk::load(
- new File/Read(__DIR__.'/../Grammar.pp')
- );
- }
- $this->nextParameterIndex = 0;
- return $this->visit($this->parser->parse($rule));
- }
這里要解讀一個(gè)核心語法文件:
- //
- // Hoa
- //
- //
- // @license
- //
- // New BSD License
- //
- // Copyright © 2007-2015, Ivan Enderlin. All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are met:
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above copyright
- // notice, this list of conditions and the following disclaimer in the
- // documentation and/or other materials provided with the distribution.
- // * Neither the name of the Hoa nor the names of its contributors may be
- // used to endorse or promote products derived from this software without
- // specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
- // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- // POSSIBILITY OF SUCH DAMAGE.
- //
- // Inspired from /Hoa/Ruler/Grammar.
- //
- // @author Stéphane Py <stephane.py@hoa-project.net>
- // @author Ivan Enderlin <ivan.enderlin@hoa-project.net>
- // @author Kévin Gomez <contact@kevingomez.fr>
- // @copyright Copyright © 2007-2015 Stéphane Py, Ivan Enderlin, Kévin Gomez.
- // @license New BSD License
- %skip space /s
- // Scalars.
- %token true (?i)true
- %token false (?i)false
- %token null (?i)null
- // Logical operators
- %token not (?i)not/b
- %token and (?i)and/b
- %token or (?i)or/b
- %token xor (?i)xor/b
- // Value
- %token string ("|')(.*?)(?<!//)/1
- %token float -?/d+/./d+
- %token integer -?/d+
- %token parenthesis_ /(
- %token _parenthesis /)
- %token bracket_ /[
- %token _bracket /]
- %token comma ,
- %token dot /.
- %token positional_parameter /?
- %token named_parameter :[a-z-A-Z0-9_]+
- %token identifier [^/s/(/)/[/],/.]+
- #expression:
- logical_operation()
- logical_operation:
- operation()
- ( ( ::and:: #and | ::or:: #or | ::xor:: #xor ) logical_operation() )?
- operation:
- operand() ( <identifier> logical_operation() #operation )?
- operand:
- ::parenthesis_:: logical_operation() ::_parenthesis::
- | value()
- parameter:
- <positional_parameter>
- | <named_parameter>
- value:
- ::not:: logical_operation() #not
- | <true> | <false> | <null> | <float> | <integer> | <string>
- | parameter()
- | variable()
- | array_declaration()
- | function_call()
- variable:
- <identifier> ( object_access() #variable_access )*
- object_access:
- ::dot:: <identifier> #attribute_access
- #array_declaration:
- ::bracket_:: value() ( ::comma:: value() )* ::_bracket::
- #function_call:
- <identifier> ::parenthesis_::
- ( logical_operation() ( ::comma:: logical_operation() )* )?
- ::_parenthesis::
上面Llk::load方法會加載這個(gè)基礎(chǔ)語法內(nèi)容并解析出片段tokens,tokens解析的邏輯就是正則匹配出我們需要的一些操作符和基礎(chǔ)標(biāo)識符,并將對應(yīng)的正則表達(dá)式提取出來:
- array:1 [▼
- "default" => array:20 [▼
- "skip" => "/s"
- "true" => "(?i)true"
- "false" => "(?i)false"
- "null" => "(?i)null"
- "not" => "(?i)not/b"
- "and" => "(?i)and/b"
- "or" => "(?i)or/b"
- "xor" => "(?i)xor/b"
- "string" => "("|')(.*?)(?<!//)/1"
- "float" => "-?/d+/./d+"
- "integer" => "-?/d+"
- "parenthesis_" => "/("
- "_parenthesis" => "/)"
- "bracket_" => "/["
- "_bracket" => "/]"
- "comma" => ","
- "dot" => "/."
- "positional_parameter" => "/?"
- "named_parameter" => ":[a-z-A-Z0-9_]+"
- "identifier" => "[^/s/(/)/[/],/.]+"
- ]
- ]
這一步也會生成一個(gè)rawRules
- array:10 [▼
- "#expression" => " logical_operation()"
- "logical_operation" => " operation() ( ( ::and:: #and | ::or:: #or | ::xor:: #xor ) logical_operation() )?"
- "operation" => " operand() ( <identifier> logical_operation() #operation )?"
- "operand" => " ::parenthesis_:: logical_operation() ::_parenthesis:: | value()"
- "parameter" => " <positional_parameter> | <named_parameter>"
- "value" => " ::not:: logical_operation() #not | <true> | <false> | <null> | <float> | <integer> | <string> | parameter() | variable() | array_declaration() | function_call( ?"
- "variable" => " <identifier> ( object_access() #variable_access )*"
- "object_access" => " ::dot:: <identifier> #attribute_access"
- "#array_declaration" => " ::bracket_:: value() ( ::comma:: value() )* ::_bracket::"
- "#function_call" => " <identifier> ::parenthesis_:: ( logical_operation() ( ::comma:: logical_operation() )* )? ::_parenthesis::"
- ]
這個(gè)rawRules會通過analyzer類的analyzeRules方法解析替換里面的::表示的空位,根據(jù)$_ppLexemes屬性的值,Compiler/Llk/Lexer()詞法解析器會將rawRules數(shù)組每一個(gè)元素解析放入雙向鏈表?xiàng)?SplStack)中,然后再通過對該棧插入和刪除操作,形成一個(gè)包含所有操作符和token實(shí)例的數(shù)組$rules。
- array:54 [▼
- 0 => Concatenation {#64 ?}
- "expression" => Concatenation {#65 ▼
- #_name: "expression"
- #_children: array:1 [▼
- 0 => 0
- ]
- #_nodeId: "#expression"
- #_nodeOptions: []
- #_defaultId: "#expression"
- #_defaultOptions: []
- #_pp: " logical_operation()"
- #_transitional: false
- }
- 2 => Token {#62 ?}
- 3 => Concatenation {#63 ▼
- #_name: 3
- #_children: array:1 [▼
- 0 => 2
- ]
- #_nodeId: "#and"
- #_nodeOptions: []
- #_defaultId: null
- #_defaultOptions: []
- #_pp: null
- #_transitional: true
- }
- 4 => Token {#68 ?}
- 5 => Concatenation {#69 ?}
- 6 => Token {#70 ?}
- 7 => Concatenation {#71 ?}
- 8 => Choice {#72 ?}
- 9 => Concatenation {#73 ?}
- 10 => Repetition {#74 ?}
- "logical_operation" => Concatenation {#75 ?}
- 12 => Token {#66 ?}
- 13 => Concatenation {#67 ?}
- 14 => Repetition {#78 ?}
- "operation" => Concatenation {#79 ?}
- 16 => Token {#76 ?}
- 17 => Token {#77 ?}
- 18 => Concatenation {#82 ?}
- "operand" => Choice {#83 ?}
- 20 => Token {#80 ?}
- 21 => Token {#81 ▼
- #_tokenName: "named_parameter"
- #_namespace: null
- #_regex: null
- #_ast: null
- #_value: null
- #_kept: true
- #_unification: -1
- #_name: 21
- #_children: null
- #_nodeId: null
- #_nodeOptions: []
- #_defaultId: null
- #_defaultOptions: []
- #_pp: null
- #_transitional: true
- }
- "parameter" => Choice {#86 ?}
- 23 => Token {#84 ?}
- 24 => Concatenation {#85 ?}
- 25 => Token {#89 ?}
- 26 => Token {#90 ?}
- 27 => Token {#91 ?}
- 28 => Token {#92 ?}
- 29 => Token {#93 ?}
- 30 => Token {#94 ?}
- "value" => Choice {#95 ?}
- 32 => Token {#87 ?}
- 33 => Concatenation {#88 ?}
- 34 => Repetition {#98 ?}
- "variable" => Concatenation {#99 ?}
- 36 => Token {#96 ?}
- 37 => Token {#97 ?}
- "object_access" => Concatenation {#102 ?}
- 39 => Token {#100 ?}
- 40 => Token {#101 ?}
- 41 => Concatenation {#105 ?}
- 42 => Repetition {#106 ?}
- 43 => Token {#107 ?}
- "array_declaration" => Concatenation {#108 ?}
- 45 => Token {#103 ?}
- 46 => Token {#104 ?}
- 47 => Token {#111 ?}
- 48 => Concatenation {#112 ?}
- 49 => Repetition {#113 ?}
- 50 => Concatenation {#114 ?}
- 51 => Repetition {#115 ?}
- 52 => Token {#116 ?}
- "function_call" => Concatenation {#117 ?}
- ]
然后返回HoaCompilerLlkParser實(shí)例,這個(gè)實(shí)例有一個(gè)parse方法,正是此方法構(gòu)成了一個(gè)語法樹。
- public function parse($text, $rule = null, $tree = true)
- {
- $k = 1024;
- if (isset($this->_pragmas['parser.lookahead'])) {
- $k = max(0, intval($this->_pragmas['parser.lookahead']));
- }
- $lexer = new Lexer($this->_pragmas);
- $this->_tokenSequence = new Iterator/Buffer(
- $lexer->lexMe($text, $this->_tokens),
- $k
- );
- $this->_tokenSequence->rewind();
- $this->_errorToken = null;
- $this->_trace = [];
- $this->_todo = [];
- if (false === array_key_exists($rule, $this->_rules)) {
- $rule = $this->getRootRule();
- }
- $closeRule = new Rule/Ekzit($rule, 0);
- $openRule = new Rule/Entry($rule, 0, [$closeRule]);
- $this->_todo = [$closeRule, $openRule];
- do {
- $out = $this->unfold();
- if (null !== $out &&
- 'EOF' === $this->_tokenSequence->current()['token']) {
- break;
- }
- if (false === $this->backtrack()) {
- $token = $this->_errorToken;
- if (null === $this->_errorToken) {
- $token = $this->_tokenSequence->current();
- }
- $offset = $token['offset'];
- $line = 1;
- $column = 1;
- if (!emptyempty($text)) {
- if (0 === $offset) {
- $leftnl = 0;
- } else {
- $leftnl = strrpos($text, "/n", -(strlen($text) - $offset) - 1) ?: 0;
- }
- $rightnl = strpos($text, "/n", $offset);
- $line = substr_count($text, "/n", 0, $leftnl + 1) + 1;
- $column = $offset - $leftnl + (0 === $leftnl);
- if (false !== $rightnl) {
- $text = trim(substr($text, $leftnl, $rightnl - $leftnl), "/n");
- }
- }
- throw new Compiler/Exception/UnexpectedToken(
- 'Unexpected token "%s" (%s) at line %d and column %d:' .
- "/n" . '%s' . "/n" . str_repeat(' ', $column - 1) . '↑',
- 0,
- [
- $token['value'],
- $token['token'],
- $line,
- $column,
- $text
- ],
- $line,
- $column
- );
- }
- } while (true);
- if (false === $tree) {
- return true;
- }
- $tree = $this->_buildTree();
- if (!($tree instanceof TreeNode)) {
- throw new Compiler/Exception(
- 'Parsing error: cannot build AST, the trace is corrupted.',
- 1
- );
- }
- //Vevb.com
- return $this->_tree = $tree;
- }
我們得到的一個(gè)完整的語法樹是這樣的:
- Rule {#120 ▼
- #_root: Operator {#414 ▼
- #_name: "and"
- #_arguments: array:2 [▼
- 0 => Operator {#398 ▼
- #_name: "="
- #_arguments: array:2 [▼
- 0 => Context {#396 ▼
- #_id: "gender"
- #_dimensions: []
- }
- 1 => Parameter {#397 ▼
- -name: "gender"
- }
- ]
- #_function: false
- #_laziness: false
- #_id: null
- #_dimensions: []
- }
- 1 => Operator {#413 ▼
- #_name: "and"
- #_arguments: array:2 [▼
- 0 => Operator {#401 ▼
- #_name: ">"
- #_arguments: array:2 [▼
- 0 => Context {#399 ?}
- 1 => Parameter {#400 ?}
- ]
- #_function: false
- #_laziness: false
- #_id: null
- #_dimensions: []
- }
- 1 => Operator {#412 ?}
- ]
- #_function: false
- #_laziness: true
- #_id: null
- #_dimensions: []
- }
- ]
- #_function: false
- #_laziness: true
- #_id: null
- #_dimensions: []
- }
- }
這里有根節(jié)點(diǎn)、子節(jié)點(diǎn)、操作符參數(shù)以及HoaRulerModelOperator實(shí)例。
這時(shí)$executorModel = $compilationTarget->compile($ast, $context);就可以通過NativeVisitor的visit方法對這個(gè)語法樹進(jìn)行訪問和分析了。
這一步走的是visitOperator()
- /**
- * {@inheritdoc}
- */
- public function visitOperator(AST/Operator $element, &$handle = null, $eldnah = null)
- {
- $operatorName = $element->getName();
- // the operator does not exist at all, throw an error before doing anything else.
- if (!$this->operators->hasInlineOperator($operatorName) && !$this->operators->hasOperator($operatorName)) {
- throw new OperatorNotFoundException($operatorName, sprintf('Operator "%s" does not exist.', $operatorName));
- }
- // expand the arguments
- $arguments = array_map(function ($argument) use (&$handle, $eldnah) {
- return $argument->accept($this, $handle, $eldnah);
- }, $element->getArguments());
- // and either inline the operator call
- if ($this->operators->hasInlineOperator($operatorName)) {
- $callable = $this->operators->getInlineOperator($operatorName);
- return call_user_func_array($callable, $arguments);
- }
- $inlinedArguments = emptyempty($arguments) ? '' : ', '.implode(', ', $arguments);
- // or defer it.
- return sprintf('call_user_func($operators["%s"]%s)', $operatorName, $inlinedArguments);
- }
返回的邏輯代碼可以通過得到:
$executorModel->getCompiledRule()
新聞熱點(diǎn)
疑難解答