Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Rule Parser Laravel Package

nicoswd/php-rule-parser

A lightweight PHP parser for validation-style rule strings, turning expressions like required|max:255 into structured rule objects with names and parameters. Useful for custom validators, form/request tooling, or inspecting Laravel-style rules without a full framework.

Deep Wiki
Context7

Getting Started

Install with Composer and confirm the package's PHP version constraint against your runtime:

composer require nicoswd/php-rule-parser

The entry point is Nicoswd\PhpRuleParser\RuleParser (check the quickstart in the README for the exact namespace and whether your version exposes static or instance methods). The minimal flow is parse-then-evaluate:

use Nicoswd\PhpRuleParser\RuleParser;

$rule = RuleParser::parse('age >= 18 && country == "US"');

$rule->evaluate([
    'age' => 21,
    'country' => 'US',
]); // true

Look first at:

  1. The README quickstart and any "supported operators" table.
  2. src/RuleParser.php and the Rule class — that's where parse/evaluate and the function registry live.
  3. tests/ — the fastest way to learn coercion, null handling, and precedence, because those are the behaviors that surprise people.
  4. composer.json for the PHP constraint and whether an AST/cache layer is exposed.

First use case: move a hard-coded if out of a service and into a stored expression. A promo eligibility check is ideal: business users can change the threshold without a deploy, and the expression is small enough to reason about.

$expression = 'cart.total >= 50 && customer.tier in ["gold", "platinum"] && !customer.blocked';
$eligible = RuleParser::parse($expression)->evaluate($payload);

Start with one rule, one data shape, and a test that pins the expected truth table before you expand.

Implementation Patterns

Compile once, evaluate many. Parsing is the expensive part; treat parse() as a compile step and reuse the Rule object. Never call parse() inside a loop over rows — parse outside, evaluate inside.

$rule = RuleParser::parse($expression);
$results = [];
foreach ($customers as $customer) {
    $results[$customer->id] = $rule->evaluate($customer->toArray());
}

Store expressions, not PHP. Keep rule strings in a rules table (name, expression, version, active) or in config/rules.php. Parse at boot for static config; parse on demand with a cache for DB-backed rules. Version the expression so you can audit changes and roll back a bad rule without touching code.

Wrap it in a domain service. Don't let RuleParser leak across the codebase. A thin wrapper gives you a single place to catch parse errors, normalize payloads, log evaluations, and swap the engine later.

final class EligibilityRule
{
    public function __construct(private string $expression) {}

    public function passes(array $context): bool
    {
        try {
            return (bool) RuleParser::parse($this->expression)->evaluate($context);
        } catch (\Throwable $e) {
            report($e);
            return false; // fail closed for eligibility decisions
        }
    }
}

Pin your data contract. Decide once whether the evaluator sees nested arrays with dot access (customer.tier) or flat keys (customer_tier), and normalize at the boundary. A dedicated RuleContext builder keeps controllers and jobs from each passing a slightly different shape.

Validate at authoring time, not request time. When a rule is saved (admin UI, seeder, config deploy), parse it and run it against a small fixture set. A syntax error discovered in the admin form is a validation message; discovered in a checkout flow is an incident.

try {
    $rule = RuleParser::parse($request->input('expression'));
    $rule->evaluate($this->samplePayload()); // smoke test
} catch (\Throwable $e) {
    throw ValidationException::withMessages(['expression' => $e->getMessage()]);
}

Register domain functions for repeated logic. Instead of duplicating customer.created_at arithmetic in every rule, expose a named function and keep the expression readable. Register functions once during application bootstrap (a service provider is the natural place), not per call.

Cache parsed rules where it matters. If rules come from the database and are evaluated per request, cache the parsed object keyed by rule id plus a content hash (Laravel's Cache::remember works). Invalidate on update. Beware: serializing an AST that holds closures will fail — cache the expression and re-parse, or cache only rules without custom closures.

Test rules as data, not code. A PHPUnit data provider mapping [expression, payload, expected] is the highest-value test suite here. It doubles as documentation for whoever edits rules next, and it catches engine edge cases before they reach production.

Gotchas and Tips

JavaScript-like semantics are not PHP semantics. Expect differences in loose equality, string-to-number coercion, truthiness of "0" and [], and + doing both addition and concatenation. When a rule behaves oddly, reduce it to a one-line expression and evaluate it in isolation before blaming your payload.

Missing keys are the most common production failure. Decide explicitly what happens when a field is absent — null, exception, or false — and test it. Defensive rules (customer.tier != null && customer.tier == "gold") are uglier but survive optional data. Better: normalize the payload so every referenced key exists.

Numeric strings bite. Database and request values often arrive as strings ("50"). >= may coerce, === will not. Cast at the boundary or standardize on == inside rules — but pick one and enforce it in review.

Don't evaluate untrusted expressions. A rule engine that supports function calls is close to a scripting language. If non-developers can author rules, restrict the function whitelist, avoid exposing anything that touches the filesystem, network, or container, and never interpolate user input into an expression string. Build the expression from a structured builder if you can.

Performance: watch the loop, not the parse. A single parse is fine; thousands of parses per request are not. Also watch repeated evaluate() calls that rebuild context arrays — hoist the array construction out of the loop.

Global function/operator registries leak between tests. If registration is static, one test's custom function is visible to the next. Reset or re-register in setUp(), and prefer registering once in a bootstrap that tests share deliberately.

Debug by bisecting the expression. When a compound rule returns the wrong answer, split on && / ||, evaluate each clause with the same payload, and find the clause that diverges. If the library exposes the AST, dump it — precedence bugs look obvious in tree form and invisible in string form.

Fail closed for decisions, fail loud for tooling. Eligibility, pricing, and access checks should return false and log on error. Admin previews and CI validation should throw so authors see the problem immediately.

Pin the version. With a recent release line and a relatively small ecosystem, an unpinned ^ constraint can change coercion or operator behavior under you. Lock the minor version and re-run your rule fixture suite on upgrade — that suite is your real compatibility test.

Extension points are your escape hatch, but use them sparingly. Custom functions and operators are great for a single well-named domain concept (is_business_day, in_region). They're a bad place to hide multi-step business logic that belongs in a service; rules should stay readable to the people who edit them.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core