rogervila/php-simple-rules-engine

1.1.0 2023-09-18 15:12 UTC

This package is auto-updated.

Last update: 2024-09-18 17:07:34 UTC


README

PHP Simple Rules Engine

Build Status StyleCI Quality Gate Status Coverage

Latest Stable Version Total Downloads License

PHP 简单规则引擎

关于

根据主题评估规则。

用法

该包期望一个主题和一组规则。

每个规则都必须是一个扩展 \SimpleRulesEngine\Rule 的类。

主题参数可以是任何类型的对象 (mixed)

基本用法

规则返回一个包含由用户定义的 result 属性的 \SimpleRulesEngine\Evaluation 对象。

此外,用户可以定义 stop 属性的值以确定评估过程是否应该停止或继续。

在此示例中,由于我们只评估一个规则,因此 stop 属性的值不会影响评估过程。

use SimpleRulesEngine\Rule;
use SimpleRulesEngine\Evaluation;
use SimpleRulesEngine\RulesEngine;

class FooRule extends Rule {
    public function evaluate(mixed $subject, ?Evaluation $previousEvaluation = null): Evaluation
    {
        return new Evaluation(
            result: $subject === 'foo', // mixed. It should contain the evaluation result defined by the user.
            stop: false, // false by default. When set to true, the evaluation process is stopped.
        );
    }
}

$evaluation = RulesEngine::run(
    subject: 'foo',
    rules: [new FooRule()]
);

var_dump($evaluation->getResult()); // bool(true)
var_dump($evaluation->getRule()::class); // string(7) "FooRule"

高级用法

当评估多个规则时,您可以通过将 withHistory 参数设置为 true 来检索特定评估过程中评估的规则的历史记录。

最终的 \SimpleRulesEngine\Evaluation 对象将包含一个包含在评估过程中由规则返回的评估结果的 history 列表。

请查看 tests/RulesEngineTest.php 中的 test_evaluation_with_history 方法以获取更详细的实现。

use SimpleRulesEngine\Rule;
use SimpleRulesEngine\Evaluation;
use SimpleRulesEngine\RulesEngine;

class RuleA extends Rule {
    // ...
}

class RuleB extends Rule {
    // ...
}

class RuleC extends Rule {
    // ...
}

$rules = [new RuleA(), new RuleB(), new RuleC()];

// Let's pretend that the final evaluation comes from RuleC()
$evaluation = RulesEngine::run(
    subject: 'C',
    rules: $rules,
    withHistory: true
);

var_dump(count($evaluation->getHistory())); // int(2)
var_dump($evaluation->getHistory()[0]->getRule()::class); // string(5) "RuleA"
var_dump($evaluation->getHistory()[1]->getRule()::class); // string(5) "RuleB"

递归使用

您可以将规则和规则数组传递给它们以递归地评估它们。

注意:此库的将来版本可能包含 Openswoole 支持,以并行化评估每个规则子集。

use SimpleRulesEngine\Evaluation;
use SimpleRulesEngine\RulesEngine;


$rules = [
    new ARule(), // 0
    new BRule(), // 1
    [
        new AARule(), // 2
        [
            new AAARule(), // 3
            new AABRule(), // 4
            new AACRule(), // 5
        ]
    ],
    new CRule() // 6
];

$evaluation = RulesEngine::run(
    subject: '...',
    rules: $rules
);

示例

这些示例非常简单,仅用于演示目的,但它们展示了该包附带的基本功能。

有一个名为 durable rules 的 Python 规则引擎,它包含一些示例。我们将使用此包重新创建它们。

模式匹配

根据其号码找到信用卡类型。

请查看 tests/RulesEngineTest.php 中的 test_match_example_with_cards 方法以获取更详细的实现。

use SimpleRulesEngine\Rule;
use SimpleRulesEngine\Evaluation;
use SimpleRulesEngine\RulesEngine;

$amex = '375678956789765';
$visa = '4345634566789888';
$mastercard = '2228345634567898';
$invalid = uniqid('invalid card ');

class AmexRule extends Rule {
    public function evaluate(mixed $subject, ?Evaluation $previousEvaluation = null): Evaluation
    {
        return new Evaluation(
            result: $result = preg_match('/^3[47][0-9]{13}$/', strval($subject)) === 1 ? 'amex' : null,
            stop: $result !== null,
        );
    }
};

