Verified against Semitexa Ultimate 2026.09.19.1020
Sessions and Cookies
This document describes how sessions and cookies work in Semitexa: lifecycle, storage, API, and integration with the request/response pipeline (including Swoole).
Overview
- Session: server-side session storage (Swoole Table), identified by a 32-character cookie. Data is read at request start and saved at request end.
- Cookies: read from the incoming request; new cookies are queued in a CookieJar and sent in the response as
Set-Cookieheaders. - Request-scoping: each request gets its own
Session,CookieJar, andRequest. Handlers receive the sameCookieJarinstance that the framework uses to send cookies, so any cookie set in a handler is included in the response. Why: so that cookies set in a handler are actually sent to the browser without mixing state between requests or losing them due to a different jar instance.
Session
Contract and implementation
- Interface:
Semitexa\Core\Session\SessionInterface - Implementation:
Semitexa\Core\Session\Session(in-memory bag + Swoole Table persistence)
Session methods:
| Method | Description |
|---|---|
get($key, $default) |
Read value |
set($key, $value) |
Write value |
has($key), remove($key), clear() |
Key presence / removal |
getId() |
Session id (32 hex chars) |
regenerate() |
New id after login (prevents fixation); applied on save() |
flash($key, $value) |
One-time message for the next request |
getFlash($key, $default) |
Read and consume flash message |
getPayload($payloadClass) |
Typed segment (see below) |
setPayload($payload) |
Persist typed segment |
save() |
Persist to storage (called by framework at end of request) |
Lifecycle
-
Request start (
Application::handleRequest):initSessionAndCookies($request)runs.- Session id is taken from cookie
SESSION_COOKIE_NAME(defaultsemitexa_session) if present and 32 chars; otherwise a new id is generated. Sessionis created withSwooleTableSessionHandler; data is read from Swoole Table.Session,CookieJar, andRequestare stored in the request-scoped container.
-
During request:
- Handlers (and other request-scoped services) receive
SessionInterfaceandCookieJarInterfacevia constructor. The container injects the same instances from the request-scoped cache (see Request-scoped Session and CookieJar).
- Handlers (and other request-scoped services) receive
-
Request end:
finalizeSessionAndCookies($request, $response)runs:session->save(), then the session cookie is added to the CookieJar, and allSet-Cookielines from the jar are attached to the response.- The Swoole server sends each
Set-Cookieline viarawcookie()before other headers.
Storage: Swoole Table
- Handler:
Semitexa\Core\Session\SwooleTableSessionHandler(read/write/destroy). - Table holder:
Semitexa\Core\Session\SwooleSessionTableHolder(staticgetTable()/setTable()).
The table must be created and set before workers start. In server.php:
$sessionTable = new Table(10000);
$sessionTable->column('data', Table::TYPE_STRING, 65535);
$sessionTable->column('expires_at', Table::TYPE_INT);
$sessionTable->create();
SwooleSessionTableHolder::setTable($sessionTable);
Session data is stored as JSON in the data column; expires_at is used for TTL. Expired rows are removed on read.
Typed session segments (getPayload / setPayload)
Use DTOs with #[SessionSegment('name')] for type-safe, key-isolated session data:
use Semitexa\Core\Session\Attribute\SessionSegment;
#[SessionSegment('user_prefs')]
final readonly class UserPrefsPayload
{
public function __construct(
public string $theme = 'light',
public string $locale = 'uk',
) {}
}
In a handler:
$prefs = $this->session->getPayload(UserPrefsPayload::class);
$prefs->theme = 'dark';
$this->session->setPayload($prefs);
The segment name (user_prefs) is the key under which the payload is stored; other keys in the session are unaffected.
Flash messages
$session->flash('message', 'Saved.')— will be available only on the next request.$session->getFlash('message')— returns the value and removes it.
Use for one-time notices (e.g. “Record saved”) after redirect.
Environment
| Variable | Default | Description |
|---|---|---|
SESSION_COOKIE_NAME |
semitexa_session |
Name of the session cookie |
SESSION_LIFETIME |
3600 |
Session lifetime in seconds (used for cookie Max-Age and storage TTL) |
Cookies
Reading cookies
- Request:
Requesthas acookiesarray (andgetCookie($key, $default)). It is filled byRequestFactory::fromSwoole():- Cookies are taken from
$swooleRequest->cookieand from theCookieheader (parsed if present). Parsed header entries are merged with Swoole’s cookies (Swoole values take precedence).
- Cookies are taken from
- CookieJar:
CookieJarInterface::get($name, $default)andhas($name)read from the incoming request (same source asRequest::cookies). The jar is built fromRequestininitSessionAndCookies.
Writing cookies
- CookieJar: use the same jar to queue cookies for the response:
set($name, $value, $options)— options:path,domain,maxAge,expires,secure,httpOnly,sameSite(lax/strict/none).remove($name, $path = '/', $domain = null)— sends the cookie with past expiry so the browser deletes it.
- The framework calls
getSetCookieLines()and adds them to the response asSet-Cookieheaders. The Swoole server sends each line viaparseSetCookieLineAndSend()→rawcookie()so multiple cookies are sent correctly.
Important: Handlers must receive the same CookieJar instance that the framework uses when building the response. That is guaranteed by request-scoped constructor overrides (see below).
CookieJar contract
- Interface:
Semitexa\Core\Cookie\CookieJarInterface - Implementation:
Semitexa\Core\Cookie\CookieJar
Methods: get, has, set, remove, getSetCookieLines().
Request-scoped Session and CookieJar
Session, CookieJar, and Request are per-request and must be the same instances for the whole pipeline:
- Application (
initSessionAndCookies): createsSessionandCookieJar($request), puts them (andRequest) intoRequestScopedContainerviaset(). When all three are set, the container’s RequestContext is updated so that mutable services receive them. - Handlers: handlers are mutable services. When the container is asked for a handler, it returns a clone of the handler prototype and then injects the current RequestContext (Request, SessionInterface, CookieJarInterface) into that clone’s matching protected properties. So the handler uses the same Session/CookieJar/Request that
finalizeSessionAndCookieslater uses — cookies set in the handler are included in the response. Handlers do not use constructor injection for these; they declare e.g.protected SessionInterface $session;(no attribute); the container fills them from RequestContext. - After each request:
RequestScopedContainer::reset()is called (e.g. inserver.phpfinally), clearing the cache so the next request gets new Session/CookieJar/Request.
See src/Container/README.md for the full DI and RequestContext behaviour.
Response path: from CookieJar to browser
-
Application
finalizeSessionAndCookies()adds the session cookie to the CookieJar, then calls$response->withHeaders(['Set-Cookie' => $cookieJar->getSetCookieLines()]). -
Response
Headers are stored as returned bygetHeaders()(key may beSet-Cookieorset-cookie; comparison is case-insensitive where needed). -
server.php
- Reads
Set-Cookiefrom the response headers (array of lines). - Sends each line with
parseSetCookieLineAndSend($response, $line), which parsesname=value; Path=...; Max-Age=...and calls Swoole’srawcookie()so every cookie is sent separately. - Then sends status and other headers (skipping
Set-Cookieto avoid duplication).
- Reads
So: CookieJar → getSetCookieLines() → response headers → server → rawcookie() per line → browser.
Summary: key files
| Area | File |
|---|---|
| Session interface | src/Session/SessionInterface.php |
| Session implementation | src/Session/Session.php |
| Session storage | src/Session/SwooleTableSessionHandler.php, src/Session/SwooleSessionTableHolder.php |
| Session segment attribute | src/Session/Attribute/SessionSegment.php |
| Cookie jar interface | src/Cookie/CookieJarInterface.php |
| Cookie jar implementation | src/Cookie/CookieJar.php |
| Init/finalize session and cookies | src/Application.php (initSessionAndCookies, finalizeSessionAndCookies) |
| RequestContext / request-scoped values | src/Container/RequestScopedContainer.php, src/Container/RequestContext.php |
| Request cookies (parsing) | src/RequestFactory.php (Cookie header + Swoole cookie merge) |
| Sending Set-Cookie in Swoole | server.php (parseSetCookieLineAndSend, request callback) |
| Session table setup | server.php (Table create + SwooleSessionTableHolder::setTable) |