Sessioni

HTTP è un protocollo senza stato, eppure quasi ogni applicazione ha bisogno di mantenere lo stato tra le richieste, per esempio il contenuto di un carrello. È esattamente a questo che servono le sessioni. Mostreremo:

  • come usare le sessioni
  • come evitare i conflitti di nomi
  • come impostare la scadenza

Quando si usano le sessioni, ogni utente riceve un identificatore univoco chiamato session ID, che viene trasmesso in un cookie. Serve da chiave per i dati della sessione. A differenza dei cookie, che sono conservati dal lato del browser, i dati della sessione sono conservati dal lato del server.

Le sessioni le configuriamo nella configurazione; è particolarmente importante la scelta del tempo di scadenza.

Della gestione delle sessioni si occupa l'oggetto Nette\Http\Session, al quale arrivate facendovelo passare con la dependency injection. Nei presenter basta chiamare $session = $this->getSession().

Installazione e requisiti

Avvio della sessione

Per impostazione predefinita Nette avvia automaticamente la sessione nel momento in cui cominciamo a leggerne o scriverne i dati. Per avviare la sessione a mano usate $session->start().

Quando avvia la sessione, PHP invia gli header HTTP che influenzano la cache (vedi session_cache_limiter) ed eventualmente il cookie con il session ID. Perciò bisogna sempre avviare la sessione prima di inviare qualsiasi output al browser, altrimenti verrà lanciata un'eccezione. Se quindi sapete che durante il rendering della pagina verrà usata una sessione, avviatela prima a mano, per esempio nel presenter.

In modalità di sviluppo la sessione viene avviata da Tracy, perché la usa per mostrare nella Tracy Bar le barre per i redirect e le richieste AJAX.

Sezioni

In PHP puro l'archivio dei dati di sessione è realizzato come un array accessibile tramite la variabile globale $_SESSION. Il problema è che le applicazioni sono di solito composte da molte parti indipendenti e, se tutte hanno a disposizione un solo array, prima o poi si verificherà una collisione di nomi.

Il Nette Framework risolve il problema dividendo tutto lo spazio in sezioni (oggetti Nette\Http\SessionSection). Ogni unità usa poi la propria sezione con un nome univoco e nessuna collisione può verificarsi.

Otteniamo la sezione dalla sessione:

$section = $session->getSection('nome univoco');

Nel presenter basta usare getSession() con un parametro:

// $this è un Presenter
$section = $this->getSession('nome univoco');

L'esistenza di una sezione si può verificare con il metodo $session->hasSection('nome univoco'). L'elenco dei nomi di tutte le sezioni esistenti lo restituisce $session->getSectionNames().

Lavorare con la sezione stessa è poi molto facile grazie ai metodi set(), get() e remove():

// scrittura di una variabile
$section->set('userName', 'john');

// lettura di una variabile, restituisce null se non esiste
echo $section->get('userName');

// rimozione di una variabile
$section->remove('userName');

Per ottenere tutte le variabili di una sezione potete usare un ciclo foreach:

foreach ($section as $key => $val) {
	echo "$key = $val";
}

Come impostare la scadenza

La scadenza si può impostare per le singole sezioni o perfino per le singole variabili. Possiamo far scadere il login di un utente dopo 20 minuti e ricordare comunque il contenuto del carrello.

// la sezione scade dopo 20 minuti
$section->setExpiration('20 minutes');

Per impostare la scadenza delle singole variabili serve il terzo parametro del metodo set():

// la variabile 'flash' scade dopo 30 secondi
$section->set('flash', $message, '30 seconds');

Ricordate che il tempo di scadenza dell'intera sessione (vedi configurazione della sessione) deve essere uguale o maggiore del tempo impostato per le singole sezioni o variabili.

Per annullare una scadenza impostata in precedenza serve il metodo removeExpiration(); per cancellare la scadenza di una singola variabile passate il suo nome: removeExpiration('flash'). Per rimuovere subito tutta la sezione serve il metodo remove().

Eventi $onStart, $onBeforeWrite

L'oggetto Nette\Http\Session ha gli eventi $onStart e $onBeforeWrite, quindi potete aggiungere callback che vengono richiamati dopo l'avvio della sessione oppure prima che venga scritta su disco e poi terminata.

$session->onBeforeWrite[] = function () {
	// scriviamo i dati nella sessione
	$this->section->set('basket', $this->basket);
};

Gestione della sessione

Panoramica dei metodi della classe Nette\Http\Session per la gestione della sessione:

start(): void

Avvia la sessione.

isStarted(): bool

La sessione è avviata?

close(): void

Termina la sessione. La sessione termina automaticamente alla fine dell'esecuzione dello script.

destroy(): void

Termina e cancella la sessione.

exists(): bool

La richiesta HTTP contiene un cookie con il session ID?

regenerateId(): void

Genera un nuovo session ID casuale. I dati restano conservati.

getId(): string

Restituisce il session ID.

Configurazione

La sessione la configuriamo nella configurazione. Se scrivete un'applicazione che non usa il container DI, usate questi metodi per configurarla. Vanno chiamati prima di avviare la sessione.

setName (string $name): static

Imposta il nome del cookie in cui viene trasmesso il session ID. Il nome standard è PHPSESSID. Torna utile se fate girare più applicazioni diverse sullo stesso sito.

getName(): string

Restituisce il nome del cookie in cui viene trasmesso il session ID.

setOptions (array $options)static

Configura la sessione. Si possono impostare tutte le direttive di sessione di PHP (in formato camelCase, scrivete per esempio savePath invece di session.save_path) e anche readAndClose.

setExpiration (?string $expire)static

Imposta il tempo di inattività dopo il quale la sessione scade.

setCookieParameters (string $path, ?string $domain=null, ?bool $secure=null, SameSite|string|null $samesite=null)static

Imposta i parametri dei cookie. I valori predefiniti dei parametri li potete cambiare nella configurazione.

setSavePath (string $path)static

Imposta la directory in cui vengono salvati i file di sessione.

setHandler (\SessionHandlerInterface $handler)static

Imposta un handler personalizzato, vedi la documentazione di PHP.

La sicurezza prima di tutto

Il server presume di comunicare con lo stesso utente finché le richieste sono accompagnate dallo stesso session ID. Il compito dei meccanismi di sicurezza è garantire che sia davvero così e che l'identificatore non possa essere rubato o sostituito.

Il Nette Framework configura perciò correttamente le direttive PHP perché il session ID venga trasmesso solo nei cookie, sia inaccessibile a JavaScript e gli eventuali identificatori nell'URL vengano ignorati. Nei momenti critici, come il login dell'utente, genera inoltre un nuovo session ID.

Per configurare PHP si usa la funzione ini_set, che purtroppo alcuni hosting vietano. Se è il caso del vostro hosting, provate a mettervi d'accordo con loro perché ve la consentano o almeno configurino il server correttamente.

versione: 4.x