本文目录导读:

在 PHP 中实现 DSL(领域特定语言)规则,主要有以下几种方式:
方法链式调用(Fluent Interface)
这是最常用的方式,通过方法链构建规则。
<?php
class RuleBuilder {
private $rules = [];
private $currentField = '';
private $currentOperator = '';
private $currentValue = null;
public function field($field) {
$this->currentField = $field;
return $this;
}
public function equals($value) {
$this->currentOperator = '=';
$this->currentValue = $value;
return $this->addRule();
}
public function greaterThan($value) {
$this->currentOperator = '>';
$this->currentValue = $value;
return $this->addRule();
}
public function lessThan($value) {
$this->currentOperator = '<';
$this->currentValue = $value;
return $this->addRule();
}
public function addRule() {
$this->rules[] = [
'field' => $this->currentField,
'operator' => $this->currentOperator,
'value' => $this->currentValue
];
return $this;
}
public function getRules() {
return $this->rules;
}
}
// 使用示例
$builder = new RuleBuilder();
$rules = $builder
->field('age')->greaterThan(18)
->field('status')->equals('active')
->field('score')->lessThan(100)
->getRules();
print_r($rules);
使用匿名函数或闭包
<?php
class RuleEngine {
private $rules = [];
public function when(callable $condition, callable $action) {
$this->rules[] = [
'condition' => $condition,
'action' => $action
];
return $this;
}
public function evaluate($data) {
foreach ($this->rules as $rule) {
if ($rule['condition']($data)) {
return $rule['action']($data);
}
}
return null;
}
}
// 使用示例
$engine = new RuleEngine();
$result = $engine
->when(
fn($user) => $user['age'] >= 18 && $user['status'] === 'active',
fn($user) => "已成年且活跃用户"
)
->when(
fn($user) => $user['age'] < 18,
fn($user) => "未成年用户"
)
->evaluate(['age' => 25, 'status' => 'active']);
echo $result;
使用数组配置 DSL
<?php
class ArrayRuleDSL {
private $rules = [];
public function addRule(array $rule) {
$this->rules[] = $rule;
return $this;
}
public function evaluate($data) {
foreach ($this->rules as $rule) {
if ($this->matches($rule['condition'], $data)) {
return $rule['result'];
}
}
return null;
}
private function matches($condition, $data) {
foreach ($condition as $field => $constraint) {
if (is_array($constraint)) {
foreach ($constraint as $op => $value) {
if (!$this->applyOperator($op, $data[$field] ?? null, $value)) {
return false;
}
}
} elseif ($data[$field] !== $constraint) {
return false;
}
}
return true;
}
private function applyOperator($operator, $actual, $expected) {
switch ($operator) {
case 'gt': return $actual > $expected;
case 'gte': return $actual >= $expected;
case 'lt': return $actual < $expected;
case 'lte': return $actual <= $expected;
case 'eq': return $actual === $expected;
case 'neq': return $actual !== $expected;
case 'in': return in_array($actual, $expected);
case 'between': return $actual >= $expected[0] && $actual <= $expected[1];
default: return false;
}
}
}
// 使用示例
$ruleDSL = new ArrayRuleDSL();
$ruleDSL
->addRule([
'condition' => [
'age' => ['gte' => 18, 'lte' => 65],
'status' => 'active'
],
'result' => '符合资质的用户'
])
->addRule([
'condition' => [
'age' => ['lt' => 18]
],
'result' => '未成年用户'
]);
echo $ruleDSL->evaluate(['age' => 25, 'status' => 'active']);
使用解析器模式(复杂 DSL)
<?php
class DSLParser {
private $data = [];
public function parse($expression) {
// 解析类似:"IF age > 18 AND status = 'active' THEN '符合条件'"
$expression = trim($expression);
if (preg_match('/IF (.+?) THEN (.+)/i', $expression, $matches)) {
$condition = $this->parseCondition($matches[1]);
$result = $matches[2];
if ($this->evaluateCondition($condition)) {
return $result;
}
}
return null;
}
private function parseCondition($condition) {
// 支持 AND/OR
$parts = preg_split('/\s+(AND|OR)\s+/i', $condition, -1, PREG_SPLIT_DELIM_CAPTURE);
$groups = [];
$currentGroup = [];
foreach ($parts as $part) {
if (strtoupper($part) === 'AND' || strtoupper($part) === 'OR') {
$currentGroup['operator'] = strtoupper($part);
} else {
$currentGroup['conditions'][] = $this->parseSimpleCondition($part);
}
}
return $currentGroup;
}
private function parseSimpleCondition($condition) {
// 解析:field > value, field = 'value', field BETWEEN x AND y
if (preg_match('/^(.+?)\s*(>=|<=|==|!=|>|<|=)\s*(.+)$/', trim($condition), $matches)) {
return [
'field' => trim($matches[1]),
'operator' => $matches[2],
'value' => $this->parseValue($matches[3])
];
}
return null;
}
private function parseValue($value) {
$value = trim($value);
if (preg_match('/^\'(.+)\'$/', $value, $matches)) {
return $matches[1];
}
if (is_numeric($value)) {
return (float)$value;
}
return $value;
}
private function evaluateCondition($condition) {
if (isset($condition['operator'])) {
$result = false;
foreach ($condition['conditions'] as $subCondition) {
$subResult = $this->evaluateSimple($subCondition);
if ($condition['operator'] === 'AND') {
$result = $result && $subResult;
} else {
$result = $result || $subResult;
}
}
return $result;
}
return $this->evaluateSimple($condition['conditions'][0]);
}
private function evaluateSimple($condition) {
$fieldValue = $this->data[$condition['field']] ?? null;
switch ($condition['operator']) {
case '>': return $fieldValue > $condition['value'];
case '<': return $fieldValue < $condition['value'];
case '>=': return $fieldValue >= $condition['value'];
case '<=': return $fieldValue <= $condition['value'];
case '=': return $fieldValue == $condition['value'];
case '==': return $fieldValue === $condition['value'];
case '!=': return $fieldValue != $condition['value'];
default: return false;
}
}
public function setData($data) {
$this->data = $data;
return $this;
}
}
// 使用示例
$parser = new DSLParser();
$result = $parser
->setData(['age' => 25, 'status' => 'active'])
->parse("IF age >= 18 AND status = 'active' THEN '资格通过'");
echo $result;
使用现有库
也可以使用一些现成的 PHP DSL 库:
使用 symfony/expression-language
composer require symfony/expression-language
<?php
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
$language = new ExpressionLanguage();
$data = ['age' => 25, 'status' => 'active'];
// 评估表达式
$result = $language->evaluate(
"age >= 18 and status == 'active'",
$data
);
echo $result ? '通过' : '不通过';
使用 hanson/expression
composer require hanson/expression
<?php
use Hanson\Expression\Expression;
$expression = new Expression();
$result = $expression->evaluate("age > 18 && status = 'active'", ['age' => 20, 'status' => 'active']);
选择建议
- 简单规则:使用方法链式调用或数组配置
- 复杂业务规则:使用闭包组合
- 需要动态配置:使用表达式解析器
- 生产环境:考虑使用成熟的库(如 symfony/expression-language)
这些方式可以根据你的具体需求选择或组合使用,DSL 的核心目的是让规则更加可读、可维护,降低代码耦合度。