本文目录导读:

在PHP项目中实现决策引擎(Decision Engine),通常是指构建一套能够根据预定义的业务规则、条件逻辑、数据输入等,自动做出判断、计算或推荐的系统。
典型的业务场景包括:风控评分、优惠券发放规则、审批流程路由、动态定价、推荐拦截等。
实现决策引擎有几种主流思路,从简单到复杂:
方案对比
| 方案 | 适用场景 | 优缺点 |
|---|---|---|
| 硬编码/简单if-else | 规则极少、几乎不变的项目 | 简单直接、维护噩梦、无法动态修改 |
| 规则引擎库 (e.g., RulerZ, JsonLogic) | 中小型项目、规则较多但逻辑不复杂 | 结构化表达、可动态配置、性能尚可 |
| 表达式引擎 (e.g., Symfony ExpressionLanguage) | 需要用户或运营配置灵活公式 | 安全沙箱、支持复杂运算、学习成本较低 |
| 决策表 / 规则脚本 (可存于DB) | 规则维度多(如:年龄+收入+城市),需要可视化维护 | 灵活性最高、适合复杂业务、需要自己写解析器 |
| 集成外部规则引擎 (e.g., Drools, OpenFGA) | 超大规模、低延时、需要复杂推理的分布式场景 | 专业且强大、但引入Java中间件或PaaS,架构复杂 |
主流实现方式详解
使用轻量级PHP规则引擎库 —— RulerZ
RulerZ 是一个专门为PHP设计的规则引擎库,它允许你用声明式的方式定义和评估规则。
安装
composer require "rulerz-php/rulerz"
定义规则(支持字符串、数组、YAML) 规则字符串有点像SQL的WHERE子句,非常直观。
use RulerZ\RulerZ;
use RulerZ\Compiler\Compiler;
use RulerZ\Target\Native\Native;
// 1. 设置编译器
$compiler = new Compiler(sprintf('%s/rulerz', sys_get_temp_dir()));
$rulerz = new RulerZ($compiler, [new Native()]);
// 2. 创建一个用户数组(作为数据上下文)
$users = [
['name' => 'Alice', 'age' => 25, 'country' => 'CN', 'score' => 600],
['name' => 'Bob', 'age' => 30, 'country' => 'US', 'score' => 400],
['name' => 'Charlie', 'age' => 17, 'country' => 'CN', 'score' => 700],
];
// 3. 定义一条规则
$rule = 'age >= 18 and country = "CN" and score > 500';
// 4. 执行过滤(相当于决策引擎的「筛选」模块)
$qualifiedUsers = $rulerz->filter($users, $rule);
print_r($qualifiedUsers);
// 输出: [['name' => 'Alice', 'age' => 25, 'country' => 'CN', 'score' => 600]]
优势:支持动态修改规则字符串(可存入数据库或Redis),无需重新部署代码。
利用表达式引擎 —— Symfony ExpressionLanguage
如果你需要让非技术人员(如运营)配置复杂的数学公式或逻辑,Symfony ExpressionLanguage 是最安全的选择,它不会执行任意PHP函数,只提供安全沙箱。
安装
composer require symfony/expression-language
实现一个动态定价决策
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
$el = new ExpressionLanguage();
// 场景:计算订单折扣
$context = [
'order_total' => 1000,
'user_level' => 'gold', // gold, silver, normal
'is_birthday' => true,
'coupon_value' => 50,
];
// 规则存储在数据库里,由运营配置:如果订单满500且是金牌会员且生日,打8折,否则用优惠券
$rule_expressions = [
'level_discount' => 'order_total > 500 and user_level == "gold" and is_birthday == true ? 0.8 : 1.0',
'use_coupon' => 'coupon_value > 0',
];
// 引擎执行
$levelDiscount = $el->evaluate($rule_expressions['level_discount'], $context);
$useCoupon = $el->evaluate($rule_expressions['use_coupon'], $context);
// 决策结果
$finalPrice = $context['order_total'] * $levelDiscount;
if ($useCoupon) {
$finalPrice -= $context['coupon_value'];
}
echo "最终价格: " . max(0, $finalPrice); // 输出: 750
优势:语法接近自然语言(PHP表达式),支持严格类型检查,自带沙箱安全。
基于数据库的决策表(最灵活、但需自行开发)
这是大型企业级PHP项目的首选,将规则拆分成「条件表」和「动作表」,用JSON/PHP数组存储逻辑。
数据库设计示例
规则表 (rules) | id | rule_name | priority | status | created_at | |:---|:---|:---|:---|:---| | 1 | 风控规则-匹配欺诈用户 | 10 | active | 2025-01-01 | | 2 | 风控规则-大额交易拦截 | 20 | active | 2025-01-02 |
规则条件表 (rule_conditions) | id | rule_id | field | operator | value | logic_group | |:---|:---|:---|:---|:---|:---| | 1 | 1 | risk_score | >= | 80 | A | | 2 | 1 | country | IN | ['NG', 'RU'] | A | | 3 | 2 | amount | > | 50000 | B | | 4 | 2 | is_verified | = | false | B |
规则动作表 (rule_actions) | id | rule_id | action_type | action_value | |:---|:---|:---|:---| | 1 | 1 | block | true | | 2 | 1 | risk_level | high | | 3 | 2 | need_approval | true |
PHP决策引擎核心类
class DecisionEngine
{
private array $rules;
public function __construct(array $allRulesWithConditionsAndActions) {
$this->rules = $allRulesWithConditionsAndActions;
}
/**
* 根据上下文执行决策,返回所有命中规则的动作列表
*/
public function evaluate(array $context): array
{
$actions = [];
// 按规则优先级排序
usort($this->rules, fn($a, $b) => $a['priority'] <=> $b['priority']);
foreach ($this->rules as $rule) {
$allConditionsMet = false;
$groupResults = [];
// 按 logic_group 分组条件 (同一组内是 AND,组之间是 OR)
$groups = $this->groupConditions($rule['conditions']);
foreach ($groups as $group) {
$groupResult = true;
foreach ($group as $condition) {
// 从上下文中取值
$fieldValue = $context[$condition['field']] ?? null;
$operator = $condition['operator'];
$expectedValue = $condition['value'];
// 调用比较函数
if (!$this->compare($fieldValue, $operator, $expectedValue)) {
$groupResult = false;
break; // 组内AND,一个不满足即跳出
}
}
$groupResults[] = $groupResult;
}
// 组之间是 OR 关系
$allConditionsMet = in_array(true, $groupResults, true);
if ($allConditionsMet) {
// 收集动作,也可以只取第一个命中规则(根据业务)
foreach ($rule['actions'] as $action) {
$actions[] = [
'rule_name' => $rule['rule_name'],
'action_type' => $action['action_type'],
'action_value' => $action['action_value'],
];
}
// 高级:如果需要短路(只执行优先级最高的规则),在此break
// break;
}
}
return $actions;
}
private function compare($actual, string $operator, $expected): bool
{
return match ($operator) {
'=', '==' => $actual == $expected,
'!=' => $actual != $expected,
'>' => $actual > $expected,
'>=' => $actual >= $expected,
'<' => $actual < $expected,
'<=' => $actual <= $expected,
'IN' => in_array($actual, (array) $expected),
'NOT_IN' => !in_array($actual, (array) $expected),
'BETWEEN' => $actual >= $expected[0] && $actual <= $expected[1],
'LIKE' => str_contains($actual, $expected),
default => throw new \InvalidArgumentException("Unsupported operator: $operator"),
};
}
private function groupConditions(array $conditions): array
{
$groups = [];
foreach ($conditions as $cond) {
$groupId = $cond['logic_group'] ?? 'default';
$groups[$groupId][] = $cond;
}
return array_values($groups);
}
}
// 使用示例(伪代码)
$context = [
'risk_score' => 85,
'country' => 'NG',
'amount' => 60000,
'is_verified' => false,
];
$engine = new DecisionEngine($rulesFromDB);
$result = $engine->evaluate($context);
// $result: [['rule_name'=>'风控规则-匹配欺诈用户', 'block'=>true, 'risk_level'=>'high']]
优势:完全自定义,可以做出复杂的多组条件(AND/OR嵌套),规则可动态管理,效率高(可加缓存)。
性能优化建议
- 规则缓存:不要每次请求都查数据库,可以使用
Redis或 本地APCu缓存编译后的规则树/数组。 - 编译/预计算:对于像 RulerZ 或 Symfony ExpressionLanguage,在规则不变的前提下,将解析后的AST(抽象语法树)缓存起来,避免重复解析。
- 短路求值:在优先级规则命中后,根据业务逻辑决定是否跳过后续规则,风控“直接拒绝”规则一旦命中,停止执行后续低级别规则。
- 减少循环:如果数据量极大(如百万用户批量评估),考虑将规则下推到数据库层(用SQL/存储过程)或使用PHP的并行扩展(如p线程、Swoole协程)。
| 你的业务需求 | 推荐方案 |
|---|---|
| 规则简单(<10条),几乎不变 | 硬编码,不需要折腾 |
| 需要运营频繁修改规则 | RulerZ 或 Symfony ExpressionLanguage |
| 规则有复杂层级(多组AND/OR嵌套),需要持久化到数据库 | 自行实现的 数据库决策表 |
| 团队有其他Java/Go服务,需要统一规则 | 集成外置规则引擎,PHP通过API/RPC调用 |
| 规则需要可视化配置(让非技术人员拖拽) | 数据库决策表 + 自定义管理后台 或 使用现成引擎(如Drools) |
对于绝大多数PHP项目,方案三(数据库决策表) 是最适合落地且维护负担较轻的选择。