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:
@@ -14,6 +14,7 @@
|
||||
<li><a class="icon-link" href="/admin/docs/libraries">{{ icons.book() }}Libraries</a></li>
|
||||
<li><a class="icon-link" href="/admin/docs/config">{{ icons.book() }}Configuration</a></li>
|
||||
<li><a class="icon-link" href="/admin/docs/database">{{ icons.book() }}Database</a></li>
|
||||
<li><a class="icon-link" href="/admin/docs/session">{{ icons.book() }}Session</a></li>
|
||||
<li><a class="icon-link" href="/admin/docs/admin-auth">{{ icons.lock() }}Admin authentication</a></li>
|
||||
<li><a class="icon-link" href="/admin/docs/layouts">{{ icons.book() }}Layouts</a></li>
|
||||
<li><a class="icon-link" href="/admin/docs/caching">{{ icons.book() }}Static caching</a></li>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
{% block title %}Docs{% endblock %}
|
||||
|
||||
{% block description %}Framework documentation: routing, sidecars, forms, libraries, database, layouts, caching, styling.{% endblock %}
|
||||
{% block description %}Framework documentation: routing, sidecars, forms, libraries, database, session, layouts, caching, styling.{% endblock %}
|
||||
|
||||
{% block robots %}noindex, nofollow{% endblock %}
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
<li><a class="icon-link" href="/admin/docs/forms">{{ icons.email() }}Forms</a> — building a custom form with a sidecar, using the novaconium validation/spam libraries.</li>
|
||||
<li><a class="icon-link" href="/admin/docs/libraries">{{ icons.book() }}Libraries</a> — plain PHP classes under <code>Lib\</code>.</li>
|
||||
<li><a class="icon-link" href="/admin/docs/config">{{ icons.book() }}Configuration</a> — override framework settings from <code>App/config.php</code>.</li>
|
||||
<li><a class="icon-link" href="/admin/docs/database">{{ icons.book() }}Database</a> — <code>Lib\Db</code>, a thin PDO/SQLite wrapper with a plain-SQL migration convention.</li>
|
||||
<li><a class="icon-link" href="/admin/docs/database">{{ icons.book() }}Database</a> — <code>Lib\Db</code>, a thin PDO wrapper (SQLite and MySQL) with named, simultaneous connections and a plain-SQL migration convention.</li>
|
||||
<li><a class="icon-link" href="/admin/docs/session">{{ icons.book() }}Session</a> — <code>Lib\Session</code>, a thin wrapper around native PHP sessions, with CodeIgniter-style flash data.</li>
|
||||
<li><a class="icon-link" href="/admin/docs/admin-auth">{{ icons.lock() }}Admin authentication</a> — gate <code>/admin/*</code> behind HTTP Basic Auth, reusable for any future admin page.</li>
|
||||
<li><a class="icon-link" href="/admin/docs/layouts">{{ icons.book() }}Layouts</a> — pages and layouts are overridable, just like <code>Lib\</code>.</li>
|
||||
<li><a class="icon-link" href="/admin/docs/caching">{{ icons.book() }}Static caching</a> — how sidecar-less pages get served as static HTML.</li>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{% extends 'admin/docs/_layout/layout.twig' %}
|
||||
|
||||
{% import '_layout/icons.twig' as icons %}
|
||||
|
||||
{% block title %}Session{% endblock %}
|
||||
|
||||
{% block description %}Lib\Session — a thin wrapper around native PHP sessions, with CodeIgniter-style flash data.{% endblock %}
|
||||
|
||||
{% block robots %}noindex, nofollow{% endblock %}
|
||||
|
||||
{% block docs_content %}
|
||||
<h1>Session</h1>
|
||||
|
||||
<p><code>Lib\Session</code> (<code>novaconium/lib/Session.php</code>) is a thin wrapper around PHP's native session handling — plain <code>session_start()</code>/<code>$_SESSION</code>, not a custom session store — so sidecars have a consistent get/set API instead of touching <code>$_SESSION</code> directly. It's a <a class="icon-link" href="/admin/docs/libraries">{{ icons.book() }}Lib\</a> class like <code>Input</code>/<code>Csrf</code>/<code>Mailer</code>, so a project can override it entirely by dropping its own <code>App/lib/Session.php</code>.</p>
|
||||
|
||||
<h2>Using it</h2>
|
||||
|
||||
<pre><code>use Lib\Session;
|
||||
|
||||
Session::set('user_id', 42);
|
||||
$userId = Session::get('user_id'); // 42
|
||||
$loggedIn = Session::has('user_id'); // true
|
||||
Session::remove('user_id');</code></pre>
|
||||
|
||||
<p>The session is started lazily — nothing calls <code>session_start()</code> until the first real call to any <code>Session</code> method, so a page that never touches <code>Session</code> never gets a session cookie. <a class="icon-link" href="/admin/docs/libraries">{{ icons.book() }}Lib\Csrf</a> uses the exact same lazy-start mechanism to run its own session-token CSRF protection — both classes can touch the same native session in the same request without conflict, since <code>session_start()</code> is only ever actually called once (PHP no-ops a second call).</p>
|
||||
|
||||
<h2>Flash data</h2>
|
||||
|
||||
<p>A flashed value is readable on exactly the next request, then gone — useful for post/redirect/GET flows (a "message sent" banner after a redirect) without a query-string flag like <code>?sent=1</code>:</p>
|
||||
|
||||
<pre><code>use Lib\Session;
|
||||
use App\Response;
|
||||
|
||||
// In the sidecar handling the POST:
|
||||
Session::flash('message', 'Sent! We\'ll be in touch soon.');
|
||||
return Response::redirect('/contact');
|
||||
|
||||
// In the sidecar handling the following GET (the redirect target):
|
||||
return [
|
||||
'flashMessage' => Session::getFlash('message'),
|
||||
];</code></pre>
|
||||
|
||||
<p><code>Session::getFlash($key, $default = null)</code> returns the value on the request immediately after <code>flash()</code> was called, and the default on every request after that — regardless of whether <code>getFlash()</code> was actually called on that one request in between. A value flashed during the current request is never visible to <code>getFlash()</code> during that same request; it becomes visible on the next one.</p>
|
||||
|
||||
<p>Mechanically, this is a single swap rather than a separate expiry/sweep step: the first time any <code>Session</code> method runs in a request, it snapshots whatever was flashed on the previous request into an in-memory value for that request's <code>getFlash()</code> calls, then immediately clears the stored flash bucket so <code>flash()</code> calls made during the current request start filling a fresh bucket — the one the next request will snapshot in turn.</p>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user