Рубрики
Uncategorized

Интерпретация использования RulerZ и Принцип реализации механизма правил

Автор оригинала: David Wong.

Не так уж много глупостей. Официальным адресом правителей является https://github.com/K-Phoen/ru…

Обратите внимание, что в данном случае в качестве примеров для анализа используются только обычные массивы.

1. введение

Rulez-это пакет зависимостей composer, реализованный на PHP для реализации механизма правил фильтрации данных. RulerZ не только поддерживает фильтрацию массивов, но также поддерживает ORM, такие как поисковая система Eloquent, Doctrine и Solr. Это пакет с открытым исходным кодом, в котором отсутствуют официальные китайские документы. Конечно, из-за относительно небольшого количества звезд автор также может чувствовать себя ненужным.

2. установка

Запустите в каталоге, где находится ваш композитор проекта. 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 ([/// Note, here is the addition of the target compiler, which corresponds to Native when processing data sources of array type
            '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 is a filtered array
array:1 [▼
  0 => array:4 [▼
    "pseudo" => "Alice"
    "fullname" => "Alice, from... you know."
    "gender" => "F"
    "points" => 9001
  ]
]

4. Использовать-определяет, соблюдаются ли правила

$rulerz->satisfies($player, $rule, $parameters);
// Returns a Boolean value, true for satisfaction

5. Интерпретация Базового Кода

Далее давайте посмотрим, что происходит от создания компилятора до конечного результата. 1. Компилятор::создать();

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. Инициализируйте механизм правил. новый правитель()

    public function __construct(Compiler $compiler, array $compilationTargets = [])
    {
        $this->compiler = $compiler;

        foreach ($compilationTargets as $targetCompiler) {
            $this->registerCompilationTarget($targetCompiler);
        }
    }

Первый параметр здесь-это просто класс компилятора, а второй-целевой класс компилятора (фактически обрабатывающий источник данных), потому что мы выбрали массив, поэтому целевой компилятор здесь Родной Движок поместит целевой скомпилированный класс в свои собственные свойства $цели компиляции

    public function registerCompilationTarget(CompilationTarget $compilationTarget): void
    {
        $this->compilationTargets[] = $compilationTarget;
    }

3. Использование фильтра или удовлетворяет метод

Это и есть ядро. Возьмем в качестве примера фильтр:

    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();
    }

Третий шаг заключается в выполнении метода 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 <<getCompiledRule()};
    }
}
EXECUTOR;
    }

Этот код генерирует хэш-строку и сращивание Исполнителя в соответствии с алгоритмом crc 13 в качестве имени временного класса исполнителя и записывает связанный с исполнителем код во временный каталог, упомянутый выше. Сгенерированный код выглядит следующим образом:

// /private/var/folders/w_/sh4r42wn4_b650l3pc__fh7h0000gp/T/rulerz_executor_ff2800e8
 :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"]));
    }
}

Этот файл временного класса является последним классом, выполняющим действие фильтрации. Сначала выполняется метод фильтра в свойстве фильтра, который основан на логическом значении, возвращаемом execute, чтобы определить, следует ли возвращать квалифицированные строки через итераторы. Метод execute возвращает значение 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;
                }
            }
        });
    }

Удовлетворяет и фильтры имеют схожую базовую логику, но, наконец, удовлетворяет, выполняет одно суждение.

Одна из проблем заключается в том, как наш компилятор знает установленные нами правила работы. $правило Что означает синтаксический анализ? Это связано с другой проблемой-абстрактным синтаксическим деревом (AST).

Идем дальше – абстрактное синтаксическое дерево

Мы все знаем, что движок PHP Zend в процессе интерпретации кода представляет собой процесс грамматического и лексического анализа, этот процесс называется синтаксическим анализатором, в середине кода в абстрактное синтаксическое дерево, что является ключевым шагом для движка для понимания кода.

Аналогично, когда мы пишем строку правила, как код может понять, что мы пишем? Это абстрактное грамматическое дерево.

Возьмем в качестве примера приведенное выше правило:

пол =:пол и баллы >:min_points

Здесь=, и, > – все операторы, но машина не знает, что они являются операторами или что означают другие поля.

Поэтому rulez использует свой собственный шаблон грамматики.

Во-первых, по умолчанию определено несколько операторов.

 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);
    }
}

В синтаксическом анализаторе линейки существуют следующие методы:

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));
}

Здесь мы будем интерпретировать основной файл грамматики, Grammar.pp, грамматический скрипт на языке Паскаль:

//
// 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 <[email protected]>
// @author     Ivan Enderlin <[email protected]>
// @author     Kévin Gomez <[email protected]>
// @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        ("|')(.*?)(? logical_operation() #operation )?

