Vous appréciez mon travail ? Je serais ravi de prendre un café !

Vous prenez du plaisir à lire mes articles ? Vous apprenez de nouvelles choses ? Je serais ravis que vous supportiez mon travail avec une petite participation

1 café Merci, vous financez ma dose quotidienne de théïne (oui, en vrai je ne bois pas de café).
5 cafés Génial, ça couvre mes frais de serveur mensuels.
10 cafés Fantastique, avec ça je peux investir dans du matériel et approfondir mes connaissances.
BazinGa's - Tips & tuto IT

Session PHP – Principes et stockage en BDD

Concepts

Version imagée

Les session PHP peuvent être comparées à des sortes de besaces virtuelles (oui on n’utilise pas assez le mot besace) qui vont suivre chacun de vos utilisateurs lors de leurs navigation sur votre site web. L’intérêt de cette besace réside dans le fait que vous allez pouvoir y stocker tout un tas d’informations afin d’améliorer l’expérience de vos utilisateurs (par exemple son nom, ses préférences ou encore son panier d’achat).

Comme nous offrons un service haut de gamme, l’utilisateur n’aura pas besoin de porter cette besace. Nous la porterons pour lui avec au choix un mulet et sa carriole (configuration standard) ou avec un véhicule électrique dernière génération (utilisation des bases de données). L’utilisateur n’aura quand à lui qu’a conserver un numéro d’identification afin de pouvoir déverrouiller sa besace pour que le site puisse personnaliser son expérience.

Version technique

Plus concrètement (et techniquement), qu’est-ce qu’une session ?

C’est une variable superglobale (accessible depuis n’importe où) nommée $_SESSION. Cette variable est un tableau que vous pouvez manipuler à volonté (ajouter, lire, modifier et supprimer des valeurs).

L’intérêt et la particularité de cette variable c’est que sa valeur va dépendre de l’utilisateur et donc être différente pour chaque utilisateur.

Pour cela, cette variable (ou session) est associée à chaque utilisateur via un identifiant spécifique : l’identifiant de session, afin de pouvoir stocker en parallèle les données de plusieurs sessions différentes. En effet, chaque utilisateur possède sa propre session.

Les différents contenus de cette variable sont stockés sur le serveur dans un fichier texte (en version standard) ou dans une base de donnée (avec un peu de paramétrage détaillé plus bas) en association avec l’identifiant de session.

Cet identifiant est généré lors de la première visite de l’utilisateur sur le site. Il conserve ensuite cet identifiant dans un cookie ou par un mécanisme qui l’ajoute dans l’URL de chaque page qu’il visite.

Dès que l’utilisateur change de page, le serveur récupère l’identifiant puis renvoi la valeur de la variable $_SESSION correspondante. La page peut alors afficher et modifier cette variable selon le code que vous avez écrit.

Lorsque l’utilisateur quitte le site puis revient quelques heures plus tard, une nouvelle session est créée et associée à celui-ci. Sauf s’il revient avant la durée de vie maximale de la session, dans ce cas il est automatiquement ré-associé à son ancienne session.

Les sessions PHP sont souvent assimilées au principe de connexion à un site web. Ce n’est pas le cas, la session est un conteneur qui permet de stocker des informations sur l’utilisateur comme par exemple le fait qu’il soit « connecté » ou non.

Utilisation des sessions

Avant de voir le paramétrage, voici comment les sessions s’utilisent.

Pour indiquer à votre serveur que la page ouverte utilise les sessions, vous devez utiliser le code suivant avant tout code HTML :

session_start();

A partir du moment où ce code apparait, la superlobale $_SESSION vous est alors disponible et vous pouvez y accéder quand bon vous semble dans votre code comme n’importe quelle autre variable PHP. Vous pouvez par exemple y associer :

  • Un nom d’utilisateur : $_SESSION['USER']['nom'] = 'Bazin';
  • Une liste : $_SESSION['PANIER']['liste'] = 'banane,pomme,fraise';
  • Un statut de connexion : $_SESSION['AUTH']['statut'] = 'Connecté';

