Weave 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

Redis Session Handler Bundle Laravel Package

tyrola/redis-session-handler-bundle

Symfony bundle providing a Redis-backed session handler, letting you store PHP sessions in Redis for faster, shared session storage across servers.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: you do not install this package in a Laravel app. It is a Symfony2 bundle (BirknerAlex/RedisSessionHandlerBundle, forked/published as tyrola/redis-session-handler-bundle), last released 2016-02-24, archived, 1 star, 0 dependents. It registers a Symfony DI service plus a bundle class — none of which Laravel's container, Kernel, or autoloader knows how to boot. Treat it as a reference implementation of a Redis-backed \SessionHandlerInterface, not a dependency.

Your first move:

  1. Confirm you actually have a session problem. Laravel already ships SESSION_DRIVER=redis; for 95% of cases that is the answer and this bundle is irrelevant.
  2. Look at the bundle's DependencyInjection/ (Configuration.php, services.xml/*.yml) and its handler class. That's the entire package — a few hundred lines that show: connection setup, key prefixing, TTL on write, and gc() behavior. That is what you'd port.
  3. First concrete use case: a mixed Symfony2 + Laravel estate where sessions must be shared, or a legacy Symfony2 service you're migrating and you need to understand/relocate its session store before you can decommission it.

Minimal Laravel equivalent (the thing you should actually write):

// config/session.php
'driver'     => 'redis',
'connection' => 'session',      // dedicated connection, see Gotchas
'store'      => 'session',
'lifetime'   => 120,
// config/database.php
'redis' => [
    'session' => [
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'port' => env('REDIS_PORT', 6379),
        'database' => 1,                 // isolate from cache/queues
        'prefix' => 'app:session:',
    ],
],

That's the whole "getting started." If you need bespoke behavior (custom key format, cross-app compatibility, locking), jump to ## Implementation Patterns and write a handler — do not pull in a dead Symfony bundle to get it.

Implementation Patterns

1. Default: native Laravel Redis sessions

Use this unless you have a specific reason not to. It routes through CacheBasedSessionHandler over the redis cache store, so lifetime becomes the Redis TTL. Set prefix and a separate database per app to keep sessions from colliding with cache/queue keys.

2. Porting the bundle's handler into Laravel (the "custom driver" path)

The bundle's real content is a session handler. In Laravel, that becomes a custom driver bound in a service provider:

// app/Providers/SessionServiceProvider.php
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Session;
use Illuminate\Session\CacheBasedSessionHandler;

class SessionServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Session::extend('redis-tenant', function ($app) {
            $minutes = $app['config']['session.lifetime'];
            return new CacheBasedSessionHandler(
                $app['cache']->store('session'), $minutes
            );
        });
    }
}

Only drop to a raw SessionHandlerInterface implementation when CacheBasedSessionHandler semantics don't fit — e.g. you need SETEX with a key layout another system expects, or you need SessionUpdateTimestampHandlerInterface::updateTimestamp() to avoid rewriting unchanged payloads (the bundle predates that interface and always wrote).

Minimal raw handler shape, mirroring what the bundle does:

class RedisSessionHandler implements \SessionHandlerInterface
{
    public function __construct(private \Redis $redis, private string $prefix, private int $ttl) {}

    public function open($path, $name): bool { return true; }
    public function close(): bool { return true; }

    public function read($id): string|false
    {
        return $this->redis->get($this->prefix . $id) ?: '';
    }

    public function write($id, $data): bool
    {
        return $this->redis->setex($this->prefix . $id, $this->ttl, $data);
    }

    public function destroy($id): bool
    {
        $this->redis->del($this->prefix . $id);
        return true;
    }

    public function gc($max_lifetime): int|false { return 0; } // TTL does the work
}

Note gc() returning 0 deliberately: Redis expiry is the GC, and Laravel's session GC lottery is wasted I/O against Redis.

3. Cross-app session sharing (the only genuinely interesting pattern here)

If a Symfony2 app uses this bundle and a Laravel app must read the same sessions, the blocker is not Redis — it's payload format. Symfony stores session.serialize_handler output (php, php_binary, php_serialize); Laravel stores its own serialized payload. Sharing requires matching the serializer, the exact key prefix, and the cookie name/domain. In practice: terminate sessions at an edge (nginx/Envoy) or move to a signed JWT/short-lived token instead. Full session-store sharing across two frameworks is a maintenance trap, and the bundle's 2016-era code gives you no help with it.

4. Reading the bundle as a migration checklist

When decommissioning the Symfony2 service, inventory first: KEYS/SCAN the prefix, count sessions, check TTL distribution, and confirm whether anything else reads those keys. Then drain by setting the app to write to a new prefix before shutting the handler off. The bundle's config exposes host/port/prefix — grab those values from the app's config.yml rather than guessing.

Gotchas and Tips

  • It cannot be installed into Laravel. No composer require path that works — it's a Symfony Bundle with a Bundle class, DI extension, and Symfony 2.x constraints. Composer may resolve it, but nothing will boot it. Archived + 0 dependents means no fixes incoming.
  • Laravel's Redis session driver has no locking. Concurrent requests (classic: several AJAX calls on page load) can interleave reads and writes and lose data. This is true of the native driver and of anything you port from this bundle. Mitigate by keeping mutable state out of the session, or by wrapping critical writes in Cache::lock().
  • allkeys-lru will silently eat sessions. If Redis is shared with cache and the eviction policy is allkeys-lru, sessions evict under memory pressure with no error — users just get logged out. Use a dedicated DB with volatile-lru or noeviction, and monitor evicted_keys.
  • Prefix collisions. database.redis.options.prefix applies globally; config/database.php connection-level prefix applies per connection. Pick one and be explicit — a doubled prefix (app:session:app:session:...) is the usual symptom.
  • TTL drift between session.lifetime and reality. Laravel refreshes the cache TTL on write. If a system reads but doesn't write, the key expires mid-session. Verify with TTL <key> after a read-only request.
  • Regenerate on login leaves the old key. session()->regenerate() writes a new ID; the old key lingers until TTL. Fine, but it inflates key counts and confuses audits — use migrate(true) semantics deliberately.
  • gc() is not your cleanup mechanism with Redis. Don't set session.gc_probability/lottery to non-zero "to be safe"; it adds latency for no benefit.
  • Debugging recipe:
    • redis-cli -n 1 --scan --pattern 'app:session:*' | head
    • redis-cli -n 1 TTL 'app:session:<id>' — sanity-check lifetime
    • redis-cli -n 1 OBJECT ENCODING <key> — confirm you're not accidentally storing huge hashes/ziplists
    • redis-cli -n 1 MONITOR in staging while reproducing a logout bug; look for DEL/EXPIRE
    • php artisan tinker → Cache::store('session')->get('...') to confirm the store wiring
  • Extension points worth knowing: Session::extend() (driver registration), Illuminate\Contracts\Session\SessionHandler, SessionUpdateTimestampHandlerInterface (skip no-op writes), and session.connection/session.store config (cheapest fix for most problems).
  • Don't use the bundle's classes as an API surface. With one star and a nine-year-old last release, any method signature you copy is your responsibility to maintain — copy the idea, write your own handler, keep it under 80 lines.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource