Weave Code
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

Expression Language Laravel Package

symfony/expression-language

Symfony ExpressionLanguage provides a fast engine to evaluate and compile simple one-line expressions (often boolean rules). Use it to embed dynamic conditions, calculations, and access to variables/functions in a safe, extensible way.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Package:

    composer require symfony/expression-language
    

    For Laravel, no additional configuration is needed—it’s a standalone component.

  2. Basic Evaluation:

    use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
    
    $expressionLanguage = new ExpressionLanguage();
    $result = $expressionLanguage->evaluate(
        "'user' instanceOf App\Models\User && user.isActive()",
        ['user' => $user]
    );
    
  3. First Use Case: Replace a hardcoded policy check in UserPolicy:

    // Before: Static logic in code
    public function update(User $user, User $model)
    {
        return $user->isAdmin() && $model->isActive();
    }
    
    // After: Dynamic expression (stored in DB/cache)
    $expression = 'user.isAdmin() && model.isActive()';
    $allowed = $expressionLanguage->evaluate($expression, [
        'user' => $user,
        'model' => $model,
    ]);
    

Implementation Patterns

Core Workflows

1. Dynamic Policy Evaluation

  • Pattern: Store expressions in a policies table (e.g., user_can_update column).
  • Example:
    // Database record: { "expression": "'user' instanceOf App\Models\User && user.canEdit(model)" }
    $policyExpression = Policy::find($id)->expression;
    $canUpdate = $expressionLanguage->evaluate($policyExpression, [
        'user' => auth()->user(),
        'model' => $model,
    ]);
    

2. Middleware for Dynamic Authorization

  • Pattern: Compile expressions once and reuse in middleware.
  • Example:
    $expressionLanguage = app(ExpressionLanguage::class);
    $compiled = $expressionLanguage->compile(
        "'user' instanceOf App\Models\User && user.hasPermission('edit_post')"
    );
    
    // Reuse compiled expression in middleware
    $compiled->evaluate([
        'user' => auth()->user(),
        'post' => $post,
    ]);
    

3. Validation Rules from Database

  • Pattern: Store validation logic in a validation_rules table.
  • Example:
    $rule = ValidationRule::where('field', 'email')->first();
    $isValid = $expressionLanguage->evaluate($rule->expression, [
        'input' => $request->input('email'),
    ]);
    

4. Feature Flags with Context

  • Pattern: Evaluate flags per user/request.
  • Example:
    $flag = FeatureFlag::where('name', 'new_ui')->first();
    $isEnabled = $expressionLanguage->evaluate($flag->expression, [
        'user' => auth()->user(),
        'request' => request(),
    ]);
    

Integration Tips

Laravel-Specific Integrations

  • Service Provider Binding:

    $this->app->singleton(ExpressionLanguage::class, function ($app) {
        $language = new ExpressionLanguage();
        // Register custom functions (see below)
        return $language;
    });
    
  • Policy Integration: Extend Authorizable to use expressions:

    public function authorize($ability, $model = null)
    {
        $expression = PolicyExpression::for($ability, $model)->get();
        return $this->expressionLanguage->evaluate($expression, [
            'user' => $this,
            'model' => $model,
        ]);
    }
    
  • Form Request Validation:

    public function rules()
    {
        $customRules = CustomValidationRule::all()->pluck('expression');
        return array_merge(
            ['email' => 'required'],
            $customRules->mapWithKeys(fn ($expr) => ['custom' => $expr])
        );
    }
    

Performance Optimization

  • Compile Once, Reuse: Cache compiled expressions for high-frequency use (e.g., middleware, policies):

    $compiled = $expressionLanguage->compile($expression);
    cache()->remember("compiled_{$expression}", now()->addHours(1), fn() => $compiled);
    
  • Variable Whitelisting: Restrict variables to trusted objects only:

    $expressionLanguage = new ExpressionLanguage();
    $expressionLanguage->addProvider(new UserProvider()); // Custom provider for 'user' variable
    

Extending Functionality

  • Custom Functions:

    $expressionLanguage = new ExpressionLanguage();
    $expressionLanguage->addFunction('is_vip', function ($user) {
        return $user->subscription === 'vip';
    });
    
    // Usage: `is_vip(user)`
    
  • Custom Operators:

    $expressionLanguage->addOperator(new InstanceOfOperator());
    // Usage: `'user' instanceOf App\Models\User`
    

Gotchas and Tips

Pitfalls

  1. Variable Scope Leaks:

    • Issue: Accidental access to global variables (e.g., $_SERVER, app()).
    • Fix: Whitelist variables explicitly:
      $expressionLanguage->evaluate($expression, ['user' => $user], ['user']);
      
  2. Null References:

    • Issue: null variables can cause errors (e.g., user->method()).
    • Fix: Use null-safe operators (?.) or coalesce:
      // Expression: `user?.isActive()`
      // Or: `user is null ? false : user.isActive()`
      
  3. Compilation Cache Invalidation:

    • Issue: Cached compiled expressions may not update if the underlying expression changes.
    • Fix: Use versioned keys or clear cache on expression updates:
      cache()->forget("compiled_{$expression}_v{$version}");
      
  4. Complex Expressions and Readability:

    • Issue: Overly complex expressions are hard to debug/maintain.
    • Fix: Break into smaller expressions or use comments:
      // Expression: `is_premium(user) && (is_weekend(date) || is_holiday(date))`
      
  5. Thread Safety:

    • Issue: Compiled expressions may not be thread-safe if they rely on shared state.
    • Fix: Avoid global state in custom functions; use request/user-specific data.

Debugging Tips

  • Enable Debug Mode:

    $expressionLanguage->setDebug(true); // Logs parsed expressions
    
  • Inspect Parsed AST:

    $parser = new Parser();
    $node = $parser->parse($expression);
    dump($node); // Debug structure
    
  • Test Edge Cases:

    • Empty expressions: "" or " ".
    • Malformed syntax: user..method() (double dot).
    • Nested conditions: a && b || c && d (operator precedence).

Configuration Quirks

  1. Provider Order Matters:

    • Custom providers are evaluated in registration order. Register critical ones first:
      $expressionLanguage->addProvider(new UserProvider());
      $expressionLanguage->addProvider(new RequestProvider());
      
  2. Reserved Keywords:

    • Avoid using PHP reserved words (e.g., new, instanceof) as variable names.
    • Use quotes for safe names: 'new'.
  3. PHP Version Compatibility:

    • Symfony 8+ requires PHP 8.4+. For older PHP, use v7.x branches.
    • Features like match expressions require PHP 8.0+.

Extension Points

  1. Custom Providers:

    • Override variable resolution:
      class UserProvider implements ProviderInterface {
          public function getVariables(array $variables, array $names) {
              return ['user' => auth()->user()];
          }
      }
      
  2. Node Visitors:

    • Modify AST before compilation (e.g., for logging or rewriting):
      $visitor = new class implements NodeVisitorInterface {
          public function enterNode(Node $node) { /* ... */ }
          public function leaveNode(Node $node) { /* ... */ }
      };
      $expressionLanguage->addNodeVisitor($visitor);
      
  3. Error Handling:

    • Customize error messages:
      $expressionLanguage->setErrorHandler(function (ParseError $error) {
          return "Invalid expression: {$error->getMessage()} (at line {$error->getLine()})";
      });
      
  4. Compilation Hooks:

    • Intercept compiled PHP code:
      $expressionLanguage->addCompilerPass(new class implements CompilerPassInterface {
          public function onCompile(Node $node, Compiler $compiler) {
              $compiler->addDebugInfo("Custom debug info");
          }
      });
      
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle