- How do I install symfony/expression-language in a Laravel application?
- Install it with Composer by running composer require symfony/expression-language. The component is framework agnostic, so there is no Laravel package auto-discovery and no service provider is registered for you. After installing, bind ExpressionLanguage as a singleton in AppServiceProvider or resolve it directly from the container wherever you need it.
- Is symfony/expression-language a standalone package or does it require the Symfony framework?
- It is a standalone component that has no dependency on the Symfony framework, so it works fine inside Laravel or any plain PHP project. In Laravel, bind it once with $this->app->singleton(ExpressionLanguage::class, ...) and register your custom functions through a provider there. You can then inject it into controllers, policies, middleware, and jobs via the method signature instead of using the container manually.
- Which PHP and Laravel versions does symfony/expression-language support?
- There is no Laravel version constraint at all, so it works with any modern Laravel release as long as the PHP requirement is met. Symfony 7.x requires PHP 8.2 or newer, Symfony 6.4 requires PHP 8.1, and the 5.4 LTS line still supports PHP 7.2.5 and up. Check the composer.json of the exact tag you plan to adopt, and remember that patch releases in the 5.4 line carry security fixes worth tracking.
- How do I add custom functions or operators to ExpressionLanguage?
- Create an ExpressionFunction with a name, a compiler callback that emits PHP, and an evaluator callback that performs the runtime logic, then register it with addFunction(). For grouped, reusable domain functions such as user_has_permission or order_is_eligible, implement ExpressionFunctionProviderInterface and register it with addProvider(). When you bind the service as a singleton in Laravel, every consumer shares the same function set.
- Is it safe to evaluate user-supplied or database-stored expressions?
- The component uses a strict parser and compiles expressions to PHP instead of calling eval(), so expressions cannot execute arbitrary code, but that does not make untrusted input risk free. Always pass a whitelisted array of variables and expose only the functions you deliberately registered; never hand over raw Eloquent models or the entire request object. Since the 5.4.54 hardening patches, parse-time bounds on node-tree depth and expression nesting block the deep-nesting denial-of-service payloads that previously existed.
- How should I handle expressions stored in the database that fail to parse?
- Malformed or over-nested expressions now throw SyntaxError during parsing and evaluation rather than exhausting the stack, so wrap every evaluate() call in a try/catch and log the rule id, entity id, and user id. Decide explicitly whether a failing rule should fail open or fail closed, and encode that decision in code rather than leaving it implicit. The depth bound and the nesting bound are separate failure paths, so write tests that trip each one independently.
- How do I cache compiled expressions for better performance?
- Parsing and compiling a unique expression costs roughly one to five milliseconds the first time, after which the compiled PHP is reused for that ExpressionLanguage instance. Recent versions accept a PSR-6 cache pool as the constructor argument, so you can pass a compatible adapter and let parsed expressions be cached across requests. For rules evaluated many times per request, a container singleton binding combined with a cache keyed by a hash of the expression string keeps overhead negligible.
- Can ExpressionLanguage replace long if-chains in Laravel policies and middleware?
- Yes, this is one of the strongest use cases: evaluate a stored rule such as user.role === "admin" || user.hasPermission("edit_post") inside a policy or middleware with a tightly scoped context array. Inject ExpressionLanguage through the method signature or resolve it from the container to keep the call site concise. Keep the original PHP branch available behind a feature flag so you can shadow-compare results and roll back quickly if a rule misbehaves.
- How do I test and debug expressions in a Laravel codebase?
- Unit test by instantiating ExpressionLanguage directly, registering the same providers your application uses, and asserting evaluate() results across representative contexts, including malformed and deeply nested expressions that must throw SyntaxError. In production, wrap evaluations with contextual logging such as rule id, entity id, and user id, because stack traces from evaluated expressions rarely reveal which stored rule caused the failure. Shadow-mode comparisons against the legacy PHP logic are a practical way to validate new rules before they go live.
- What are the alternatives if I want to avoid another Composer dependency?
- Config-driven arrays, Laravel validation rules, or a closure-based rule registry can cover simple conditional logic without any extra package. Reach for ExpressionLanguage when rules are authored or edited outside the codebase and you need safe parsing plus extensible custom functions. Twig, a JSON Logic library, or a small bespoke parser are reasonable alternatives if you need a different syntax or a full templating engine.