Vous remarquerez l’utilisation du tableau avec plusieurs dimensions, n’hésitez pas à structurer vos sous-variables pour mieux vous y retrouver.

Lors de l’utilisation de la fonction session_start(), le serveur procède d’abord à la vérification de l’identifiant de l’utilisateur. Trois cas sont possibles :

  • L’utilisateur n’a pas d’identifiant, l’identifiant est créé et la variable $_SESSION est initialisée à vide.
  • L’utilisateur possède un identifiant correspondant à une session stockée sur le serveur. La variable $_SESSION est initialisée avec les données stockées.
  • L’utilisateur possède un identifiant sans correspondance avec une session stockée sur le serveur. La variable $_SESSION est initialisée à vide avec un nouvel identifiant.

Il est impossible de savoir si l’utilisateur à quitté le site ou non. Ainsi, la destruction de la session peut intervenir de deux façons différentes :

  • Via une action de l’utilisateur (par exemple en cliquant sur un bouton) lançant la fonction session_destroy() qui va supprimer les valeurs de la superglobale $_SESSION pour l’identifiant spécifié.
  • Via un timeout, c’est à dire lorsqu’il n’y a pas eu de rechargement de la session (suite à un changement de page) depuis un temps définit.

Conservation de l’identifiant de session

Chaque session possède sont propre identifiant qui est conservé par l’utilisateur afin que le serveur puisse lui délivrer les éléments de la bonne session. Deux méthode de conservation peuvent être utilisées :

Le trans SID :

Le serveur ajoute l’identifiant de la session à la fin de l’URL lorsque l’utilisateur change de page via le code suivant ?PHPSESSID=id_de_session.
Sur la page suivante, il lit cette valeur. Le fait de quitter le site fait donc perdre la session à moins d’ajouter manuellement l’identifiant de la session à l’URL.
Cette méthode est plutôt oldschool et expose l’utilisateur à un risque de vole de session l’identifiant étant en clair dans l’URL.

Le cookie :

Le serveur créé un cookie dans le navigateur de l’utilisateur dans lequel il stocke l’identifiant de la session. A chaque ouverture de page, le serveur lit la valeur du cookie et associe la session stockée qui possède le même identifiant.
Il est donc possible de conserver sa session sur plusieurs jours, tant que le cookie ou que la session ne sont pas expirés.
Cette méthode est à privilégier car beaucoup plus sûr que le trans SID, l’identifiant n’étant que peu exposé.

Attention, vous pouvez définir la durée d’expiration de la session et du cookie de session indépendamment.
Pour régler ces paramètres rappelez-vous que :

  • Un cookie sans session ne sert à rien, il faudra créer une nouvelle session.
  • Une sessions sans cookie ne sert à rien, elle ne sera plus utilisée et vous prendra un peu d’espace pour rien.

Ainsi, l’idéal est d’avoir une certaine adéquation entre les deux valeurs.

Paramétrage des sessions

La configuration des sessions se fait dans le fichier php.ini. Voici les principales variables qui peuvent vous être utile avec les paramétrage que j’utilise régulièrement (mais pas toujours) :

Démarrage des sessions (1 = oui, 0 = non) :
L’utilisation du code « session_start(); » est un démarrage manuel. Cela permet de contrôler sur quelles pages utiliser les sessions.
session.auto_start = 0

Durée de vie de la session (en seconde) :
session.gc_maxlifetime = 86400

Probabilité de nettoyage des sessions
Les valeurs de la variable session ne sont pas nettoyées lorsque l’utilisateur qui le site cela permet entre autre de conserver les données de session si l’utilisateur revient quelques heures plus tard.
Le nettoyage des données de session (garbage collection) est opéré lors de la connexion d’un utilisateur de façon périodique. Les paramètres suivants règlent la périodicité (ici 1 fois toutes les 50 ouvertures de session) :
session.gc_probability = 1
session.gc_divisor = 50

