Sessions
HTTP est un protocole sans état ; presque toutes les applications ont pourtant besoin de conserver un état entre les requêtes, par exemple le contenu d'un panier d'achat. C'est exactement à cela que servent les sessions. Nous allons montrer :
- comment utiliser les sessions
- comment éviter les conflits de noms
- comment définir l'expiration
Lors de l'utilisation des sessions, chaque utilisateur reçoit un identifiant unique appelé ID de session, transmis dans un cookie. Il sert de clé vers les données de la session. Contrairement aux cookies, qui sont stockés du côté du navigateur, les données de session sont stockées du côté du serveur.
Nous configurons les sessions dans la configuration ; le choix de la durée d'expiration est particulièrement important.
La gestion des sessions est assurée par l'objet Nette\Http\Session, auquel vous accédez en vous le faisant
passer par injection de dépendances. Dans les
presenters, il suffit d'appeler $session = $this->getSession().
Démarrer la session
Par défaut, Nette démarre automatiquement une session dès que nous commençons à y lire ou à y écrire des données. Pour
démarrer une session manuellement, utilisez $session->start().
PHP envoie au démarrage de la session des en-têtes HTTP influençant la mise en cache (voir session_cache_limiter), et éventuellement un cookie contenant l'ID de session. Il est donc toujours nécessaire de démarrer la session avant d'envoyer la moindre sortie au navigateur ; sinon, une exception sera levée. Si vous savez qu'une session sera utilisée pendant le rendu de la page, démarrez-la donc manuellement au préalable, par exemple dans le presenter.
En mode développement, Tracy démarre la session, car elle s'en sert pour afficher dans la Tracy Bar les barres relatives aux redirections et aux requêtes AJAX.
Sections
En PHP pur, le stockage des données de session est implémenté comme un tableau accessible via la variable globale
$_SESSION. Le problème est que les applications se composent généralement de nombreuses parties indépendantes et
que, si toutes ne disposent que d'un seul tableau, une collision de noms finira tôt ou tard par se produire.
Nette Framework résout ce problème en divisant tout l'espace en sections (objets Nette\Http\SessionSection). Chaque unité utilise alors sa propre section portant un nom unique, et aucune collision ne peut se produire.
Nous obtenons une section depuis la session :
$section = $session->getSection('nom unique');
Dans le presenter, il suffit d'utiliser getSession() avec un paramètre :
// $this est un Presenter
$section = $this->getSession('nom unique');
L'existence d'une section peut être vérifiée à l'aide de la méthode $session->hasSection('nom unique'). La
liste des noms de toutes les sections existantes est renvoyée par $session->getSectionNames().
Le travail avec la section elle-même est ensuite très simple grâce aux méthodes set(), get() et
remove() :
// écriture d'une variable
$section->set('userName', 'john');
// lecture d'une variable, renvoie null si elle n'existe pas
echo $section->get('userName');
// suppression d'une variable
$section->remove('userName');
Pour obtenir toutes les variables d'une section, vous pouvez utiliser une boucle foreach :
foreach ($section as $key => $val) {
echo "$key = $val";
}
Comment définir l'expiration
L'expiration peut être définie pour chaque section, voire pour chaque variable. Nous pouvons faire expirer la connexion d'un utilisateur au bout de 20 minutes tout en continuant de mémoriser le contenu du panier.
// la section expire au bout de 20 minutes
$section->setExpiration('20 minutes');
Pour définir l'expiration de variables individuelles, utilisez le troisième paramètre de la méthode
set() :
// la variable 'flash' expire au bout de 30 secondes
$section->set('flash', $message, '30 seconds');
Rappelez-vous que la durée d'expiration de la session entière (voir la configuration des sessions) doit être égale ou supérieure à la durée définie pour les sections ou variables individuelles.
Pour annuler une expiration définie précédemment, utilisez la méthode removeExpiration() ; pour effacer
l'expiration d'une variable précise, passez son nom : removeExpiration('flash'). Pour supprimer immédiatement toute
la section, utilisez la méthode remove().
Événements $onStart, $onBeforeWrite
L'objet Nette\Http\Session possède les événements $onStart et $onBeforeWrite,
vous pouvez donc ajouter des callbacks invoqués après le démarrage de la session ou avant qu'elle ne soit écrite sur le disque
puis terminée.
$session->onBeforeWrite[] = function () {
// écriture de données dans la session
$this->section->set('basket', $this->basket);
};
Gestion de la session
Aperçu des méthodes de la classe Nette\Http\Session pour la gestion des sessions :
start(): void
Démarre la session.
isStarted(): bool
La session est-elle démarrée ?
close(): void
Termine la session. La session se termine automatiquement à la fin de l'exécution du script.
destroy(): void
Termine et supprime la session.
exists(): bool
La requête HTTP contient-elle un cookie avec un ID de session ?
regenerateId(): void
Génère un nouvel ID de session aléatoire. Les données sont conservées.
getId(): string
Renvoie l'ID de session.
Configuration
Nous configurons la session dans la configuration. Si vous écrivez une application qui n'utilise pas de conteneur DI, utilisez ces méthodes pour la configurer. Elles doivent être appelées avant le démarrage de la session.
setName (string $name): static
Définit le nom du cookie dans lequel l'ID de session est transmis. Le nom standard est PHPSESSID. C'est utile si
vous faites tourner plusieurs applications différentes sur le même site.
getName(): string
Renvoie le nom du cookie dans lequel l'ID de session est transmis.
setOptions (array $options): static
Configure la session. Il est possible de définir toutes les directives de session de PHP (au format camelCase, par exemple
écrire savePath au lieu de session.save_path) ainsi que readAndClose.
setExpiration (?string $expire): static
Définit la durée d'inactivité au bout de laquelle la session expire.
setCookieParameters (string $path, ?string $domain=null, ?bool $secure=null, SameSite|string|null $samesite=null): static
Définit les paramètres des cookies. Vous pouvez changer les valeurs par défaut des paramètres dans la configuration.
setSavePath (string $path): static
Définit le répertoire où sont stockés les fichiers de session.
setHandler (\SessionHandlerInterface $handler): static
Définit un handler personnalisé, voir la documentation PHP.
La sécurité avant tout
Le serveur part du principe qu'il communique avec le même utilisateur tant que les requêtes sont accompagnées du même ID de session. Le rôle des mécanismes de sécurité est de garantir qu'il en va bien ainsi et que l'identifiant ne peut être ni volé ni substitué.
Nette Framework configure donc correctement les directives de PHP pour ne transmettre l'ID de session que dans les cookies, le rendre inaccessible au JavaScript et ignorer tout identifiant présent dans l'URL. De plus, aux moments critiques, comme la connexion de l'utilisateur, il génère un nouvel ID de session.
La fonction ini_set est utilisée pour configurer PHP, mais malheureusement certains hébergeurs en
interdisent l'usage. Si c'est le cas du vôtre, essayez de convenir avec lui qu'il vous autorise cette fonction ou qu'il configure
au moins correctement le serveur.