v13.39.0. For basic usage, see Session. For automatic provider loading, see Package discovery.
extend receives a handler, not a Store
The callback passed toSession::extend() returns a storage handler that implements PHP’s SessionHandlerInterface. It is not an API for returning a Store that implements Illuminate\Contracts\Session\Session, nor a cache Repository.
SessionManager::callCustomCreator() passes the callback’s return value to buildSession(), which wraps it in a Store or an EncryptedStore depending on session.encrypt. At that point it also applies the cookie name and session.serialization.
In normal HTTP handling, the StartSession middleware manages starting and saving the session. Your package does not need to call session_start() or session_set_save_handler() to provide a handler.
Think about the storage contract in separate parts
When you implement your own backend, fulfill the following roles.
Because the
Store handles serialization and restoration, the handler must not serialize() or json_encode() the session array again. When encryption is enabled, store the string processed by EncryptedStore as is.
Register only the creation logic in boot
The following minimal example reuses the built-inCacheBasedSessionHandler. It does not implement a new storage protocol; it shows how to register a custom driver that uses a named cache store. Acme\SessionBridge\ maps to src/ via Composer PSR-4.
Create the provider in src/SessionBridgeServiceProvider.php.
Session::extend() retrieves the SessionManager and registers the creation logic, but the cache store inside the callback is not resolved at this point. The storage is created when the driver is first selected. Do not run connection checks or store data in the provider’s boot().
If you use your own SessionHandlerInterface implementation, swap out the handler the callback returns. If you go as far as new Store(...), the return value no longer matches what SessionManager expects.
- Load the provider as a normal, non-deferred provider.
- Complete registration before the session is first used. If another provider resolves your custom driver earlier, check the loading order as well.
- Use a package-specific driver name. Calling
extend()with the same name overwrites the creation logic, and registering a built-in driver name takes precedence over the built-in implementation.
Let the consuming application select it explicitly
Users change the relevant keys in their existingconfig/session.php and leave the other settings in place.
stores array in config/cache.php. The following example uses the Redis connection default. The application is responsible for preparing the Redis connection settings and the required client.
createRedisDriver() of the built-in Redis session driver, adding session.connection or session.prefix is not automatically applied by this callback.
This callback does not modify the Repository or the storage configuration; it uses the named store as is. Even a test array store shares the same storage within the same application.
Do not unconditionally overwrite session.driver, the cookie name, or the application-wide cache prefix from your package. Also confirm with users that the storage is not shared with other uses and that multiple servers can use the same storage.
Preserve TTL units and the storage format
TheCacheBasedSessionHandler constructor receives the lifetime in minutes. write() passes $minutes * 60 to the cache Repository’s put() every time, so in the example above the TTL is refreshed on every write. gc() returns 0 and leaves expiration to the cache.
On the other hand, the lifetime that StartSession passes to the handler’s gc($lifetime) is in seconds. Do not write an implementation that stores the session.lifetime value as if it were seconds.
The following changes also involve more than renaming a setting.
If a package update changes any of these, consider guidance for re-authentication, whether a migration is needed, and deployments where old and new versions run side by side. As with Package versioning and compatibility, treat the storage format and configuration keys as part of the public specification you maintain.
Changing configuration alone does not replace a created Store
Manager, the parent class of SessionManager, retains created drivers by name. In addition, SessionServiceProvider registers session.store as a singleton.
forgetDrivers() is not an API for deleting session data either. Recreating the Store during an active request causes inconsistencies with data already loaded and with other references.
The CacheManager in this example also retains named stores. If you change that connection configuration, re-resolving only the SessionManager is not enough.
In deployments that use configuration caching, rebuild the cache, and restart long-lived workers so they load the new code and configuration. Restarting the application and migrating stored data are separate tasks.
Session storage and request locking are separate
Implementing a custom handler alone does not serialize concurrent requests for the same session.StartSession blocking uses locks from the cache store selected by session.block_store, which is not necessarily the same as the storage for the session itself.
If you control this with something like Route::post(...)->block(), separately verify a lock-capable cache store, sharing across multiple servers, and the wait and hold durations. Do not let your package enable application-wide blocking settings on its own.
Test through saving and reloading
In packages that use Orchestra Testbench, load the provider and write tests that actually select your custom driver. The following is an exampletests/SessionDriverTest.php. The test array cache does not use any external connection.
array as shared production storage.
Before publishing your own handler, also add tests for the following.
- Unsaved or expired IDs return an empty string
- A stored string is restored unchanged and cannot be retrieved after deletion
- TTL units and refreshes, and expiration on the real backend
- Read, write, and delete failures produce the defined return values or exceptions
- Round trips with
session.encryptenabled, and the supported serialization formats - Loading the provider alone does not access the storage and does not take over selection of built-in drivers
- If needed, HTTP tests that go through the actual
webmiddleware and cookies, and locking for concurrent requests
Related pages
Session
Review reading and writing sessions, flash data, and regenerating IDs.
Manager internals
Review how custom drivers are created and retained, and how calls are delegated to the default driver.
Custom cache stores in packages
Review the division of roles between the cache Store and Repository used as storage, and the TTL contract.
Package service lifecycles
Review dependencies retained by long-lived workers and how to isolate state.
Primary sources
- Official HTTP Session documentation: custom drivers and blocking
- Official package development documentation
- SessionManager: custom creation and building the Store
- Store: loading, serialization, saving, and ID regeneration
- CacheBasedSessionHandler: TTL conversion and the storage contract
- CacheManager: named stores and Redis configuration
- Manager: extend, driver, forgetDrivers
- SessionServiceProvider: singleton registration of the Manager and session.store
- StartSession: saving, GC, and cache-based locking
- Official CacheBasedSessionHandlerTest: verifying missing data, TTL, and deletion