承接 ols/php-ruler 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

ols/php-ruler

Composer 安装命令:

composer require ols/php-ruler

包简介

A transparent expression & rule evaluator in pure PHP. No dependencies. Strict typing, a safe mode that collects missing variables instead of failing, and an explain mode that shows exactly why a rule passed or failed. Move business logic out of your code.

README 文档

README

CI Packagist Version PHP Version License Downloads

Evaluate business rules stored as strings — with strict typing and a node-by-node trace of why each rule passed or failed.

Strict typing · Safe mode · Full evaluation trace · Variable aliasing · Zero dependencies · PHP 8.1+

Who is this for?

php-ruler exists to move business logic out of your code and into expressions that can be stored, edited, and evaluated at runtime — pricing rules, eligibility checks, feature flags, validation conditions, content filters — without redeploying. It is also a practical way to let non-developers author rules, because every evaluation can be explained step by step.

If you need to traverse objects, call methods, or you are already invested in the Symfony stack, Symfony's ExpressionLanguage is the mature, battle-tested choice. php-ruler deliberately does less: it never touches objects, methods, or PHP internals. That is precisely what lets it stay strict, dependency-free, and safe to expose to semi-trusted rule authors — and what lets it tell you why a rule evaluated the way it did.

It is a good fit if:

  • You want to store and evaluate rules without redeploying code
  • You want strict, predictable semantics (no silent type juggling)
  • You let users or non-developers write rules, and need to explain the outcome
  • You want zero dependencies and nothing to install beyond the library itself

It is not a good fit if:

  • You need to read object properties or call methods inside expressions
  • You need the full operator surface of Twig/Symfony ExpressionLanguage
  • You want to run arbitrary, fully untrusted input without any review (see Security)

Features

  • Strict evaluation — no type coercion. 1 = '1' is a type error, not true. NaN/INF can never silently slip through an operator. (docs/evaluate.md)
  • Safe mode — evaluate against a partial context: instead of throwing on the first missing variable, it collects every missing path so you can report them all at once. (docs/evaluate-safe.md)
  • Explain mode — turn any expression into a tree showing, leaf by leaf, what was evaluated, what passed or failed, what was short-circuited, and why something could not be evaluated (the missing variable, the type error). (docs/explainer.md)
  • Variable aliasing — translate between a human-facing form ("cart amount > 100") and the technical paths your context actually uses (cart.total > 100), in both directions. (docs/alias-resolver.md)
  • AST caching & export — parsed expressions are cached (LRU); ASTs can be exported to JSON and re-imported, so you can pre-compile once and store the result. (docs/ast-management.md)
  • Custom functions — register your own callables alongside the built-ins. (docs/functions.md)
  • A clear error model — typed exceptions with structured details (the offending variable path, the syntax error position). (docs/exceptions.md)
  • No extensions required — runs on any standard PHP 8.1+ installation.

Requirements

  • PHP 8.1 or higher
  • No PHP extensions, no external services, no dependencies

Installation

Via Composer

composer require ols/php-ruler

Manual install — if you are not using Composer, copy the src/ directory into your project and include its autoloader:

require '/path/to/php-ruler/src/autoload.php';

Quick start

use Ols\PhpRuler\ExpressionEvaluator;

$eval = new ExpressionEvaluator();

$context = [
    'cart'     => ['total' => 150.00],
    'customer' => ['group' => 'vip', 'vip' => true],
    'product'  => ['price' => 49.99],
];

$eval->evaluate("cart.total > 100 AND customer.group = 'vip'", $context); // true

Variables are read from the context by dot-notation path. Evaluation is strict: comparing incompatible types, or using a missing variable, raises a typed exception rather than guessing.

Evaluating

The strict entry points throw on the first problem (missing variable, type error, syntax error):

$eval->evaluate("round(cart.total * 0.9, 2)", $context);   // mixed  → 135.0
$eval->evaluateBoolean("cart.total >= 50", $context);      // bool   → true
$eval->evaluateNumeric("product.price * 2", $context);     // float  → 99.98

→ Detailed documentation: docs/evaluate.md

Safe mode

When the context may be incomplete, evaluateSafe() does not throw on missing variables — it reports them so you can decide what to do:

$result = $eval->evaluateSafe("cart.total > customer.creditLimit", $context);

$result->success;       // false
$result->missingVars;   // ['customer.creditLimit']
$result->getValueOr(false);

→ Detailed documentation: docs/evaluate-safe.md

Explain mode

explain() returns the full evaluation tree — the part that makes a rule auditable instead of a black box:

use Ols\PhpRuler\Explainer\ExpressionExplainer;

$explainer = new ExpressionExplainer($eval);
$result    = $explainer->explain(
    "customer.vip = true AND cart.total >= 50 AND product.price < 10 OR customer.group = 'vip'",
    $context
);

$result->passed;        // true | false | null (null = could not be fully evaluated)
$result->failures();    // evaluated leaves that returned false
$result->missing();     // leaves that needed an absent variable
$result->root;          // the full tree (expression, status, passed, resolved values, children)

