laravel/serializable-closure
Securely serialize and unserialize PHP closures with Laravel’s fork of opis/closure 3.x, updated for modern PHP without requiring FFI. Wrap closures in SerializableClosure, set a secret key for signing, serialize safely, then restore with getClosure().
SerializableClosure), allowing for future extensions (e.g., encryption, signing, or custom serialization logic).SerializableClosure before serialization (e.g., serialize(new SerializableClosure($closure))). No need to modify existing closure logic.Bus::chain).Cache::put($key, $serializedClosure)).| Risk Area | Assessment | Mitigation |
|---|---|---|
| Security | Closures must be signed to prevent tampering. The package provides setSecretKey() for signing, but misconfiguration (e.g., hardcoded keys) could expose vulnerabilities. |
Enforce secure key management (e.g., environment variables, Laravel’s config). Use sign: true explicitly in SerializableClosure. |
| Performance Overhead | Serialization/deserialization adds CPU/memory overhead. Benchmarking shows ~10-20% overhead for complex closures (per opis/closure benchmarks). | Profile critical paths (e.g., queue jobs). Cache serialized closures where possible. Avoid overusing for trivial logic. |
| Edge Cases | Caveats in README (e.g., REPL environments, multiple closures on the same line) may break in edge cases. PHP 8.4+ virtual properties or attributes could introduce regressions. | Test in staging with real-world closures (e.g., from migrations, middleware). Use SerializableClosure::setSecretKey() to debug deserialization failures. |
| Dependency on PHP Features | Relies on PHP’s serialize()/unserialize(), which may behave unpredictably with custom objects or magic methods. PHP 8.5+ features (e.g., constant expressions) are supported but untested in all contexts. |
Validate with PHPStan/Psalm. Test on target PHP versions. Avoid closures with dynamic __invoke or magic methods. |
| Laravel Version Lock | Officially supports Laravel 12/13 but may work with older versions. No guarantees for Laravel 11 or below. | Pin to a stable minor version (e.g., ^2.0) in composer.json. Test against target Laravel version. |
secretKey be managed (e.g., environment variable, Laravel config)? Is signing enabled by default?opis/closure been considered? Why was this Laravel fork preferred?Closure::fromCallable) that could complement this?config('app.serializable_closure_secret')).Bus::chain), Horizon, and third-party queue systems (e.g., RabbitMQ, SQS).| Current State | Migration Strategy | Risks |
|---|---|---|
Manual Serialization (e.g., base64_encode(serialize($closure))) |
Replace with SerializableClosure::serialize($closure). Update deserialization to use unserialize($serialized)->getClosure(). |
Breaking changes if existing code relies on raw serialize() output. |
Custom JSON/Closure Workarounds (e.g., ['class' => 'Closure', 'data' => ...]) |
Replace with SerializableClosure. Update serialization logic to emit SerializableClosure objects instead of raw closures. |
May require changes to cache/event systems if they expect specific payload formats. |
| No Closure Serialization | Identify pain points (e.g., queues, caching) and introduce SerializableClosure incrementally. Start with non-critical paths (e.g., logging, analytics). |
Risk of missed use cases. Requires proactive discovery of closures needing serialization. |
Third-Party Packages (e.g., opis/closure) |
Replace opis/closure with laravel/serializable-closure. Update imports and configuration (e.g., secret key). |
Potential API differences (e.g., method names, signing behavior). |
^2.0 in composer.json and test against target Laravel version.composer.json for conflicts (e.g., opis/closure).SerializableClosure in a non-production environment with 2–3 critical use cases (e.g., a queue job, a cached closure).How can I help you explore Laravel packages today?