rezzza/ruler-bundle
Symfony bundle integrating the Ruler rule engine, letting you define and evaluate business rules and contexts via configuration. Useful for stateless, expression-based decision logic in PHP applications.
Architecture fit. The underlying model fits well in the abstract: rule evaluation is a pure function of a context array returning a boolean, with no persistence, no session state, and fully deterministic output. That shape is a good fit for embedded policy decisions — feature gating, entitlement checks, eligibility gates, promotion logic — where a TPM wants a single auditable decision layer instead of scattered if branches. What does not fit is the bundle layer itself: it is Symfony 2 dependency-injection wiring (service definitions, a config tree, possibly a profiler collector), all of which a Laravel product would discard. Net reusable assets are the evaluation model and the idea of declarative, config-declared rule sets. Everything else is dead weight.
Integration feasibility — the framing problem to surface first. This is not a Laravel package. There is no service provider, no facade, no publishable config, no Laravel version constraint. It targets Symfony 2 and is not an installable dependency for a modern Laravel application. Treating it as "add to composer.json and go" is the single most likely way to burn a sprint on this evaluation. The realistic path is to consume the underlying Ruler library directly, or — better — reimplement the ~150–300-line evaluation core as a framework-agnostic domain primitive and bind it Laravel-natively with one provider and one config file.
Technical risk — high, and mostly non-technical.
Key questions before any commitment.
php, symfony/*, the wrapped engine) — run composer why-not against the target stack.services.yml wiring — compiler pass, config tree, data collector, console command? This determines whether any code is worth reading.Stack fit: none directly. Do not install rezzza/ruler-bundle into a Laravel application. There is no supported path, and forking an EOL Symfony bundle to shim it into Laravel's container is strictly worse than a short port.
Recommended path — port the pattern, not the package.
RuleSet, Rule, Operator, evaluate(array $context): Decision). Target ~150–300 lines, PHP 8 typed, no framework imports, no container awareness. This is the highest-leverage move and costs days, not weeks.If an off-the-shelf engine is preferred, take the underlying Ruler library as a standalone dependency and skip the bundle — but verify PHP 8 compatibility first, since the library is as old as the bundle.
Migration path. There is nothing to migrate from: no runtime, no data, no state. The work is greenfield authorship plus, where scattered if/else policy code already exists, a strangler refactor of those branches into named rules wrapped in golden-case tests.
Compatibility checklist. PHP version support, PSR-4 autoloading, operator type-coercion behavior under PHP 8 (string/numeric comparisons shifted subtly across 7→8), and determinism of the context contract. Any failure here is a fork-or-port decision, and port wins on cost almost every time at this size.
Sequencing.
Positioning against alternatives. Laravel has no first-class rules engine; Pennant is feature flags, not rules. That gap is real and is why the pattern is attractive — but it is filled by a ~300-line first-party primitive, not by resurrecting a 2015 Symfony bundle.
Maintenance. Upstream maintenance is effectively zero. Every line depended on becomes ours to patch, port, and secure. The honest TCO comparison is not "bundle vs. build" — it is "abandoned code we now own vs. code we wrote and already own." The latter is cheaper and better documented by construction.
Support model. If this touches a real decisioning path (pricing, eligibility, entitlements), it needs a named internal owner and an escalation story for "why did this account get this outcome." An unmaintained third-party bundle cannot sit on that path. A ported primitive keeps the burden small, but the ownership must be explicitly assigned.
Scaling. Statelessness is a genuine strength: no shared cache, no session affinity, horizontally trivial, safe inside HTTP requests and queue workers alike. The cost is that rules are constructed and evaluated per call, with no compiled-rule cache and no memoization — CPU grows linearly with rule count times context size. Fine at tens to low hundreds of evaluations per request; a profiling target in the thousands. Short-circuit ordering and rule-set partitioning are the levers.
Failure modes to design against.
false that looks like a legitimate "no."Ramp-up. The evaluation model is small and readable; a mid-level engineer is productive in a day, and the resulting primitive is easy to test and reason about. The dead-end skill is Symfony 2 bundle wiring, which should deliberately not be taught to anyone. Documentation cost is ours either way: budget a short rule-authoring guide plus a golden-case regression suite, because rules are business logic and will drift silently without both.
Change management. Rule sets belong in version control with semantic versioning and a review gate. Roll out new rule sets in shadow mode against production inputs, compare decisions to the incumbent, and only then promote — the stateless design makes this cheap, and it is the cheapest insurance available against a silent policy regression.
How can I help you explore Laravel packages today?