class VisaRule extends Rule {
    public function evaluate(mixed $subject, ?Evaluation $previousEvaluation = null): Evaluation
    {
        return new Evaluation(
            result: $result = preg_match('/^4[0-9]{12}(?:[0-9]{3})?$/', strval($subject)) === 1 ? 'visa' : null,
            stop: $result !== null,
        );
    }
};

class MastercardRule extends Rule {
    public function evaluate(mixed $subject, ?Evaluation $previousEvaluation = null): Evaluation
    {
        return new Evaluation(
            result: $result = preg_match('/(5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|2720)[0-9]{12}/', strval($subject)) === 1 ? 'mastercard' : null,
            stop: $result !== null,
        );
    }
};


$rules = [new VisaRule(), new AmexRule(), new MastercardRule()];

shuffle($rules); // Rules should apply always, the order does not matter

$evaluation = RulesEngine::run($amex, $rules);
var_dump($evaluation->getResult()); // string(4) "amex"
var_dump($evaluation->getRule()::class); // string(8) "AmexRule"

$evaluation = RulesEngine::run($visa, $rules);
var_dump($evaluation->getResult()); // string(4) "visa"
var_dump($evaluation->getRule()::class); // string(8) "VisaRule"

$evaluation = RulesEngine::run($mastercard, $rules);
var_dump($evaluation->getResult()); // string(10) "mastercard"
var_dump($evaluation->getRule()::class); // string(14) "MastercardRule"

$evaluation = RulesEngine::run($invalid, $rules);
var_dump($evaluation->getResult()); // NULL
var_dump($evaluation->getRule()::class); // Since we are using shuffle here, the rule applied can be any of the rules previously passed

事实集

根据事实找到动物。

在这种情况下,我们将比较当前规则结果与先前评估结果。如果它们匹配,我们将停止评估过程。

请查看 tests/RulesEngineTest.php 中的 test_facts_example 方法以获取更详细的实现。

use SimpleRulesEngine\Rule;
use SimpleRulesEngine\Evaluation;
use SimpleRulesEngine\RulesEngine;

class Animal {
    public string $eats = '';
    public string $lives = '';
    public string $color = '';
};

$frog = new Animal();
$frog->eats = 'flies';
$frog->lives = 'water';
$frog->color = 'green';

$bird = new Animal();
$bird->eats = 'worms';
$bird->lives = 'nest';
$bird->color = 'black';

class EatsRule extends Rule {
    public const FACTS = ['flies' => 'frog', 'worms' => 'bird'];

    public function evaluate(mixed $subject, ?Evaluation $previousEvaluation = null): Evaluation
    {
        $previousResult = $previousEvaluation?->getResult();
        $currentResult = self::FACTS[$subject?->eats];

        return new Evaluation(
            result: $currentResult,
            stop: $previousResult === $currentResult,
        );
    }
};

class LivesRule extends Rule {
    public const FACTS = ['water' => 'frog', 'nest' => 'bird'];

    public function evaluate(mixed $subject, ?Evaluation $previousEvaluation = null): Evaluation
    {
        $previousResult = $previousEvaluation?->getResult();
        $currentResult = self::FACTS[$subject?->lives];

        return new Evaluation(
            result: $currentResult,
            stop: $previousResult === $currentResult,
        );
    }
};

class ColorRule extends Rule {
    public const FACTS = ['green' => 'frog', 'black' => 'bird'];

    public function evaluate(mixed $subject, ?Evaluation $previousEvaluation = null): Evaluation
    {
        $previousResult = $previousEvaluation?->getResult();
        $currentResult = self::FACTS[$subject?->color];

        return new Evaluation(
            result: $currentResult,
            stop: $previousResult === $currentResult,
        );
    }
};

$rules = [new EatsRule(), new LivesRule(), new ColorRule()];

shuffle($rules); // Rules should apply always, the order does not matter

$evaluation = RulesEngine::run($frog, $rules);
var_dump($evaluation->getResult()); // string(4) "frog"

$evaluation = RulesEngine::run($bird, $rules);
var_dump($evaluation->getResult()); // string(4) "bird"

作者

Roger Vilà 创建

许可证

PHP Simple Rules Engine 是开源软件,许可协议为 MIT 许可证

图标由 Gregor Cresnar 制作,来自 www.flaticon.com