- What is laravel/serializable-closure used for?
- laravel/serializable-closure converts a PHP closure into a portable string that can be stored or transported, then restores it later. Laravel uses it under the hood for queued closures, dispatch(fn () => ...) jobs, Bus::chain payloads, and other callables written to the queue or cache. It is a pure-PHP library with zero runtime dependencies and one public entry point: SerializableClosure::setSecretKey() plus wrapping, serializing, and unserializing closures.
- How do I install laravel/serializable-closure?
- Install it with Composer by running composer require laravel/serializable-closure. The package requires PHP 7.4 or higher and has no runtime dependencies, so there is no service provider to register and no configuration file to publish. In a normal Laravel application it is already present as a transitive dependency of laravel/framework.
- Do I need to set a secret key for laravel/serializable-closure?
- Setting a secret key is strongly recommended and is done once at boot with SerializableClosure::setSecretKey('your-secret'). The key enables HMAC signing so tampered or forged payloads fail verification on unserialize. Unsigned closures are supported, but they make deserialization a code-execution primitive if untrusted input ever reaches unserialize(). Treat the key as a credential and plan rotation carefully, because in-flight payloads signed with the old key will fail signature verification.
- Which PHP versions does laravel/serializable-closure support?
- The package requires PHP 7.4 or higher and adds support for new PHP releases as they ship. Because it parses closure source code, every PHP version introduces new syntax the parser must handle, including enums, readonly properties, first-class callables, named arguments, and property hooks. Keep producer and consumer PHP versions identical during rolling deploys, since source serialized with PHP 8.4 syntax will not parse on a PHP 8.2 worker.
- Is laravel/serializable-closure safe to use with untrusted input?
- No. Unserializing a closure re-evaluates extracted source code, which is functionally equivalent to running code, so it must never be applied to user-supplied data. Only unserialize payloads your own application produced and stored in a trusted channel such as your queue backend or cache. HMAC signing with setSecretKey() protects integrity, but it does not make untrusted deserialization safe.
- Why is laravel/serializable-closure a fork of opis/closure?
- Laravel forked opis/closure 3.x because the upstream 4.x rewrite depends on the FFI extension, which is not enabled by default in typical web and PHP-FPM request contexts. The fork keeps a pure-PHP implementation of closure serialization while adding support for newer PHP versions. It is maintained by Laravel and powers the framework's queue and cache serialization.
- Do I need to install laravel/serializable-closure in a Laravel app?
- Usually not explicitly. It arrives as a transitive dependency of laravel/framework, which is why its dependents and stars metrics understate real adoption. Add it directly only when writing a package or standalone PHP application that serializes closures outside Laravel. Either way, pin the version and monitor releases, because patch releases have historically introduced parser regressions.
- What are the caveats of serializing closures with laravel/serializable-closure?
- Serializing closures from a REPL such as Laravel Tinker is not supported. Multiple closures declared on the same source line with identical signatures may not be distinguishable after serialization, so put each closure on its own line. Also remember that the closure body is frozen at serialization time while the classes, functions, and namespaces it references resolve against the current codebase on unserialize, so renames and signature changes fail inside a worker rather than at dispatch.
- Can laravel/serializable-closure break my queue after an upgrade?
- Yes, and it has before. Version 2.0.9 shipped a regression that broke Bus::chain with nested closures, fixed only in 2.0.11, which shows a patch release can disrupt production queue dispatch. Pin the version in composer.json, cover queued-closure paths in CI, and stage upgrades before rolling them out to workers.
- How do I test closures serialized with laravel/serializable-closure?
- Wrap the closure in SerializableClosure, set a test secret key, serialize it, unserialize it, and assert the restored closure returns the expected value when invoked. Include cases for nested closures and Bus::chain payloads, since those are the paths that have broken in past releases. If you are migrating from opis/closure 3.x, add a test that round-trips an existing serialized payload or drain the queue before switching.