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 an engine to compile and evaluate one-line expressions that return values (often booleans). Use it to embed simple, safe business rules and conditions in your app, with support for custom functions and variables.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require symfony/expression-language
    

    For Laravel, add to composer.json under require or use laravel/symfony-expression-language if available.

  2. Basic Usage:

    use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
    
    $expressionLanguage = new ExpressionLanguage();
    $result = $expressionLanguage->evaluate(
        'user.role === "admin" || user.hasPermission("edit")',
        ['user' => $user]
    );
    
  3. First Use Case: Replace a simple if condition in a Laravel policy:

    // Before: App/Policies/UserPolicy.php
    public function update(User $user, User $model)
    {
        return $user->isAdmin() || $user->id === $model->id;
    }
    
    // After:
    $expressionLanguage = app(ExpressionLanguage::class);
    $canUpdate = $expressionLanguage->evaluate(
        'user.isAdmin() || user.id === model.id',
        ['user' => $user, 'model' => $model]
    );
    

Where to Look First


Implementation Patterns

Core Workflows

1. Dynamic Policy Evaluation

Pattern: Replace verbose if chains in policies with expressions stored in a database.

// Database: policies table
// id | model | expression
// 1  | User | 'user.id === model.id || user.role === "admin"'

// Policy.php
public function update(User $user, User $model)
{
    $expression = Policy::where('model', 'User')->first()->expression;
    return app(ExpressionLanguage::class)->evaluate($expression, [
        'user' => $user,
        'model' => $model,
    ]);
}

2. Middleware for Dynamic Routing

Pattern: Route users based on expressions (e.g., feature flags, A/B tests).

// Kernel.php
protected function routeMiddleware()
{
    return [
        // ...
        \App\Http\Middleware\DynamicRoute::class,
    ];
}

// DynamicRoute.php
public function handle($request, Closure $next)
{
    $expression = config('features.user_dashboard');
    if (app(ExpressionLanguage::class)->evaluate($expression, [
        'user' => $request->user(),
        'request' => $request,
    ])) {
        return redirect('/dashboard');
    }
    return $next($request);
}

3. Form Validation with Database Rules

Pattern: Store validation rules in a validation_rules table and apply them dynamically.

// ValidationRule.php (Model)
public function validate($data)
{
    $expression = $this->expression;
    return app(ExpressionLanguage::class)->evaluate($expression, [
        'data' => $data,
        'rule' => $this,
    ]);
}

// Example rule: 'data.email.endsWith("@company.com") || data.phone.startsWith("+1")'

4. Feature Flags

Pattern: Enable/disable features per user segment.

// config/features.php
'new_ui' => 'user.region === "EU" || user.isBetaTester()',

// FeatureService.php
public function isEnabled(string $feature, User $user)
{
    return app(ExpressionLanguage::class)->evaluate(
        config("features.$feature"),
        ['user' => $user]
    );
}

Integration Tips

Laravel-Specific

  1. Service Binding: Bind the ExpressionLanguage instance in a service provider:

    public function register()
    {
        $this->app->singleton(ExpressionLanguage::class, function ($app) {
            $el = new ExpressionLanguage();
            $el->addProvider(new UserProvider()); // Custom provider for user methods
            return $el;
        });
    }
    
  2. Variable Providers: Extend ProviderInterface to expose custom objects/methods:

    use Symfony\Component\ExpressionLanguage\Provider\ProviderInterface;
    
    class UserProvider implements ProviderInterface
    {
        public function getParameters()
        {
            return ['user'];
        }
    
        public function getMethods()
        {
            return ['isAdmin', 'hasPermission'];
        }
    }
    
  3. Caching Compiled Expressions: Cache compiled expressions for performance-critical paths (e.g., policies):

    $cacheKey = 'expr:' . md5($expression);
    $compiled = Cache::remember($cacheKey, 60, function () use ($expression) {
        return app(ExpressionLanguage::class)->compile($expression);
    });
    

Symfony Integration

  • Use with Symfony’s ExpressionLanguage in controllers/services:
    $this->expressionLanguage->evaluate(
        'product.price > 100 && product.stock > 0',
        ['product' => $product]
    );
    

Testing

  • Mock the ExpressionLanguage in tests:
    $expressionLanguage = $this->createMock(ExpressionLanguage::class);
    $expressionLanguage->method('evaluate')->willReturn(true);
    $this->app->instance(ExpressionLanguage::class, $expressionLanguage);
    

Gotchas and Tips

Pitfalls

  1. Variable Whitelisting:

    • Gotcha: Forgetting to add required variables to the evaluation context.
      // Fails: 'user' is not defined
      $el->evaluate('user.isAdmin()'); // Throws Error\UndefinedVariableException
      
    • Fix: Always pass all variables used in the expression:
      $el->evaluate('user.isAdmin()', ['user' => $user]);
      
  2. Null Safety:

    • Gotcha: Null-safe operators (?.) may not work as expected with older Symfony versions.
      // May fail in Symfony <7.3
      $el->evaluate('user?.profile.name');
      
    • Fix: Upgrade to Symfony 7.3+ or use explicit checks:
      $el->evaluate('user !== null && user.profile !== null && user.profile.name');
      
  3. Thread Safety:

    • Gotcha: Compiled expressions may not be thread-safe in older versions (pre-7.3).
      // Risky in Symfony <7.3
      $compiled = $el->compile('1 + 1'); // May cause issues in concurrent environments
      
    • Fix: Use Symfony 7.3+ or avoid sharing compiled expressions across threads.
  4. PHP Version Requirements:

    • Gotcha: Symfony 8.x requires PHP 8.4+. Laravel 10/11 may need compatibility checks.
    • Fix: Use Symfony 7.4 for PHP 8.1+ or 6.4 for older PHP versions.
  5. Complex Expressions:

    • Gotcha: Deeply nested or complex expressions can be hard to debug.
      // Hard to debug
      $el->evaluate('(user.group === "admin" && (order.amount > 1000 || order.isUrgent())) ? true : false');
      
    • Fix: Break into smaller expressions or use comments:
      // Allowed in Symfony 7.1+
      $el->evaluate('# Admin can override limits
                     user.group === "admin" ||
                     # Regular users with large/urgent orders
                     (order.amount > 1000 || order.isUrgent())');
      

Debugging Tips

  1. Enable Debug Mode:

    $el = new ExpressionLanguage();
    $el->setDebug(true); // Shows compilation errors
    
  2. Log Compiled Expressions:

    $compiled = $el->compile('user.isAdmin()');
    \Log::debug('Compiled expression:', ['code' => $compiled]);
    
  3. Use repr() for Variable Inspection:

    $repr = $el->repr($user); // Converts object to a string representation
    \Log::debug('User object:', ['repr' => $repr]);
    
  4. Test with Simple Expressions First: Start with basic expressions (e.g., `'1 + 1

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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport