PHPackages                             enlivenapp/flight-sessions - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. enlivenapp/flight-sessions

ActiveFlightphp-plugin

enlivenapp/flight-sessions
==========================

Database-backed sessions for FlightPHP — encrypted payloads, unified API, AJAX-friendly

0.1.1(today)06↑2900%MITPHPPHP &gt;=8.1

Since Aug 24Pushed todayCompare

[ Source](https://github.com/enlivenapp/flight-sessions)[ Packagist](https://packagist.org/packages/enlivenapp/flight-sessions)[ RSS](/packages/enlivenapp-flight-sessions/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (2)Versions (3)Used By (0)

enlivenapp/flight-sessions
==========================

[](#enlivenappflight-sessions)

Database-backed session storage for FlightPHP. Sessions live in a SQL table through PHP's `SessionHandlerInterface`, payloads are encrypted at rest with AES-256-GCM, and a single `SessionManager` service handles cookie hardening, id regeneration, flash messages, and garbage collection for the whole request.

Features
--------

[](#features)

- **Database storage** - one row per session, stamped on every write with `user_id`, IP address, user agent, and `last_activity`
- **Encryption at rest** - AES-256-GCM (`enc1:` prefix, random IV per write, verified auth tag); mandatory on web requests
- **No request locking** - concurrent requests from the same client are not serialized (AJAX/HTMX friendly)
- **Hardened cookies** - HttpOnly, SameSite=Lax, strict mode; `Secure` follows `flight.force_https` or is set explicitly
- **Flash messages** - two-generation scheme: `flash()`, `pullFlash()`, `hasFlash()`, `keepFlash()`
- **Per-user session tools** - active-session listings and remote logout via the handler API
- **Garbage collection** - probabilistic per request plus a deterministic `sessions:gc` CLI command

Requirements
------------

[](#requirements)

- PHP 8.1+
- `flightphp/core` ^3.0
- MySQL or MariaDB (writes use MySQL upsert syntax)
- A PDO connection available as `$app->db()`

Install
-------

[](#install)

```
composer require enlivenapp/flight-sessions
php vendor/bin/runway migrate:all   # creates the sessions table
```

Generate an encryption key and add it to `.env`:

```
php -r 'echo bin2hex(random_bytes(32));'
```

```
SESSION_ENCRYPTION_KEY=

```

Register the plugin in `app/config/config.php`:

```
'plugins' => [
    'enlivenapp/flight-sessions' => [
        'enabled'        => true,
        'priority'       => 2,
        'encryption_key' => '', // SESSION_ENCRYPTION_KEY in .env takes precedence
    ],
],
```

On web requests a missing key stops the application with HTTP 500 and a setup screen naming the env var. CLI commands run without a key. Rotating the key invalidates existing sessions: rows that no longer decrypt are deleted and their sessions restart empty.

How it works
------------

[](#how-it-works)

`Plugin::register()` binds `SessionManager` as `$app->session()` and starts it eagerly on every web request, so the hardened cookie parameters and the database save handler are in place before any consumer resolves. The handler implements PHP's `SessionHandlerInterface`, so `$_SESSION`, `session_regenerate_id()`, and all native session mechanics operate over SQL exactly as they would over files.

Each `write()` stores the encrypted payload alongside the session id, bound user id, remote IP, user agent, and current timestamp. When `read()` cannot decrypt a payload (rotated key, tampered row) it deletes that row and returns an empty session instead of failing the request.

Only one save handler exists per process. A second `SessionManager` instance adopts the already-registered handler rather than replacing it; the first `start()` owns storage for the process lifetime.

Configuration
-------------

[](#configuration)

Defaults from `src/Config/Config.php`; override inside the plugin entry:

KeyDefaultDescription`table``'sessions'`Session table name`cookie_name``'flight_session'`Session cookie name`cookie_lifetime``0`0 = browser-session cookie`cookie_path``'/'`Cookie path`cookie_domain``''`Cookie domain`cookie_secure``null``null` follows `flight.force_https`; `true`/`false` overrides`cookie_httponly``true`HttpOnly flag`cookie_samesite``'Lax'`SameSite policy`use_strict_mode``true`Reject uninitialized session ids`maxlifetime``7200`Idle timeout in seconds; drives GC deletes`gc_probability` / `gc_divisor``1` / `100`Roughly 1% chance of GC per request`encryption_key``''`64 hex chars; `.env` value takes precedenceUsage
-----

[](#usage)

```
$session = $app->session();          // service bound by the plugin

$session->set('key', $value);
$value = $session->get('key', $fallback);
$session->has('key');
$session->delete('key');
$value = $session->pull('once');     // read + remove
$all   = $session->all();            // everything except flash keys
$session->clear();                   // wipe data, keep session alive

$session->regenerate();              // new id, data preserved - call on login
$session->destroy();                 // clear data, delete row, expire cookie

$session->id();                      // current session id or null
$session->isActive();
```

### Flash messages

[](#flash-messages)

```
$session->flash('status', 'Saved');      // readable on the next request
$status = $session->pullFlash('status'); // read once, then removed
$session->keepFlash();                   // carry current flash into next request
```

Writes go to the *next* generation and reads come from the *current* one; generations rotate once per `start()`.

### Per-user sessions

[](#per-user-sessions)

Bind the owning user so rows can be listed or revoked later:

```
$session->setUserContext($userId);   // stamped onto every subsequent write
```

The handler exposes the query side:

```
use Enlivenapp\FlightSessions\Handlers\DatabaseHandler;

$handler = new DatabaseHandler(\Flight::db(), 'sessions', $hexKey);

$rows  = $handler->findByUser($userId);     // active sessions, newest first
$count = $handler->destroyByUser($userId);  // remote logout: deletes every row
```

Each row contains: `session_id`, `ip_address`, `user_agent`, `last_activity`. String fields arrive HTML-escaped (`htmlspecialchars`, `ENT_QUOTES`, UTF-8), so they are safe to render directly; decode explicitly if you need raw values downstream.

### Garbage collection

[](#garbage-collection)

GC runs probabilistically (~1% of requests) and deletes rows idle longer than `maxlifetime`. For deterministic cleanup, schedule the CLI command:

```
php vendor/bin/runway sessions:gc
```

Example cron entry:

```
*/15 * * * * cd /path/to/app && php vendor/bin/runway sessions:gc

```

Plugin ordering
---------------

[](#plugin-ordering)

Load this plugin before anything that touches sessions. With the common enlivenapp plugin set, use priorities: sessions `2`, flight-shield `5`, flight-csrf `10`. CSRF middleware must call its `before()` after plugins load so tokens persist through the same store.

Security notes
--------------

[](#security-notes)

- Payloads are encrypted with AES-256-GCM; each payload carries its own random IV and auth tag, verified on read
- Decryption failure deletes the row and returns an empty session - tampered data never reaches application code
- Session validity is possession of a valid session id plus a decryptable payload; IP address and user agent are recorded for audit and listing only, and are never matched against incoming requests (binding them causes false logouts on rotating IPs, shared NAT, and browser updates)
- `findByUser()` HTML-escapes all string fields at the boundary, so request-controlled values such as user agent cannot inject markup into admin screens that render them
- Cookies are HttpOnly with SameSite=Lax under strict mode; `Secure` follows `flight.force_https`
- Call `regenerate()` on privilege changes (login) to prevent fixation; `destroy()` removes the row and expires the cookie

---

License
-------

[](#license)

MIT - see [LICENSE](LICENSE).

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance100

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity33

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/3036663?v=4)[Mike W](/maintainers/enlivenapp)[@enlivenapp](https://github.com/enlivenapp)

---

Top Contributors

[![enlivenapp](https://avatars.githubusercontent.com/u/3036663?v=4)](https://github.com/enlivenapp "enlivenapp (2 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/enlivenapp-flight-sessions/health.svg)

```
[![Health](https://phpackages.com/badges/enlivenapp-flight-sessions/health.svg)](https://phpackages.com/packages/enlivenapp-flight-sessions)
```

###  Alternatives

[flightphp/skeleton

A Flight PHP framework skeleton app to get your new projects up and running ASAP

663.0k](/packages/flightphp-skeleton)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