operand:
    ::parenthesis_:: logical_operation() ::_parenthesis::
  | value()

parameter:
    
  | 

value:
    ::not:: logical_operation() #not
  |  |  |  |  |  | 
  | parameter()
  | variable()
  | array_declaration()
  | function_call()

variable:
     ( object_access() #variable_access )*

object_access:
    ::dot::  #attribute_access

#array_declaration:
    ::bracket_:: value() ( ::comma:: value() )* ::_bracket::

#function_call:
     ::parenthesis_::
    ( logical_operation() ( ::comma:: logical_operation() )* )?
    ::_parenthesis::

Приведенная выше ссылка:: метод load загружает базовое содержимое грамматики и анализирует маркеры фрагментов. Логика синтаксического анализа токенов заключается в регулярном сопоставлении некоторых операторов и базовых идентификаторов, которые нам нужны, и извлечении соответствующих регулярных выражений:

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" => "("|')(.*?)(? "-?\d+\.\d+"
    "integer" => "-?\d+"
    "parenthesis_" => "\("
    "_parenthesis" => "\)"
    "bracket_" => "\["
    "_bracket" => "\]"
    "comma" => ","
    "dot" => "\."
    "positional_parameter" => "\?"
    "named_parameter" => ":[a-z-A-Z0-9_]+"
    "identifier" => "[^\s\(\)\[\],\.]+"
  ]
]

Этот шаг также создает необработанные правила

array:10 [▼
  "#expression" => " logical_operation()"
  "logical_operation" => " operation() ( ( ::and:: #and | ::or:: #or | ::xor:: #xor ) logical_operation() )?"
  "operation" => " operand() (  logical_operation() #operation )?"
  "operand" => " ::parenthesis_:: logical_operation() ::_parenthesis:: | value()"
  "parameter" => "  | "
  "value" => " ::not:: logical_operation() #not |  |  |  |  |  |  | parameter() | variable() | array_declaration() | function_call( ▶"
  "variable" => "  ( object_access() #variable_access )*"
  "object_access" => " ::dot::  #attribute_access"
  "#array_declaration" => " ::bracket_:: value() ( ::comma:: value() )* ::_bracket::"
  "#function_call" => "  ::parenthesis_:: ( logical_operation() ( ::comma:: logical_operation() )* )? ::_parenthesis::"
]

rawRules анализирует и заменяет пустые места в методе analyzeRules класса analyzer: лексический анализатор компилятора Llk Lexer () анализирует каждый элемент массива rawRules в двусторонний стек списков (SplStack) на основе значения атрибута $_ppLexemes, а затем вставляет и удаляет стек, чтобы сформировать массив всех операторов и экземпляров токенов. Исп..

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 ▶}
]

Затем вернитесь к экземпляру LlkParser компилятора Hoa, который имеет метод синтаксического анализа, формирующий грамматическое дерево.

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 (!empty($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
            );
        }

        return $this->_tree = $tree;
    }

Мы получаем полное грамматическое дерево, подобное этому:

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: []
  }
}

Существуют корневые узлы, дочерние узлы, параметры оператора и экземпляры оператора модели линейки Тсж.

На этом этапе $executorModel = $цель компиляции – > компиляция ($ast, $контекст); вы можете получить доступ к дереву грамматики и проанализировать его с помощью метода посещения Native Visitor.

Этот шаг-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 = empty($arguments) ? '' : ', '.implode(', ', $arguments);

        // or defer it.
        return sprintf('call_user_func($operators["%s"]%s)', $operatorName, $inlinedArguments);
    }

Затем скомпилированные правила можно получить следующими способами:

$executorModel->getCompiledRule()
// The rule is $this - > unwrapArgument ($target ["gender"]) === parameters ["gender"] & ($this - > unwrapArgument ($target ["points"]) > parameters ["min_points"] & $this - > unwrapArgument ($target ["points"]) > parameters ["min_points"]).

Настройка оператора

Поскольку официальный документ слишком старый и не более, если вы настроите его в соответствии с его документом, у вас закружится голова. Вот соответствующий пример.

$compiler = Compiler::create();
$rulerz = new RulerZ($compiler, [
    new Native([
        'length' => 'strlen'
    ],[
        'contains' => function ($a, $b) {
            return sprintf('strstr(%s, %s)', $a, $b);
        }
    ])
]);

Выше содержит Представляет системную функцию. strstr() Чтобы определить, содержит ли $a символ $b, поскольку скомпилированный код генерируется строками, вы должны использовать строки для выражения логики суждения в этой анонимной функции, что также является одним из ее недостатков.

Оригинал: “https://developpaper.com/interpretation-of-rulerz-usage-and-implementation-principle-of-rule-engine/”