Add Lib\Session: native session wrapper with flash data

Lib\Session (novaconium/lib/Session.php) is a thin, all-static wrapper
around PHP's native session handling — get/set/has/remove plus
CodeIgniter-style flash data (flash()/getFlash()): a value set now is
readable on exactly the next request, then gone, for post/redirect/GET
flows like the contact form's hand-rolled ?sent=1 (not refactored here —
the original spec cites it as a motivating example, not a mandate).

Lazy-start, same shape as the already-shipped Lib\Csrf, which the two
classes can share a native session with in the same request without
conflict. Flash data is a single per-request swap (snapshot last
request's bucket, clear the stored one) rather than a sweep/expiry pass.

Verified end-to-end across three separate HTTP requests sharing a cookie
jar (not just in-process calls), confirming a flashed value survives
exactly one subsequent request.

Closes the "Session handling (with flash sessions)" backlog item in
novaconium/ISSUES.md.
This commit is contained in:
code
2026-07-14 06:43:11 +00:00
parent 3a59269eaa
commit 5deb298b91
7 changed files with 243 additions and 39 deletions
+27
View File
@@ -191,6 +191,33 @@ tables of its own yet — if a future framework feature needs a shipped
migration, extend this to the same App-over-novaconium two-root scan
`Overlay.php` already does for pages/lib, don't invent a second mechanism.
`Lib\Session` (`novaconium/lib/Session.php`) is a thin wrapper around
native PHP sessions (`session_start()`/`$_SESSION`, not a custom store),
all-static and lazy-start like `Lib\Csrf` — nothing calls `session_start()`
until the first real call to a `Session` method. Its `ensureSession()` is a
**deliberate duplicate** of `Csrf::ensureSession()` (same cookie params,
same `session_status()` guard) rather than a shared helper — keeps `Csrf`
standalone with zero new dependencies on a class that didn't exist when it
shipped, same tolerance for small duplication already established by the
config-load block duplicated across `bootstrap.php`/`bin/clear-cache.php`/
`Lib\Db::config()`. Both classes touching the same native session in the
same request is safe either way, since `session_start()` silently no-ops
if a session is already active — there's no ordering requirement between
`Csrf::token()`/`::verify()` and any `Session` method.
Flash data (`Session::flash()`/`::getFlash()`) is one swap, not a
sweep/expiry pass: the first `Session` method call in a request snapshots
whatever was flashed on the *previous* request into an in-memory static
(`self::$currentFlash`) for that request's `getFlash()` reads, then
immediately empties the stored flash bucket so `flash()` calls made
*during* the current request start filling a fresh bucket for the request
after this one. This relies on static properties not persisting across
requests (true under `php -S`, mod_php, and PHP-FPM alike — each request
gets fresh PHP state regardless of worker-process reuse) — don't add any
caching/memoization to `Session` that assumes static state survives
between requests, since none of it does. See `/admin/docs/session` for a
worked flash example.
## Running it
```