Each node carries its reconstructed sub-expression, its status (evaluated / short-circuited / missing / error), the resolved left/right values, and — for missing or errored nodes — a detail string (the variable path or the error message).

→ Detailed documentation: docs/explainer.md

Variable aliasing

AliasResolver is a standalone, two-way text translator between a human-facing form and your technical paths. It does not parse or evaluate — it only substitutes — so it composes cleanly with the evaluator:

use Ols\PhpRuler\AliasResolver;

$resolver = (new AliasResolver())
    ->add('customer.group', 'customer group')
    ->add('cart.total',     'cart amount');

// Human input → technical expression → evaluate
$expr = $resolver->humanToExpression("customer group = 'vip' AND cart amount > 100");
// "customer.group = 'vip' AND cart.total > 100"
$eval->evaluate($expr, $context);

// Stored technical expression → human form for display
$resolver->expressionToHuman("cart.total > 100"); // "cart amount > 100"

→ Detailed documentation: docs/alias-resolver.md

What you can express

From a simple condition to a multi-criteria rule combining functions, dates, and fallback logic:

// Basic comparison
"cart.total > 100"
"user.role = 'admin'"

// Membership
"product.category IN ['clothing', 'shoes', 'boots']"
"article.status NOT IN ['draft', 'archived']"

// Combining conditions
"cart.total > 100 AND customer.group = 'vip'"
"article.author = user.id OR user.role = 'editor'"

// Null-coalescing and ternary
"customer.discount ?? 0"
"(customer.score ?? 0) >= 50"
"user.plan = 'pro' ? 'full_access' : 'limited'"

// Functions — math, strings, dates
"round(cart.total * 1.2, 2) < 500"
"dateDiff(today(), article.published_at) <= 30"
"upper(customer.country) IN ['FR', 'BE', 'CH']"

// Multi-criteria, realistic rules
"customer.group = 'vip' AND cart.total >= 100 AND cart.country IN ['FR', 'BE', 'CH']"
"article.status = 'published' AND dateDiff(today(), article.published_at) <= 90 AND article.author = user.id"
"(customer.score ?? 0) >= 50 AND NOT customer.country IN ['IR', 'KP'] AND dateAdd(user.created_at, 30, 'day') < today()"

// Not just boolean rules — expressions can return values
"customer.vip ? round(cart.total * 0.85, 2) : cart.total"
// → float: the discounted price, or the original

"clamp((customer.score ?? 0) / 10, 0, 100)"
// → a numeric score between 0 and 100

"dateDiff(today(), subscription.expires_at) <= 0 ? 'expired' : concat('expires in ', str(dateDiff(today(), subscription.expires_at)), ' days')"
// → string: 'expired' or 'expires in 14 days'

// The same rules, with human-readable aliases registered via AliasResolver:
// "vip customer AND cart amount >= 100 AND shipping country IN ['FR', 'BE', 'CH']"

// You can also register custom functions and call them directly in expressions:
// "is_eligible(customer.score, plan.threshold) AND quota.remaining > 0"

All types, operators, functions, and precedence rules are in docs/language-reference.md and docs/functions.md.

Security

php-ruler evaluates only the context you pass in. It cannot read object properties, call methods, reach PHP constants, or touch the filesystem — there is no escape hatch into the host. That makes it well suited to evaluating rules authored in a back office or by semi-trusted users.

It is not, however, a hardened sandbox for arbitrary hostile input out of the box: a registered regex-style custom function could expose ReDoS, and pathologically deep expressions are bounded by a depth guard but still cost CPU. Review or constrain rules that come from fully untrusted sources.

Documentation

Topic Document
Language reference (grammar, operators, precedence, typing) docs/language-reference.md
Built-in functions docs/functions.md
Evaluating (strict) docs/evaluate.md
Safe mode docs/evaluate-safe.md
Explain mode docs/explainer.md
Variable aliasing docs/alias-resolver.md
Context resolution docs/context.md
AST caching & export docs/ast-management.md
Static analysis (extract variables / functions) docs/static-analysis.md
Exceptions & error model docs/exceptions.md

Demo

A small local playground to write expressions and see the evaluation trace — which sub-conditions passed, which failed, which were short-circuited, and, when a variable is missing or a value has the wrong type, why the rule could not be evaluated.

No build step, no Composer required:

php -S localhost:8000 -t demo

Then open http://localhost:8000/demo.html.

php-ruler demo — an expression evaluated, with its full evaluation trace

See demo/README.md for details.

License

MIT — see LICENSE.

ols/php-ruler 适用场景与选型建议

ols/php-ruler 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1 次下载、GitHub Stars 达 2, 最近一次更新时间为 2026 年 06 月 06 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「rules」 「expression」 「strict」 「expression language」 「predicate」 「evaluator」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 ols/php-ruler 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 ols/php-ruler 我们能提供哪些服务?
定制开发 / 二次开发

基于 ols/php-ruler 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 1
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 32
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-06