Session Management
Systém pro správu PHP sessions s vlastní konfigurací, bezpečností a logika pro expiraci.
Přehled
Petrovo CMS používá vlastní Session wrapper kolem PHP sessions. Poskytuje:
- Jednoduchou API –
Session::get(),Session::set(),Session::delete() - Automatický start – Session se spouští on-demand
- Custom session ID – Bezpečnější ID generování
- Periodic cleanup – Automatické čištění zastaralých session souborů
- Vnořené hodnoty – Přístup k nested hodnotám přes dvoutečku
user:name
Konfigurace – session.yaml
Konfigurační soubor /config/session.yaml definuje chování PHP sessions:
# Backend pro uložení sessions
save_handler: 'files'
save_path: '/var/sessions'
# Garbage collection – jak často se spustí čištění
gc_probability: 1
gc_maxlifetime: 1800 # 30 minut – maximální lifetime session
# Cookie nastavení
name: 'PHPSESSID'
cookie_httponly: true # Zákaz přístupu z JavaScript (XSS ochrana)
use_only_cookies: true # Session ID jen v cookies, ne v URL
use_strict_mode: true # Validace Session ID
use_trans_sid: false # Žádné transparent SID
cookie_secure: true # Jen HTTPS cookies
cookie_samesite: 'Strict' # CSRF ochrana (problém s card payment přesměrováním)
Bezpečnostní nastavení:
cookie_httponly: true– JavaScript nemůže číst session cookie (XSS ochrana)use_only_cookies: true– Session ID jen v cookies, ne v URLuse_strict_mode: true– Nové SID, když se přijme neznámé IDcookie_secure: true– Jen HTTPS (v produkci)cookie_samesite: 'Strict'– CSRF ochrana, ale problém s card payment přesměrováním (Proto disabled!)
Session Třída
Inicializace
use Petrovo\Session\Session;
// Session se automaticky spouští on-demand
// Nebo explicitně:
Session::start();
Co start() dělá:
- Kontroluje, zda session již běží
- Načte konfiguraci z
session.yaml - Validuje cestu pro uložení session souborů (existence, writability)
- Spouští session
- Provede periodic cleanup zastaralých session souborů
Metody
Session::get(string $key): mixed
Získá hodnotu z session.
use Petrovo\Session\Session;
// Jednoduché hodnoty
$userId = Session::get('user_id');
// Colon-notation pro nested values
$userName = Session::get('user:name');
$email = Session::get('user:profile:email');
// Vrátí null, pokud klíč neexistuje
$value = Session::get('non_existent'); // null
Session::set(string $key, mixed $value): mixed
Nastaví hodnotu v session. Klíč je string s podporou colon-notation pro nested hodnoty.
// Jednoduché hodnoty
Session::set('user_id', 123);
Session::set('cart_items', 5);
// Nested hodnoty (vytvoří strukturu) - colon-notation
Session::set('user:name', 'John');
Session::set('user:profile:email', 'john@example.com');
// Vrátí nastavenou hodnotu
$result = Session::set('key', 'value'); // 'value'
Session::delete(string $key): void
Smaže hodnotu z session. Klíč je string s podporou colon-notation pro nested hodnoty.
// Smazat jednoduchou hodnotu
Session::delete('user_id');
// Smazat nested hodnotu
Session::delete('user:profile'); // Smaže celý profil
Session::getAll(): array
Vrátí všechny session hodnoty.
$allData = Session::getAll();
// Vrátí: $_SESSION
var_dump($allData);
// Array (
// [user_id] => 123,
// [user] => Array([name] => 'John', ...),
// ...
// )
Session::regenerate(): void
Vygeneruje nový Session ID, zachovává existující data. Používá se po úspěšném loginu pro prevenci session fixation útoků.
// Po přihlášení
Session::set('user_id', $userId);
Session::regenerate(); // Nový Session ID, data zůstávají
Session::destroy(): void
Zcela zničí aktuální session včetně cookies. Smaže všechna data a cookie.
// Logout
Session::destroy();
// Session je zcela smazána, user musí se znovu přihlásit
Session::getOptions(): array
Vrátí konfigurační možnosti z session.yaml.
$options = Session::getOptions();
$lifetime = $options['gc_maxlifetime']; // 1800 sekund = 30 minut
$savePath = $options['save_path'];
Příklady
Login workflow:
// Login
Session::set('user_id', $user['id']);
Session::set('user:name', $user['name']);
Session::set('user:email', $user['email']);
Session::set('admin', true);
// Použití v šablonách
$userId = Session::get('user_id');
if (Session::get('admin')) {
echo 'Přihlášený admin';
}
// Logout
Session::delete('user_id');
Session::delete('user');
Session::delete('admin');
Nákupní košík:
// Přidání do košíku
$cart = Session::get('cart') ?? [];
$cart[] = ['product_id' => 42, 'qty' => 1];
Session::set('cart', $cart);
// Počet položek v košíku
$itemCount = count(Session::get('cart') ?? []);
// Vyprázdnění košíku
Session::delete('cart');
Flash data (jednorazové zprávy):
// Nastavit flash zprávu
Session::set('flash:message', 'Změny byly uloženy!');
Session::set('flash:type', 'success');
// V šablonách
if ($message = Session::get('flash:message')) {
echo $message;
Session::delete('flash:message');
}
Middleware – Admin Session Expiration
Middleware sessionExpirationAdminMiddleware kontroluje expiraci admin session.
Umístění: /app/Middlewares/Session/sessionExpirationAdminMiddleware.php
Jak funguje
- Čte timeout z
config/session.yaml(gc_maxlifetime) - Kontroluje last activity – čas poslední aktivity v session
- Pokud vypršel timeout → vyprázdní session a přesměruje na login
- Aktualizuje last_activity – zaznamenání nové aktivity
$lifeTime = Session::getOptions()['gc_maxlifetime']; // 1800 sekund = 30 minut
// Kontrola expiraci
$lastActivity = Session::get('last_activity');
if (isset($lastActivity) && (time() - $lastActivity) > $lifeTime) {
// Session vypršela – logout
Session::destroy();
redirect(loginUrl);
}
// Aktualizace aktivity
Session::set('last_activity', time());
Praktické scénáře
Scénář 1: Admin pracuje aktivně
10:00 - Admin se přihlásí
last_activity = 10:00
10:15 - Admin kliká na stránky
Middleware kontroluje: 10:15 - 10:00 = 15 minut < 30 minut (OK)
last_activity = 10:15
10:30 - Admin kliká
Middleware kontroluje: 10:30 - 10:15 = 15 minut < 30 minut (OK)
last_activity = 10:30
10:45 - Admin se neaktivní
(žádné klikání)
Scénář 2: Admin se nechal sedět
10:00 - Admin se přihlásí
last_activity = 10:00
10:40 - Admin se vrátí a kliká
Middleware kontroluje: 10:40 - 10:00 = 40 minut > 30 minut (VYPRŠELO)
Session VYPRŠELA → session_unset() → session_destroy()
Přesměrování na login
Integraci do middleware pipeline
Middleware se načítá v /app/bootstrap.php pro admin pipeline:
// app/bootstrap.php
$app->pipe('/ADMIN', sessionExpirationAdminMiddleware);
// Ostatní admin middleware...
Spouští se na každý admin request PŘED kontrolerům.
Garbage Collection Logika
Session soubory se akumulují na disku. Session::start() provádí periodic garbage collection:
private static function updateGcMarker(string $path, int $lifetime): void
{
$lifetime += 120; // +2 minuty buffer
$lastCleanFile = $path . '/last_clean';
if (!file_exists($lastCleanFile)) {
touch($lastCleanFile);
}
$lastCleanTimestamp = filemtime($lastCleanFile);
if (time() - $lastCleanTimestamp > $lifetime) {
touch($lastCleanFile); // Aktualizace timesteampu
}
}
Jak to funguje:
- Sleduje soubor
/var/sessions/last_clean(GC marker) - Pokud od poslední kontroly uplynulo víc než
gc_maxlifetime: touch()se aktualizuje timestamp souboru- Signalizuje PHP, že je čas pro garbage collection
- PHP smaže session soubory starší než
gc_maxlifetime
Proč to takhle?
- Na každém requestu kontrolujeme jenom timestamp souboru (levné)
- Garbage collection se spouští jen periodicky (drahé)
- Není potřeba cron – PHP to řeší automaticky
Příklad:
gc_maxlifetime: 1800 # 30 minut
last_clean: 10:00
10:05 - Session start → check: 10:05 - 10:00 = 5 min < 30 min → žádný GC
10:35 - Session start → check: 10:35 - 10:00 = 35 min > 30 min → touch() → GC spuštěn!
Bezpečnostní Best Practices
Správně
// Bezpečné – session je vždy HTTP-only, HTTPS-only
Session::set('user_id', $userId);
Session::set('token', hash('sha256', $secret));
// Bezpečné – logout smaže všechna data
Session::delete('user_id');
session_destroy();
// Bezpečné – regenerace Session ID po loginu
session_regenerate_id(true);
Session::set('user_id', $userId);
Nebezpečně
// Neukládat hesla v session
Session::set('password', $plainPassword);
// Neukládat citlivé tokeny bez hashu
Session::set('api_key', $rawApiKey);
// Neměnit session ID po loginu – Session fixation útok!
Časté Problémy
Session data zmizejí
Příčina: Session timeout v gc_maxlifetime
// Kontrola v middleware
$lifeTime = 1800; // 30 minut
if (time() - $lastActivity > $lifeTime) {
// Data byla smazána!
}
Řešení: Zvýšit timeout nebo refresh stránky
Není možné zapisovat session soubory
Příčina: /var/sessions není writable
# Kontrola
ls -la var/sessions
# Oprava
chmod 755 var/sessions
Session cookie se neposílá v API
Příčina: cookie_secure: true vyžaduje HTTPS
// V .env dev prostředí
APP_ENV=dev
// A nebo vypnout cookie_secure pro dev
# config/session.yaml
cookie_secure: false # Dev only!
Konfigurace pro vývoj vs produkci
Development (.env dev)
# config/session.yaml (dev)
gc_maxlifetime: 7200 # 2 hodiny
cookie_secure: false # Bez HTTPS
cookie_samesite: 'Lax' # Tolerantní
Production (.env prod)
# config/session.yaml (prod)
gc_maxlifetime: 1800 # 30 minut (krátko!)
cookie_secure: true # Jen HTTPS
cookie_samesite: 'Strict' # Striktní
API Reference
| Metoda | Parametry | Vrací | Popis |
|---|---|---|---|
start() |
- | bool |
Spustí session, vrátí true pokud nová |
get(key) |
string |
mixed |
Získá hodnotu (colon-notation) |
getAll() |
- | array |
Vrátí všechny session hodnoty |
set(key, value) |
string, mixed |
mixed |
Nastaví hodnotu, vrátí ji |
delete(key) |
string |
void |
Smaže hodnotu |
regenerate() |
- | void |
Nový Session ID (po login) |
destroy() |
- | void |
Zcela zničí session (logout) |
getOptions() |
- | array |
Vrátí session.yaml config |