Chiama session_start() in cima a una pagina, poi leggi e scrivi $_SESSION['key'] come qualsiasi array: PHP lo salva sul server e lo ritrova alla richiesta successiva del visitatore. Per un piccolo valore che il browser deve conservare, chiama setcookie('name', 'value', time() + 86400) e rileggilo nelle richieste successive da $_COOKIE['name'].
<?php
session_start(); // before any output
setcookie('theme', 'dark', time() + 60 * 60 * 24 * 30); // 30 days, also before output
$_SESSION['visits'] = ($_SESSION['visits'] ?? 0) + 1;
$theme = $_COOKIE['theme'] ?? 'light'; // set on an earlier visit
echo "You have opened this page {$_SESSION['visits']} times.";
Sessioni e cookie hanno senso solo su più richieste HTTP di un browser, quindi gli esempi che ne hanno bisogno sono codice semplice. La logica intorno a essi è normale PHP su array, e quella parte viene eseguita qui: in ogni blocco eseguibile, $session fa le veci di $_SESSION, e ogni chiamata è una richiesta.
Come funziona una sessione PHP
- Il primo
session_start()crea un id casuale, lo invia al browser in un cookie chiamatoPHPSESSIDe ti dà un$_SESSIONvuoto. - Alla fine della richiesta PHP salva
$_SESSIONsul server (di default in file, oppure in Redis o in un database). - Alla richiesta successiva il browser rimanda il cookie,
session_start()legge l'id e carica l'array salvato.
Il browser non vede mai i dati, solo l'id. Ecco perché l'id dell'utente autenticato va in $_SESSION e mai in un tuo cookie: un visitatore può modificare i propri cookie con qualsiasi valore, ma non può modificare $_SESSION.
Login e logout di un utente con una sessione
Fare il login significa: verificare la password, rigenerare l'id di sessione, salvare l'id utente. Il logout è il contrario, e ha tre passaggi.
<?php
// login.php
session_start();
$user = find_user_by_email($_POST['email'] ?? '');
if ($user && password_verify($_POST['password'] ?? '', $user['password_hash'])) {
session_regenerate_id(true); // new id after login: stops session fixation
$_SESSION['user_id'] = $user['id'];
header('Location: /dashboard.php');
exit;
}
$error = 'Wrong email or password.';
<?php
// any protected page
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: /login.php');
exit;
}
<?php
// logout.php
session_start();
$_SESSION = []; // 1. forget the data
$p = session_get_cookie_params(); // 2. delete the cookie
setcookie(session_name(), '', time() - 3600, $p['path'], $p['domain'], $p['secure'], $p['httponly']);
session_destroy(); // 3. delete the stored session
header('Location: /');
exit;
session_regenerate_id(true) conta più di quanto sembri. Se un attaccante riesce a far usare a una vittima un id di sessione che conosce (per esempio tramite un link), e l'id resta lo stesso dopo il login, anche l'attaccante risulta autenticato. Un id nuovo al login chiude quella falla. La verifica della password è trattata nella pagina su password_hash.
Far scadere una sessione dopo un periodo di inattività
La pulizia propria di PHP (session.gc_maxlifetime, 1440 secondi di default) avviene in modo casuale e non è un timeout affidabile. Salva l'ora dell'ultima richiesta e verificala tu. Il blocco usa timestamp fissi così puoi vedere ogni caso; cambia $timeout o gli orari.
L'intervallo dalle 09:05 alle 09:16:40 è di 700 secondi, sotto il limite di 15 minuti, quindi l'utente resta autenticato. L'intervallo successivo è di 1000 secondi, quindi la sessione viene azzerata.
Messaggi flash
Un messaggio flash viene impostato in una richiesta e mostrato una volta nella successiva, tipicamente "Saved." dopo un redirect. Salvalo nella sessione, poi leggilo e rimuovilo in un solo passo:
Impostare un cookie con setcookie
setcookie() accetta il nome, il valore e un array di opzioni (PHP 7.3 e successivi). Imposta queste quattro opzioni su ogni cookie, a meno che tu non abbia un motivo per non farlo:
<?php
setcookie('theme', 'dark', [
'expires' => time() + 60 * 60 * 24 * 30, // 30 days; 0 means "until the browser closes"
'path' => '/', // send it on every page, not just this folder
'secure' => true, // only over HTTPS
'httponly' => true, // JavaScript cannot read it
'samesite' => 'Lax', // not sent on most cross-site requests
]);
$theme = $_COOKIE['theme'] ?? 'light';
Due cose fanno inciampare:
$_COOKIEnon cambia nella stessa richiesta. Contiene ciò che il browser ha inviato con questa richiesta. Un cookie che imposti ora compare in$_COOKIEalla prossima.- I cookie sono intestazioni, quindi
setcookie()deve essere eseguito prima di qualsiasi output, esattamente comesession_start()eheader().
Per eliminare un cookie, impostalo di nuovo con lo stesso nome e path e una scadenza nel passato: setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']).
Salvare un array in un cookie
Il valore di un cookie è una stringa di circa 4 KB al massimo, quindi salva i dati strutturati come JSON, e tratta ciò che torna come non affidabile: il visitatore può modificarlo. Non usare mai unserialize() su un cookie: può creare oggetti delle tue classi ed eseguirne i metodi magici, mentre json_decode() restituisce sempre e solo array, stringhe, numeri, booleani, null o stdClass.
La pagina su json_encode tratta i flag. Per qualsiasi cosa più grande di pochi valori brevi, tieni i dati nella sessione (o in un database) e salva nel cookie solo una chiave.
Token "ricordami"
Un cookie "ricordami" non deve contenere l'id utente né l'hash della password. Salva nel cookie un lungo token casuale e nel database solo il suo hash, e confronta con hash_equals():
I valori cambiano a ogni esecuzione, perché random_bytes() è casuale. Se il database viene sottratto, gli hash sono inutili senza i cookie, e hash_equals() confronta in tempo costante così il controllo non rivela informazioni sui tempi.
Sessione vs cookie: quale usare
Sessione ($_SESSION) | Cookie ($_COOKIE) | |
|---|---|---|
| Salvata | Sul server | Nel browser |
| Il visitatore può leggerla o modificarla | No (solo l'id) | Sì |
| Dimensione | Limitata dal tuo spazio di archiviazione | Circa 4 KB per cookie |
| Dura | Finché il browser si chiude o la sessione scade | Fino alla sua scadenza expires |
| Usala per | Utente autenticato, carrello, token CSRF, messaggi flash | Tema, lingua, scelta sul consenso, token "ricordami" |
Errore comune: headers already sent
session_start(), setcookie() e header() inviano tutte intestazioni HTTP, e le intestazioni si possono inviare solo prima del corpo. Qualsiasi output precedente, anche una riga vuota prima di <?php in un file incluso, produce:
Warning: session_start(): Session cannot be started after headers have already been sent in /var/www/page.php on line 3
Warning: Cannot modify header information - headers already sent by (output started at /var/www/page.php:2) in /var/www/page.php on line 4
Il secondo messaggio ti dice dove è iniziato l'output (page.php:2): vai a quella riga e rimuovi l'output, oppure sposta session_start() sopra di essa. Nei file che contengono solo PHP, omettere il ?> di chiusura impedisce che spazi bianchi residui dopo di esso diventino output.
Domande frequenti
Qual è la differenza tra una sessione e un cookie in PHP?
Un cookie viene salvato nel browser del visitatore e inviato con ogni richiesta, quindi il visitatore può leggerlo e modificarlo. Una sessione salva i suoi dati sul server; il browser tiene solo un id di sessione casuale in un cookie (PHPSESSID). Metti in $_SESSION tutto ciò che deve essere affidabile, come l'id dell'utente autenticato.
Come distruggo una sessione al logout in PHP?
Svuota i dati, elimina il cookie di sessione e distruggi la sessione salvata: $_SESSION = [];, poi setcookie(session_name(), '', time() - 3600, '/'), poi session_destroy();. Fai poi un redirect così la pagina successiva riparte da zero.
Come elimino un cookie in PHP?
Impostalo di nuovo con lo stesso nome, percorso e dominio e una scadenza nel passato: setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']);. Fai anche unset($_COOKIE['theme']) se il resto dello script corrente lo legge, perché $_COOKIE cambia solo alla richiesta successiva.
Quanto dura una sessione PHP?
Di default il cookie di sessione dura finché il browser non viene chiuso (session.cookie_lifetime = 0) e il server può eliminare i dati di sessione dopo 1440 secondi (24 minuti) di inattività (session.gc_maxlifetime). Per un timeout fisso, salva nella sessione l'ora dell'ultima richiesta e verificala tu.
Perché session_start() dice headers already sent?
L'id di sessione viaggia in un'intestazione di cookie, e le intestazioni devono essere inviate prima di qualsiasi output. Un echo, dell'HTML, o anche una riga vuota o un BOM prima di <?php inviano prima l'output. Chiama session_start() proprio in cima allo script, prima che venga stampato qualsiasi cosa.