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

Technical Evaluation

Architecture Fit

  • Strengths:

    • Decouples logic from code: Enables dynamic, configurable rules without modifying application logic (aligns with Laravel’s service container and dependency injection principles).
    • Compiled execution: Converts expressions to optimized PHP bytecode, reducing runtime overhead for high-frequency evaluations (critical for APIs, middleware, or policies).
    • Extensible syntax: Supports custom functions, operators, and providers, allowing domain-specific extensions (e.g., user.hasPermission() or order.isEligibleForDiscount()).
    • Thread-safe: Recent fixes (e.g., Compiler::repr thread-safety) ensure stability in concurrent environments (e.g., Laravel queues or Horizon jobs).
    • Symfony/Laravel synergy: Integrates seamlessly with Laravel’s Policies, Middleware, Form Requests, and Service Container (via ExpressionLanguage service binding).
  • Potential Friction Points:

    • Abstraction overhead: Compilation step adds minor latency (~1–5ms per unique expression) during first evaluation. Mitigate by caching compiled expressions (e.g., via Laravel’s cache or Symfony’s ExpressionLanguage compiler cache).
    • Debugging complexity: Stack traces for evaluated expressions may obscure origin (e.g., database-stored rules). Use custom error handlers or wrap evaluations in try-catch blocks with context logging.
    • Variable scoping: Requires explicit variable whitelisting to prevent injection. Laravel’s service container can inject safe contexts (e.g., ['user', 'request']).

Integration Feasibility

  • Laravel Compatibility:

    • Service Provider: Bind the component as a singleton in AppServiceProvider:
      $this->app->singleton(ExpressionLanguage::class, function ($app) {
          $language = new ExpressionLanguage();
          $language->addProvider(new FunctionProvider([
              'user_has_permission' => [$app['auth']->user(), 'hasPermission'],
          ]));
          return $language;
      });
      
    • Facade: Create a Expression facade for concise syntax (e.g., Expression::evaluate('user.isAdmin()')).
    • Service Injection: Dependency-inject ExpressionLanguage into controllers, policies, or middleware:
      public function handle(Request $request, ExpressionLanguage $expressionLanguage) {
          $allowed = $expressionLanguage->evaluate(
              'user.role === "admin" || user.hasPermission("edit_post")',
              ['user' => $request->user()]
          );
      }
      
  • Database-Backed Rules:

    • Store expressions in a rules table (e.g., pricing_rules, feature_flags) and hydrate them dynamically:
      $rule = Rule::where('entity_type', 'order')->where('entity_id', $order->id)->first();
      $discount = $expressionLanguage->evaluate($rule->expression, [
          'order' => $order,
          'user' => $order->user,
      ]);
      
    • Use Eloquent accessors or model observers to auto-evaluate expressions on model events (e.g., retrieved, saved).
  • Policy/Middleware Integration:

    • Replace verbose if chains in App\Policies\UserPolicy with expressions:
      public function update(User $user, User $model)
      {
          return $this->expressionLanguage->evaluate(
              'user.id === model.id || user.hasPermission("edit_any_user")',
              compact('user', 'model')
          );
      }
      
    • Dynamically generate middleware via route model binding or policy checks:
      Route::get('/admin', function () {
          return $this->expressionLanguage->evaluate(
              'user.isAdmin() && request.path.startsWith("/admin")',
              ['user' => auth()->user(), 'request' => request()]
          );
      })->middleware(function ($request, $next) {
          if (!$this->expressionLanguage->evaluate(
              'user.isAdmin() && request.path.startsWith("/admin")',
              ['user' => $request->user(), 'request' => $request]
          )) {
              abort(403);
          }
          return $next($request);
      });
      