Longueur des identifiants de session générés :
Privilégiez une valeur importante pour plus de sécurité.
session.sid_length = 128

Nombre de bit utilisés pour la génération des SID :
4 bits : utilisation des valeur de « 0 » à « 9 » et de « a » à « f »
5 bits : utilisation des valeur de « 0 » à « 9 » et de « a » à « v »
6 bits : utilisation des valeur de « 0 » à « 9 », de « a » à « z », de « A » à « Z », de « – » et de « , »
session.sid_bits_per_character = 5

Utilisation des trans SID (1 = oui, 0 = non) :
session.use_trans_sid = 0

Utilisation des cookies pour stockage des sessions (1 = oui, 0 = non) :
session.use_cookies = 1

Session uniquement par cookies (1 = oui, 0 = non) :
Il est en effet possible de faire cohabiter cookie et trans SID.
session.use_only_cookies = 1

Nom de la session et par extension du cookie de session :
Vous pouvez votre propre nom si besoin.
session.name = MON_COOKIE_SESSION

Durée de vie du cookie (0 = redémarrage navigateur, sinon délai en seconde) :
session.cookie_lifetime = 86400

Gestion du stockage des sessions et BDD

Par défaut, les sessions sont sauvegardées en clair dans des fichiers sur le serveur.

Le fichier php.ini contient l’emplacement par défaut de ces fichiers. Cet emplacement peut être configuré :
session.save_path = "/chemin/vers/repertoire/des/sessions"

Ces fichiers sont parfaitement compréhensible et peuvent être consultés librement à partir du moment où l’on a accès à leur répertoire. Ainsi, vous comprendrez le risque si l’accès à votre serveur venait à être corrompu.

Nous allons donc voire comment stocker les sessions dans une base de donnée (BDD) pour plus de praticité. De plus, le stockage en BDD nous offrira des options supplémentaires.

La gestion du stockage des sessions est intégrée à PHP et peut être modifiée à loisir en redéfinissant les fonctions permettant de créer, lire, modifier et supprimer les sessions.

Pour cela, Il faut utiliser la fonction suivante avant chaque démarrage de session :

session_set_save_handler(
    ma_fonction_ouverture_session($savePath, $sessionID),
    ma_fonction_fermeture_session(),
    ma_fonction_lecture_session($sessionID),
    ma_fonction_modification_session($sessionID, $sessionData),
    ma_fonction_destruction_session($sessionID),
    ma_fonction_nettoyage_session($sessionMaxLifetime)
);

Il est donc possible de définir précisément chaque action de gestion des sessions avec les éléments suivants :

  • ma_fonction_ouverture_session : créer le fichier de session et l’ouvre. Le répertoire de sauvegarde ainsi que l’identifiant de session sont passés en paramètre.
  • ma_fonction_fermeture_session : ferme le fichier de session.
  • ma_fonction_lecture_session : lit le contenu du fichier de session. L’identifiant de session est passé en paramètre.
  • ma_fonction_modification_session : modifie le contenu du fichier de session. L’identifiant de session et le contenu linéarisé de la super globale $_SESSION sont passés en paramètre.
  • ma_fonction_destruction_session : ferme le fichier de session puis le supprime. L’identifiant de session est passé en paramètre.
  • ma_fonction_nettoyage_session : supprime toutes les données obsolètes. La valeur de session.gc_maxlifetime est passée en paramètre.

Nous allons donc créé des fonctions dédiées à la gestion des sessions dans une BDD.

Modèle de données

Avant de créer les fonctions nécessaire, il va falloir créer le modèle de donnée qui va contenir les données de session.

