hoa/ruler
Hoa/ruler is a PHP rule engine for defining and evaluating business rules with a dedicated, expressive language. It supports stateless production rules, rule manipulation, proposition evaluation, and integration into PHP applications. Use it to model conditions and make declarative decisions.
Architecture fit. hoa/ruler is a framework-agnostic business rule engine: you hand it a rule string (logged(user) and user.age > 18) plus a context array/object, and it returns a boolean (Ruler::assert($rule, $context)). That is a legitimate fit for eligibility checks, pricing/discount logic, feature gating, and routing rules that business stakeholders want to change without a deploy. It is not a decision-table or workflow engine — no rule persistence, no versioning, no authoring UI, no evaluation audit trail, no explainability output. Every one of those capabilities is a layer we would build and own around the library. Treat it as a parser + evaluator, not a rules platform.
Integration feasibility. Installable via Composer, but it drags in the full Hoa dependency tree (hoa/compiler, hoa/ustring, hoa/visitor, hoa/stream, hoa/consistency, hoa/zformat, hoa/exception). Hoa bootstraps a custom hoa:// stream wrapper and constants at autoload time — a side effect we would be introducing into a Laravel request lifecycle. There is no service provider, facade, config, or container awareness; the adapter is entirely ours to write. Context hydration (mapping Eloquent models, guards, and request data into the evaluator's array shape) remains the bulk of the integration work. The latest release, 2.17.05.16 (2017-05-16), marginally reduces that work: the Asserter can now visit array-like dimensions, meaning context objects exposing ArrayAccess-style offsets (Eloquent models, ArrayObject, some value objects) can be traversed directly instead of being flattened into plain arrays first. Useful, but incremental — it does not eliminate the need for a normalisation layer, since guards, DTOs, and relations still have heterogeneous shapes.
Technical risk. High. The repository is archived, the last release is 2017-05-16, and the Hoa project itself is disbanded. The 2.17.05.16 note confirms rather than changes this: a single-commit feature release from May 2017 is the terminal state of the project, not evidence of renewed maintenance. There is no security response process, no PHP 8.x maintenance, and transitive dependencies (hoa/compiler, hoa/ustring) that are known to trip on deprecations and removals introduced in PHP 8.1+. The 619 stars / 0 listed dependents combination is a weak ecosystem-validation signal for a modern Laravel stack. Licensing (BSD-family — verify) permits a fork, but not free maintenance. Secondary risk: rules are configuration authored by humans, so parse failures surface at runtime unless we validate at write time. The array-dimension capability also introduces a new failure surface — behaviour when a dimension is absent, or when an ArrayAccess implementation throws — that is undocumented and must be pinned by tests before we rely on it.
Key questions.
ArrayAccess traversal change our context contract, and what is the defined behaviour for missing dimensions?Stack fit. Define our own RuleEngine contract (evaluate / validate / explain), bind it in a service provider, expose a thin facade, and put the Hoa calls behind it. Keep the DSL surface in config (allowed functions, allowed context keys, comparison operators) so we can swap the underlying engine without touching call sites. This interface-first framing is the single most important decision — it converts an abandoned dependency into a replaceable implementation detail. Given 2.17.05.16, define the context contract in terms of plain arrays first and treat ArrayAccess traversal as an optional optimisation the adapter may exploit, so the contract does not become coupled to a Hoa-specific behaviour.
Migration path. If we proceed, do not depend on hoa/ruler directly from Packagist. Fork it into our VCS, pin a patch branch, add PHP 8 compatibility fixes and a test suite, and consume it as a path/VCS repository. Capture 2.17.05.16 as the exact baseline commit so the array-dimension behaviour is reproducible. In parallel, spike symfony/expression-language as the primary comparator: it is maintained, PHP 8-ready, has an explicit sandbox for untrusted expressions, supports attribute access on arrays and objects, and its syntax (and/or/not, registered functions) is close enough that most rules translate mechanically. The honest recommendation is still to adopt Symfony's engine behind the same adapter and skip Hoa unless the spike proves otherwise.
Compatibility. Verify Composer constraints against Laravel 10/11 and PHP 8.1+. Watch for conflicts between pinned hoa/* versions and Symfony components Laravel already ships — that is a realistic install-time blocker. The 2.17.05.16 feature is behaviourally additive, so it introduces no migration obligation for existing rules, but it does mean any previously flattened context can now be passed through — adopt that deliberately, not implicitly.
Sequencing.
symfony/expression-language.ArrayAccess context cases and missing-dimension cases so the 2.17.05.16 behaviour is locked down.Maintenance. We become the maintainer. That means PHP upgrade churn on a codebase nobody on the team wrote, plus the ongoing cost of an abandoned dependency appearing in SCA reports and slowing down procurement/audit reviews. Budget it as an owned internal component, not a third-party library. The 2.17.05.16 release changes nothing here — it is the last thing upstream shipped, and it shipped in 2017.
Support. No upstream, no CVE feed, no issue triage. Community answers are five-plus years stale and predate modern PHP. Internal documentation and tests are the only support surface.
Scaling. Evaluation is CPU-bound: grammar compilation dominates, then AST traversal. Compiling the rule string on every request is the default and is wasteful at volume — cache the parsed/compiled artifact keyed by rule hash, and memoize per-request when evaluating the same rule across a collection. Throughput is fine for per-request decision points; it is a poor fit for tight loops over large datasets without pre-compilation. Passing live ArrayAccess objects into evaluation can trigger lazy loading or query fan-out if those objects are Eloquent-backed — resolve hydration eagerly and pass plain structures at high volume.
Failure modes.
Ramp-up. The DSL itself is small — a competent developer is productive in a day. The real ramp is the surrounding discipline: context shaping, rule testing, versioning, and safe rollout. Business users cannot safely author raw rule strings without a validating UI, so plan for an engineering-mediated authoring flow in v1.
Verdict. Conditional no-go for greenfield adoption, unchanged by release 2.17.05.16. The array-dimension support in Asserter is a small, welcome ergonomics gain for hydrating Eloquent- and collection-shaped context, but it arrives four years after the project was abandoned and does not alter the maintenance, security, or PHP 8 exposure. Adopt symfony/expression-language behind an internal rule-engine abstraction, or buy a managed rules product; fork hoa/ruler only if the spike proves it clean and the rule corpus is small, stable, and owned by a team prepared to maintain it indefinitely.
How can I help you explore Laravel packages today?