Technical Risk

  • Security Risks:

    • Injection: Evaluate only whitelisted variables and predefined functions. Use ExpressionLanguage::addProvider() to restrict available functions/objects.
    • Mitigation:
      • Validate expressions against a regex pattern (e.g., ^[a-zA-Z0-9_\.\+\-\*\/\%\&\|\!\=\<\>\(\)\?\:\s]+$) before evaluation.
      • Use compiled expressions (via ExpressionLanguage::compile()) to avoid runtime parsing.
      • Log evaluated expressions with context (e.g., user_id, rule_id) for auditing.
    • Example: Restrict to safe operators/functions:
      $language = new ExpressionLanguage();
      $language->addProvider(new FunctionProvider([
          'user_has_permission' => [$user, 'hasPermission'],
          'order_is_eligible' => [$order, 'isEligibleForDiscount'],
      ]));
      
  • Performance Risks:

    • Cold starts: First evaluation compiles the expression (~1–5ms). Mitigate by:
      • Caching compiled expressions (e.g., Cache::remember() or Symfony’s ExpressionLanguage cache).
      • Pre-compiling expressions during model hydration or route registration.
    • Memory: Compiled expressions consume minimal memory (~KB per expression). Monitor in high-scale environments (e.g., Laravel Forge/Panel).
  • Compatibility Risks:

    • PHP 8.4+: Requirement for Symfony 8.0+. Ensure Laravel app meets this (or use Symfony 7.x for PHP 8.1+).
    • Laravel Ecosystem: Test with:
      • Policies: Ensure authorize() methods work with injected ExpressionLanguage.
      • Form Requests: Validate rules dynamically (e.g., Rule::expression('input.length > 5')).
      • Nova: Extend toolbars or detail views with expression-driven logic.

Key Questions

  1. Use Cases:
    • Which dynamic rules will this replace? (e.g., pricing, access control, validation).
    • How frequently will these rules change? (Daily? Per user segment?)
  2. Data Sources:
    • Will expressions come from a database, config files, or API responses?
    • How will you validate user-provided expressions? (Regex? Whitelisting?)
  3. Performance:
    • How many expressions will be evaluated per request? (Cache compiled versions?)
    • What’s the SLA for expression evaluation? (e.g., <10ms for API responses)
  4. Security:
    • Are expressions trusted (e.g., admin-defined) or untrusted (e.g., user-uploaded)?
    • How will you audit evaluated expressions? (Logging? Database trails?)
  5. Integration:
    • Which Laravel components will use this? (Policies, Middleware, Form Requests, Jobs?)
    • Will you create a facade or helper methods for consistency?
  6. Error Handling:
    • How will you handle syntax errors or missing variables? (Graceful fallbacks? Custom exceptions?)
  7. Testing:
    • How will you test expressions? (Unit tests for compiled logic? Integration tests for dynamic rules?)
    • Will you mock the ExpressionLanguage service in tests?

Integration Approach

Stack Fit

  • Laravel Native:
    • Service Container: Bind ExpressionLanguage as a singleton with preconfigured providers/functions.
    • Facades/Helpers: Create a Expression facade or ExpressionHelper trait for concise syntax.
    • Service Injection: Inject into controllers, policies, middleware, and jobs.
  • Symfony Synergy:
    • Leverage Symfony’s DependencyInjection, Compiler Passes, and Cache systems.
    • Use ExpressionLanguage with Symfony’s Validator for dynamic validation rules.
  • Database Integration:
    • Store expressions in JSON/TEXT columns (e.g., pricing_rules.expression).
    • Use Eloquent accessors or model events to evaluate expressions on demand.
  • Caching Layer:
    • Cache compiled expressions (e.g., Cache::remember('expression:user_policy', 60, fn() => $language->compile($expression))).
    • Integrate with Redis or Symfony Cache for distributed environments.

Migration Path

  1. Pilot Phase:
    • Start with non-critical use cases (e.g., feature flags, non-sensitive validation).
    • Example: Replace a hardcoded policy:
      // Before
      public function update(User $user, Post $post) {
          return $user->isAdmin() || $user->id === $post->user_id;
      }
      // After
      public function update(User $user, Post $post, ExpressionLanguage $language) {
          return $language->evaluate(
              '
      
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