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

Serializable Closure Laravel Package

laravel/serializable-closure

Securely serialize and unserialize PHP closures with Laravel’s maintained fork of opis/closure 3.x, updated for modern PHP without requiring FFI. Wrap closures in SerializableClosure, set a secret key, and safely persist or transport executable callbacks.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require laravel/serializable-closure
    

    Requires PHP 7.4+ and Laravel 8+ (or standalone usage).

  2. First Use Case: Serialize a simple closure for later execution:

    use Laravel\SerializableClosure\SerializableClosure;
    
    $closure = fn () => 'Hello, World!';
    SerializableClosure::setSecretKey('your_secure_key_here');
    
    // Serialize
    $serialized = serialize(new SerializableClosure($closure));
    
    // Later...
    $unserialized = unserialize($serialized);
    echo $unserialized->getClosure()(); // Outputs: Hello, World!
    
  3. Where to Look First:

    • README.md for basic usage.
    • Changelog for PHP/Laravel version compatibility.
    • Tests for edge cases (e.g., nested closures, attributes).

Implementation Patterns

Core Workflows

  1. Storing Closures for Later Execution:

    // Cache a closure for a delayed job
    $delayedClosure = new SerializableClosure(fn () => $this->processPayment());
    cache()->put('delayed_payment', $delayedClosure, now()->addHour());
    
    // Retrieve and execute later
    $stored = cache()->get('delayed_payment');
    $stored->getClosure()();
    
  2. Queue Jobs with Closures:

    // Serialize a closure for a queued job
    $job = new class implements ShouldQueue {
        public function handle() {
            $serialized = unserialize($this->serializedClosure);
            $serialized->getClosure()();
        }
    };
    
    $job->serializedClosure = serialize(new SerializableClosure(fn () => $this->sendEmail()));
    dispatch($job);
    
  3. Event Listeners with Dynamic Logic:

    // Store event handlers dynamically
    event(new OrderPlaced($order));
    $serializedHandler = new SerializableClosure(fn () => $this->notifyAdmin($order));
    cache()->forever("event_handler_{$order->id}", $serializedHandler);
    
    // Retrieve and execute during event dispatch
    $handler = cache()->get("event_handler_{$order->id}");
    $handler?->getClosure()();
    
  4. Middleware with Configurable Logic:

    // Serialize middleware logic per route
    routeMiddleware(['custom' => function ($request, $next) {
        $serialized = unserialize($request->route()->middleware['serialized']);
        return $serialized->getClosure()($request, $next);
    }]);
    
    // Store serialized closure in route definition
    Route::get('/admin', function () {})->middleware([
        'custom' => serialize(new SerializableClosure(fn ($req, $next) => $this->adminGuard($req, $next)))
    ]);
    

Integration Tips

  • Laravel Caching: Use SerializableClosure with cache()->put() to store closures for later execution (e.g., delayed processing, scheduled tasks).

  • Queue Jobs: Serialize closures in job payloads to defer execution (e.g., Bus::chain() with serialized steps).

  • Event System: Store event handlers in caches or databases for dynamic subscription (e.g., user-specific notifications).

  • API Rate Limiting: Serialize rate-limiting logic per user/IP:

    $limiter = new SerializableClosure(fn () => $this->throttleByRequests(100, 'minute'));
    cache()->put("rate_limiter_{$ip}", $limiter);
    
  • Testing: Mock serialized closures in tests to avoid recreating complex logic:

    $serialized = new SerializableClosure(fn () => $this->complexLogic());
    $this->app->instance('serialized.logic', $serialized);
    

Gotchas and Tips

Pitfalls

  1. Secret Key Management:

    • Issue: Forgetting to set SerializableClosure::setSecretKey() causes security vulnerabilities (unsigned closures can be tampered with).
    • Fix: Always set a strong secret key (e.g., from .env):
      SerializableClosure::setSecretKey(config('app.closure_secret'));
      
  2. REPL Environments:

    • Issue: Serialization fails in Laravel Tinker/Artisan REPL due to missing context.
    • Fix: Avoid using this package in REPLs. Use it only in HTTP requests or CLI scripts.
  3. Multiple Closures on One Line:

    • Issue: Closures defined on the same line with identical signatures may serialize incorrectly.
    • Fix: Place each closure on its own line:
      // Bad (may fail)
      $a = fn () => 1; $b = fn () => 2;
      
      // Good
      $a = fn () => 1;
      $b = fn () => 2;
      
  4. PHP 8.4+ Virtual Properties:

    • Issue: Serialization may fail with virtual properties or readonly classes.
    • Fix: Upgrade to v2.0.11+ (includes fixes for PHP 8.4+).
  5. Nested Closures:

    • Issue: Deeply nested closures (e.g., Bus::chain()) may break in older versions.
    • Fix: Use v2.0.9+ (includes nested closure fixes).
  6. First-Class Callables:

    • Issue: Namespaced closures (e.g., fn in namespace {}) may not serialize correctly.
    • Fix: Use v2.0.0+ (explicitly supports first-class callables).
  7. Enum/Attribute Serialization:

    • Issue: Closures using enums or attributes may fail.
    • Fix: Use v1.2.0+ (includes enum/attribute support).

Debugging Tips

  1. Validation Errors:

    • If unserialize() fails, check:
      • The secret key is set.
      • The closure doesn’t reference undefined variables (use use or static bindings).
      • No syntax errors in the closure (e.g., missing semicolons).
  2. Logging Serialized Data:

    $serialized = serialize(new SerializableClosure($closure));
    file_put_contents('debug.serialized', $serialized); // Inspect manually
    
  3. Testing Edge Cases:

    • Test with:
      • Closures using static, self, or parent.
      • Closures with named arguments.
      • Closures referencing class properties.
      • Closures with attributes (e.g., @deprecated).

Extension Points

  1. Custom Signing: Override the default HMAC signing:

    SerializableClosure::setSigner(function ($data, $key) {
        return hash_hmac('sha256', $data, $key);
    });
    
  2. Unsigned Closures: Disable signing for trusted environments:

    $unsigned = new SerializableClosure($closure, sign: false);
    
  3. Custom Serialization: Extend SerializableClosure to handle custom types:

    class CustomSerializableClosure extends SerializableClosure {
        public function __serialize(): array {
            $data = parent::__serialize();
            $data['custom_field'] = $this->customData;
            return $data;
        }
    }
    
  4. Integration with Laravel: Bind the package to the container for global access:

    $this->app->bind(SerializableClosure::class, function () {
        $closure = new SerializableClosure(...);
        SerializableClosure::setSecretKey(config('app.closure_secret'));
        return $closure;
    });
    

Performance Notes

  • Memory Usage: Serialized closures are larger than native closures (~2-3x overhead).
  • Execution Overhead: Deserialization adds ~1-2ms per closure (negligible for most use cases).
  • Caching: Reuse SerializableClosure instances for the same logic to avoid re-serialization.

Security

  • Secret Key: Treat setSecretKey() like APP_KEY—store it securely and rotate it periodically.
  • Untrusted Input: Never deserialize closures from user input without validation.
  • Signed Closures: Always prefer signed closures (sign: true) in production.
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