J’utilise MySQL mais vous pouvez vous baser sur PostgreSQL, Oracle ou d’autres SGBD (il faudra cependant adapter les fonctions définies plus bas).

Voici donc le script qui vous permet de créer la table qui stockera les sessions :

CREATE TABLE `php_session` (
    `id_session` varchar(1024) COLLATE utf8_unicode_ci NOT NULL,
    `data` varchar(10000) COLLATE utf8_unicode_ci DEFAULT NULL,
    `creation` datetime DEFAULT CURRENT_TIMESTAMP,
    `update` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id_session`)
);

Cette table contient les champs suivants :

  • id_session : identifiant de la session PHP
  • date : données de la session (contenu de la variable $_SESSION)
  • creation : date de création de la session (automatiquement renseignée)
  • update : date de dernière lecture et/ou mise à jour des données de la session (mise à jour par le script mais qui peut être automatiquement renseignée en cas d’UPDATE)

N’hésitez pas à adapter ce script pour stocker d’autres éléments.

Fonctions de gestion des sessions

Dans un premier temps, créons un fichier php nommé « session_handler.php » (nom libre) dans lequel nous définissons une fonction nous permettant de se connecter à une base de donnée et de l’interroger :

// Fonction de connexion à la BDD
/////////////////////////////////
function general_connexion_bdd() {

    $bdd_dns='mysql:host=127.0.0.1;port=3306;dbname=ma_bdd;charset=utf8';
    
    $bdd_option['PDO::ATTR_EMULATE_PREPARES']='FALSE';
    $bdd_option['PDO::ATTR_ERRMODE']='PDO::ERRMODE_EXCEPTION';
    $bdd_option['PDO::ATTR_DEFAULT_FETCH_MODE']='PDO::FETCH_ASSOC';
    
    // Instantiation de la BDD
    try {
        $Connexion_BDD = new PDO($bdd_dns, 'username', 'password', $bdd_option);
    }
    catch (PDOException $e) {
        print "Erreur de connexion à la BDD ! Message : " . $e->getMessage() . "<br/>";
        die();
    }
    
    return $Connexion_BDD;
};

Définissons maintenant une classe PHP (pour plus de simplicité dans l’utilisation) permettant la gestion des sessions.

// Classe "session"
///////////////////

class Session {

    // Variable interne contenant la BDD
    private $_Connexion_BDD;


    // Initialisation de la session lors de l'appel de la classe
    public function __construct(){

        // Ouverture de la connexion à la BDD et association de cette connexion à la variable $_Connexion_BDD
        $this->_Connexion_BDD = general_connexion_bdd();


        // Paramétrage des sessions
        session_set_save_handler(
            array($this, "session_ouverture"),
            array($this, "session_fermeture"),
            array($this, "session_lecture"),
            array($this, "session_ecriture"),
            array($this, "session_destruction"),
            array($this, "session_nettoyage")
            );

        // Démarrage des sessions
        session_start();
    }


    // Définition des fonctions de gestion des sessions
    ///////////////////////////////////////////////////

    // Ouverture des sessions
    public function session_ouverture($savePath, $sessionID) {
        if ( $this->_Connexion_BDD ) {
            // Si la connexion existe, on renvoie "true".
            return true;
        }

        // En cas d'erreur, on force php à annuler l'utilisation des sessions.
        return false;
    }



    // Fermeture des sessions
    public function session_fermeture() {

        // Nettoyage de la BDD lors de la fermeture pour ne pas attendre le nettoyage automatique
        $this->session_nettoyage(ini_get("session.gc_maxlifetime"));

        // Destruction de la connexion
        $this->_Connexion_BDD = null;

        // Renvoie de "true" pour valider la fermeture.
        return true;
    }



    // Lecture des sessions
    public function session_lecture($sessionID) {

        // Création d'un date_time actuel
        $datetime_actuel = new DateTime("now", new DateTimeZone('Europe/Paris'));

        // Préparation de la requête
        $requete = $this->_Connexion_BDD->prepare("SELECT `data` FROM php_session WHERE `id_session` = ? LIMIT 1");
        // Execution de la requête
        $requete->execute([$sessionID]);
        // Récupération des résultats
        $resultat = $requete->fetch(PDO::FETCH_ASSOC);

        if ( $resultat == true ) {
            // Mise à jour de la date
            // Préparation de la requête
            $requete = $this->_Connexion_BDD->prepare("UPDATE php_session SET `update` = ? WHERE `id_session` = ?");
            // Execution de la requête
            $requete->execute([($datetime_actuel->format('Y-m-d H:i:s')), $sessionID]);

            return $resultat['data'];
        };

        // Si quelque chose ne fonctionne pas, on ne retourne rien.
        return '';
    }



    // Ecriture des sessions
    public function session_ecriture($sessionID, $sessionData) {

        // Création d'un date_time actuel
        $datetime_actuel = new DateTime("now", new DateTimeZone('Europe/Paris'));

        // Préparation de la requête d'INSERT avec UPDATE si la données existe déjà
        $requete = $this->_Connexion_BDD->prepare("INSERT INTO php_session (`id_session`, `data`, `update`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE `data` = ?");
        // Execution de la requête
        $requete->execute([$sessionID, $sessionData, ($datetime_actuel->format('Y-m-d H:i:s')), $sessionData]);
        // Récupération des résultats
        $resultat = $requete->rowCount();

        if ( $resultat >= 0 ) {
            // Si l'INSERT ou l'UPDATE réussi, on renvoie "true".
            return true;
        };

        // Si quelque chose ne fonctionne pas, on retourne "false".
        return false;
    }



    // Destruction des sessions
    public function session_destruction($sessionID) {

        // Préparation de la requête
        $requete = $this->_Connexion_BDD->prepare("DELETE FROM php_session WHERE `id_session` = ?");
        // Execution de la requête
        $requete->execute([$sessionID]);
        // Récupération des résultats
        $resultat = $requete->rowCount();

        if ( $resultat >= 1 ) {
            // Si la suppression a réussi, on renvoie "true".
            return true;
        };

        // Si quelque chose ne fonctionne pas, on retourne "false".
        return false;
    }


    // Nettoyage de la BDD

    public function session_nettoyage($sessionMaxLifetime) {

        // Calcul du timestamp d'expiration.
        $timestamp_expiration = time() - $sessionMaxLifetime;

        // Cacul de la date d'expiration UTC.
        $date_expiration = new DateTime("@".$timestamp_expiration);

        // Formatage de la date dans le bon fuseau horaire
        $date_expiration->setTimezone(new DateTimeZone('Europe/Paris'));


        // Préparation de la requête
        $requete = $this->_Connexion_BDD->prepare("DELETE FROM php_session WHERE `update` <= ?");
        // Execution de la requête
        $requete->execute([$date_expiration->format('Y-m-d H:i:s')]);
        // Récupération des résultats
        $resultat = $requete->rowCount();

        if ( $resultat >= 0 ) {
            // Si la suppression a réussi, on renvoie "true".
            return true;
        };

        // Si quelque chose ne fonctionne pas, on retourne "false".
        return false;

    }
}

Afin d’utiliser vos sessions « personnalisées », vous devez utiliser le code suivant dans chaque page pour lesqunécessitant l’utilisation de sessions :

// Appel du fichier de gestion des session
require_once 'general_session_handler.php';
// Démarrage de la session
$session = new Session();

Et voila, PHP s’occupera alors tout seul de mettre à jour vos sessions lorsque vous définirez la variable $_SESSION.


Cet article vous a plu ?

N'hésitez pas à le partager, il interessera surement certains de vos contacts.

Les thèmes suivants contiennent des articles en lien avec celui-ci, allez faire un tour :

PHPProgrammation bddsession

50%