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

Slim Session Laravel Package

bryanjhv/slim-session

A session middleware for Slim 4 built on PHP's native session handling, with flash messages, session segments, ID rotation, and configurable cookie/storage options. PSR-7/PSR-15 compatible and easy to integrate into Slim applications.

View on GitHub
Deep Wiki
Context7

Getting Started

What it is: a ~200-line Slim 4 middleware plus a static helper that wires PHP's native session machinery into Slim's PSR-7 request/response cycle. It is not a Laravel package — as a Laravel developer you'll meet it when you inherit a Slim 4 microservice, a Slim-based admin/tool app, or a small service sitting inside an otherwise-Laravel estate. The mental model maps almost 1:1 onto Laravel's session: Session::get/set/delete ≈ session(), regenerateId() ≈ $request->session()->regenerate().

Install:

composer require bryanjhv/slim-session

Register once, in the app bootstrap, before any middleware that needs the session:

use Slim\Factory\AppFactory;
use Slim\Middleware\Session;

$app = AppFactory::create();

$app->add(new Session([
    'name'        => 'app_session', // cookie name
    'autorefresh' => true,          // slide expiry on every request
    'lifetime'    => '2 hours',     // accepts strtotime-style strings
    'secure'      => true,          // HTTPS only — see Gotchas
    'httponly'    => true,
    'samesite'    => 'Lax',
]));

Ordering matters: Slim 4's $app->add() stack is LIFO, so the last middleware added executes first. Add the session middleware so it wraps the handlers that depend on it (auth, CSRF, flash), and keep it outside addRoutingMiddleware() if you want it available to other middleware too.

First use case — a login flag and a guarded route:

use Slim\Session;

$app->post('/login', function ($request, $response) {
    if (check_credentials($request->getParsedBody())) {
        Session::regenerateId();          // kill fixation before trusting the session
        Session::set('user_id', 42);
        return $response->withHeader('Location', '/dashboard')->withStatus(302);
    }
    Session::set('error', 'Bad credentials');
    return $response->withHeader('Location', '/login')->withStatus(302);
});

$app->get('/dashboard', function ($request, $response) {
    if (!Session::exists('user_id')) {
        return $response->withHeader('Location', '/login')->withStatus(302);
    }
    return $response;
});

Where to look first: the GitHub README (options table), then src/Middleware/Session.php for the options array and defaults, then src/Session.php for the helper surface (get, set, delete, exists, destroy, regenerateId). The whole thing is small enough to read end-to-end before you depend on it — do that, it takes ten minutes and answers every "does it support X?" question.

Implementation Patterns

Auth guard as middleware. Extract the Session::exists('user_id') check into a dedicated middleware and attach it per route group rather than repeating it:

$auth = function ($request, $handler) {
    if (!\Slim\Session::exists('user_id')) {
        return (new \Slim\Psr7\Response())
            ->withHeader('Location', '/login')->withStatus(302);
    }
    return $handler->handle($request);
};

$app->group('/admin', function ($group) { /* routes */ })->add($auth);

Flash messages. There is no Laravel-style flash() here. The standard idiom is store-then-consume:

Session::set('flash', ['error' => 'Saved failed']);
// next request, in a view middleware:
$flash = Session::get('flash', []);
Session::delete('flash');

Wrap that pair in a tiny helper class so every controller doesn't reimplement it.

CSRF. Generate a token on GET into Session::set('csrf', bin2hex(random_bytes(32))), compare on POST with hash_equals, and regenerate on login. Pair it with your routing/error middleware rather than sprinkling checks in handlers.

Custom storage for anything multi-node. The default file handler serializes per-session on disk and will quietly break behind multiple app servers or containers. The middleware accepts a handler option taking any SessionHandlerInterface — pass a Redis or database-backed handler and you get shared sessions plus proper locking behaviour for free:

$app->add(new Session([
    'handler'  => new RedisSessionHandler($redis),
    'lifetime' => '8 hours',
]));

Wrap the static helper. Because Slim\Session is a static facade over global state, it's awkward to inject and awkward to test. Define your own SessionService that delegates to it, register it in the container, and type-hint that in handlers. You get constructor injection, easy fakes in tests, and a single place to swap implementations later.

Don't session-ify stateless APIs. Apply the middleware only to browser-facing route groups. Slapping it on a JSON API means Set-Cookie on every response, which ruins CDN caching and confuses clients.

View integration. For Twig/Blade-style templates, add a small middleware that computes user, flash, and csrf_token from the session once and stores them as request attributes or view globals — otherwise templates reach into the static helper, which is untestable.

Testing. Build a ServerRequestFactory request, run the app, and assert on the Set-Cookie header. In setUp() call Session::destroy() and reset any static instance state, or session data leaks between test cases.

Gotchas and Tips

  • secure => true on http://localhost is the #1 "sessions never persist" bug. The browser silently drops the cookie. Toggle it via environment ('secure' => $config->isHttps()) rather than hardcoding.
  • session_start() and output. If anything echoes before Slim writes headers, PHP emits "headers already sent" and the session cookie never lands. Enable output buffering in dev to surface this early.
  • samesite => 'Lax' breaks cross-site POSTs — payment provider callbacks, OAuth redirects, embedded iframes. Use None + Secure for those flows, and be aware older browsers ignore None without Secure.
  • Native file sessions lock per session. Two concurrent requests from the same browser serialize; a slow endpoint blocks the whole UI. Symptom looks like "the app randomly hangs." Move to a Redis handler, or close the session early (session_write_close()) once you've finished reading.
  • lifetime is not gc_maxlifetime. The package controls the cookie's lifetime; PHP's garbage collector uses session.gc_maxlifetime from php.ini. On shared hosting they diverge and users get logged out early. Align them, and note that autorefresh => true re-sends Set-Cookie on every response — fine for apps, hostile to caching layers.
  • Namespace collision. The package ships classes under Slim\ (Slim\Middleware\Session, Slim\Session), the same root as Slim core. IDE auto-import will grab the wrong Session class regularly, and any of your own Slim\* classes risk colliding. Import explicitly and review imports in PRs.
  • Static helper fails silently if the middleware isn't registered. No exception, just a session that isn't started and values that vanish. Guard with a boot-time assertion, and always register the middleware in the same place.
  • Cookie scoping. Sharing a session across app.example.com and api.example.com requires 'domain' => '.example.com'. Changing name invalidates every existing cookie — everyone gets logged out; treat it as a breaking config change.
  • Debugging checklist: inspect Set-Cookie in the response, then Session::getId(), then session_save_path() and directory permissions (/var/lib/php/sessions is a classic permission trap in Docker images).
  • Maintenance risk. 231 stars, one maintainer, effectively no dependents. Pin the version in composer.json, read the source before upgrading, and keep the wrapper service described above so replacing it with odan/session or ~100 lines of your own middleware is a contained change rather than a rewrite.
  • Don't mix paradigms. If you're in a Laravel app, use Laravel's session — installing this there is a category error. And never let legacy code call session_start() while this middleware is active; you'll get two competing session lifecycles and very confusing data loss.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle