Session Adapters
Flick abstracts session management behind a clean interface, enabling seamless framework integration while maintaining zero-config standalone usage. When you use Laravel or Symfony, their session managers can be wrapped and injected — but if you're using plain PHP, Flick "just works" with native sessions.
Quick Start
By default, Flick uses NativeSession which wraps PHP's native session functions. No configuration needed:
$form = new Flick\Flick('bootstrap'); // Flick automatically manages sessions for CSRF, multistep forms, etc. if ($form->submitted()) { $data = $form->request('Name, Email, Message'); }
To use a custom adapter (for framework integration or testing), pass it in the config:
use Flick\Session\ArraySession; $session = new ArraySession(['user_id' => 123]); $form = new Flick\Flick([ 'session' => $session, 'csrf' => false, ]);
Why Use Session Adapters?
Session adapters solve three common problems:
Framework Integration — Laravel, Symfony, and other frameworks manage their own sessions. Adapters let Flick work with these session managers instead of calling session_start() directly.
Testing — Unit tests shouldn't interact with PHP's native session system. With ArraySession, you can inject test data and verify session operations cleanly:
public function test_login_regenerates_session_id() { $session = new ArraySession(); $form = new Flick(['session' => $session, 'csrf' => false]); // ... perform login ... $form->session->regenerateId(true); expect($session->wasRegenerated())->toBeTrue(); }
Namespace Isolation — All Flick session data is stored under $_SESSION['flick'] to avoid conflicts with your application's session data.
Available Adapters
| Adapter | Purpose |
|---|---|
NativeSession |
Default — wraps PHP native sessions with secure defaults |
ArraySession |
Testing utility with in-memory storage and assertion helpers |
Using NativeSession (Default)
NativeSession is used automatically when no adapter is specified. It provides:
- Auto-start behavior — Sessions start automatically when needed
- Secure cookie defaults —
httponly,secure(on HTTPS), andsamesite=Strict - Namespace isolation — All data stored under
$_SESSION['flick']
use Flick\Session\NativeSession; $session = new NativeSession(); // Store and retrieve values $session->setValue('step', 1); $step = $session->getValue('step'); // 1 // Check for values if ($session->hasValue('step')) { $session->deleteValue('step'); }
You typically don't need to interact with NativeSession directly — Flick creates one internally.
Using ArraySession for Testing
ArraySession provides an in-memory session for testing, with helpers to verify session operations:
Constructor
use Flick\Session\ArraySession; // Empty session $session = new ArraySession(); // Pre-populated session $session = new ArraySession(['user_id' => 123, 'role' => 'admin']); // Inactive session (for testing edge cases) $session = new ArraySession([], false);
Testing Helpers
ArraySession provides six testing helpers:
| Method | Returns | Description |
|---|---|---|
wasRegenerated() |
bool |
Check if regenerateId() was called |
getRegenerateCount() |
int |
Count how many times regenerateId() was called |
wasDestroyed() |
bool |
Check if destroy() was called |
getAllValues() |
array |
Get all stored values for assertions |
setActive(bool) |
static |
Simulate inactive session state |
resetFlags() |
static |
Reset testing flags (regenerated, destroyed) |
Complete Test Example
Here's a complete test showing a login flow with session regeneration:
use Flick\Flick; use Flick\Http\ArrayRequest; use Flick\Session\ArraySession; test('login regenerates session ID to prevent fixation', function () { // Set up test fixtures $request = ArrayRequest::createPost([ '_id' => 'login', 'email' => 'user@example.com', 'password' => 'secret123', ]); $session = new ArraySession(); $form = new Flick([ 'id' => 'login', // must match the posted _id 'request' => $request, 'session' => $session, 'csrf' => false, ]); // Simulate login process $data = $form->request('Email[email, required], Password[required]'); if ($form->ok()) { // Verify credentials (mocked) $authenticated = true; if ($authenticated) { // Security: regenerate session ID after login $form->session->regenerateId(true); $form->session->setValue('user_id', 42); } } // Assert session was properly secured expect($session->wasRegenerated())->toBeTrue(); expect($session->getValue('user_id'))->toBe(42); });
Framework Integration
Laravel
Don't write this one yourself — install the integration package:
composer require flickphp/laravel
It is auto-discovered, and ships a LaravelSession adapter already installed as
the default, so multistep forms and CSRF tokens use Laravel's session with no
setup:
// In any controller $form = new Flick('tailwind'); // Uses Laravel's session, not $_SESSION $form->session->setValue('form_step', 2);
It also wires up Laravel's request, CSRF token and validator. See Flick for Laravel.
The rest of this page is for frameworks Flick has no package for.
Symfony
// src/EventSubscriber/FlickSubscriber.php namespace App\EventSubscriber; use Flick\Flick; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; class FlickSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [KernelEvents::REQUEST => 'onRequest']; } public function onRequest(RequestEvent $event): void { $request = $event->getRequest(); $session = $request->getSession(); Flick::setDefaultRequest(new SymfonyRequest($request)); Flick::setDefaultSession(new SymfonySession($session)); } }
SymfonyRequest and SymfonySession are adapters you write yourself — Flick
doesn't ship them. See Creating Custom Adapters below
for the interface to implement.
Disabling Auto-Start
sessionAutoStart only applies when Flick falls back to its own NativeSession —
when you provide a session adapter (via the session key or
setDefaultSession()), that adapter controls the session lifecycle and the flag
is ignored. Use it when you want native PHP sessions but your app calls
session_start() itself:
$form = new Flick([ 'sessionAutoStart' => false, // The app already started the native session ]);
Creating Custom Adapters
To integrate with any framework, implement SessionInterface:
use Flick\Session\SessionInterface; use Illuminate\Session\Store; class LaravelSession implements SessionInterface { public function __construct(private Store $session) {} public function isActive(): bool { return $this->session->isStarted(); } public function start(): void { if (!$this->session->isStarted()) { $this->session->start(); } } public function regenerateId(bool $deleteOldSession = false): void { $this->session->regenerate($deleteOldSession); } public function setValue(string $key, mixed $value): void { $this->session->put("flick.{$key}", $value); } public function getValue(string $key): mixed { return $this->session->get("flick.{$key}"); } public function hasValue(string $key): bool { // exists(), not has(): Laravel's has() reports a stored null as absent, // and hasValue() is a presence check return $this->session->exists("flick.{$key}"); } public function deleteValue(string $key): void { $this->session->forget("flick.{$key}"); } public function destroy(): void { $this->session->forget('flick'); } public function getAll(): array { return $this->session->get('flick', []); } }
Warning
getAll() is easy to miss — it returns everything Flick has stored, and multistep
forms rely on it. Leave it out and PHP fatals at class-definition time with
"contains 1 abstract method and must be declared abstract". The full interface is
isActive, start, regenerateId, setValue, getValue, hasValue,
deleteValue, destroy, getAll.
Session Resolution Order
Flick resolves which session adapter to use in this order:
- Explicit
sessionkey in config — Highest priority - Static default via
Flick::setDefaultSession()— For service providers - Falls back to
NativeSession— WithsessionAutoStartconsideration
// 1. Explicit adapter in config (highest priority) $form = new Flick(['session' => new ArraySession()]); // 2. Static default (set once in service provider) Flick::setDefaultSession(new LaravelSession(session())); $form = new Flick('bootstrap'); // Uses Laravel session // 3. Falls back to NativeSession Flick::resetDefaultSession(); $form = new Flick('bootstrap'); // Uses NativeSession
Security: Session Regeneration
Security
Always call regenerateId(true) after authentication state changes (login/logout) to prevent session fixation attacks:
// After successful login if ($authenticated) { $form->session->regenerateId(true); // true = delete old session data $form->session->setValue('user_id', $user->id); } // After logout $form->session->destroy(); $form->session->regenerateId(true);
Session fixation attacks occur when an attacker sets a victim's session ID before they authenticate. By regenerating the session ID after login, you ensure the attacker's known session ID becomes invalid.
SessionInterface Reference
All adapters implement these methods:
| Method | Returns | Description |
|---|---|---|
isActive() |
bool |
Check if session is active and ready |
start() |
void |
Start the session if not already started |
regenerateId(bool $delete) |
void |
Regenerate session ID (security) |
setValue(string $key, mixed $value) |
void |
Store a value in the Flick namespace |
getValue(string $key) |
mixed |
Retrieve a value (or null if not found) |
hasValue(string $key) |
bool |
Check if a value exists (presence, not truthiness — see below) |
deleteValue(string $key) |
void |
Remove a value |
destroy() |
void |
Destroy all Flick session data |
getAll() |
array |
Get every value Flick has stored (multistep forms rely on it) |
`hasValue()` checks presence, not truthiness
A stored '0', 0, '', false or null is still a stored value, and your
adapter must report it as present. Reach for a presence check
(array_key_exists(), Laravel's exists()), never empty() or Laravel's
has() — both of those call a stored value absent and break repopulating a
field whose value is '0'.
Static Methods
Flick provides static methods for managing the default session adapter:
Flick::setDefaultSession
Sets the default session adapter for all Flick instances.
use Flick\Flick; // In a service provider Flick::setDefaultSession(new LaravelSession(session())); // All subsequent Flick instances use this adapter $form = new Flick('bootstrap');
Flick::getDefaultSession
Returns the currently configured default session adapter, or null if not set.
$session = Flick::getDefaultSession();
Flick::resetDefaultSession
Resets the default session adapter to null (useful in tests).
beforeEach(function () { Flick::resetDefaultSession(); });
Tip
setDefaultSession() is ideal for service providers where you configure Flick once for your entire application.