Cookies & sessions — Programmation objet avec PHP & architecture d’un site web

Licence Informatique, semestre 6

Alexandre Niveau — Valentin Lemière

Enseignement des technologies du Web

 

Cookies & sessions — Programmation objet avec PHP & architecture d’un site web

Notes de cours

Travail personnel

Objectifs

Ce TP commence par deux exercices d'application directe des principes des cookies et des sessions. Ils ont l'avantage de vous faire manipuler du PHP simple et réfléchir un peu au fonctionnement de HTTP, avant de passer au troisième exercice, qui met en place l'architecture de site présentée en cours, et dont les objectifs sont sans doute un peu plus abstraits.

Exercice 1 — Cookies

Un corrigé de cet exercice est disponible (version simple ; avec formulaire ; archive du code).

  1. Écrire un script PHP qui crée une page HTML simple, avec un titre et un peu de texte.
  2. Créer deux fichiers CSS pour cette page, defaut.css et special.css, qui donnent des rendus très différents (par exemple différentes couleurs de fond).
  3. En utilisant les cookies, faire en sorte que le CSS utilisé soit defaut.css lors de la première visite d'un·e internaute, et special.css si l'internaute a déjà visité la page il y a moins d'un an.
  4. Modifier le code précédent, pour que le site ne se souvienne de l'internaute que pendant une minute.
  5. Optionnel : Ajouter un formulaire sur la page permettant à l'internaute de choisir le fichier CSS qu'il ou elle préfère, et faire en sorte que ce choix soit enregistré pendant 30 minutes.

Exercice 2 — Sessions PHP

Un corrigé de cet exercice est disponible (archive du code).

  1. En utilisant les sessions PHP, écrire un script qui crée une page HTML indiquant le nombre de visites de l'internaute, en suivant ce modèle.
  2. Ajouter à la page un bouton ou un lien qui permet de remettre le compteur à zéro.

Exercice 3 — Introduction à l’architecture MVCR

Une proposition de correction incrémentale de ce TP est disponible ici sous forme d'un dépôt Git. Cet exercice commence au commit « Préliminaires » et va jusqu'au commit « Compléments: AnimalStorage plus construit par le routeur ». Une archive du résultat final (comprenant le dépôt) est disponible ici.

Cet exercice vise à construire un site web très simple utilisant l'architecture MVCR présentée en cours.

L'énoncé est long, mais c'est parce que l'exercice est très guidé. Il n'y a aucune difficulté particulière jusqu'à la section « Compléments ». Ne pas hésiter à s'inspirer de l'exemple des poèmes présenté (en partie) en cours (résultat final, archive du code).

Préliminaires

  1. Créer un nouveau répertoire pour cet exercice, par exemple exoMVCR (c'est le nom qui sera utilisé dans cet énoncé). Dans toute la suite, on se placera dans ce répertoire.
  2. Créer dans exoMVCR un répertoire src, qui contiendra tout le code source de l'application (c'est-à-dire tout ce qui n'est pas censé être directement accessible depuis un client).
  3. Dans un fichier src/Router.php, créer une classe Router contenant uniquement une méthode main qui affiche quelque chose (par exemple « Hello world »).
  4. Créer un fichier animaux.php à la racine de exoMVCR avec le contenu suivant :
    <?php
    /*
     * On indique que les chemins des fichiers qu'on inclut
     * seront relatifs au répertoire src.
     */
    set_include_path("./src");
    
    /* Inclusion des classes utilisées dans ce fichier */
    require_once("Router.php");
    
    /*
     * Cette page est simplement le point d'arrivée de l'internaute
     * sur notre site. On se contente de créer un routeur
     * et de lancer son main.
     */
    $router = new Router();
    $router->main();
    ?>

Normalement, en allant à l'URL du répertoire (par exemple http://dev-NUMETU.users.info.unicaen.fr/exoMVCR/animaux.php, en adaptant le chemin), vous devriez voir le message du routeur.

Vue

  1. Créer un répertoire view dans src, contenant une classe View avec deux attributs, $title et $content, et une méthode render() qui affiche une page HTML avec le contenu de ces attributs. (La méthode render() peut se contenter d'inclure un squelette défini dans un autre fichier, mais ce n'est pas obligatoire.)
  2. Ajouter une méthode makeTestPage() à View, qui remplit les attributs avec du texte quelconque. Dans le main du routeur, créer une instance de View et lui faire afficher la page de test. Tester le résultat.
  3. Ajouter une méthode makeAnimalPage($name, $species) à View, qui génère l'affichage d'une page sur l'animal passé en argument : par exemple, la page générée par l'appel makeAnimalPage("Médor", "chien") aura pour titre « Page sur Médor » et pour contenu « Médor est un animal de l'espèce chien ». Tester cet affichage depuis le routeur.

On a maintenant une page qui affiche des informations sur Médor. On va faire en sorte qu'elle puisse en afficher sur divers animaux, en fonction de l'URL. Pour cela, on va d'abord créer un contrôleur pour gérer les informations.

Contrôleur

  1. Créer un répertoire src/control, contenant une classe Controller avec un attribut $view. Le constructeur doit prendre en argument une instance de View qu'il met dans cet attribut.
  2. Ajouter la méthode suivante dans le contrôleur :
    public function showInformation($id) {
    	$this->view->makeAnimalPage("Médor", "chien");
    }
    
    Modifier le routeur pour qu'il fasse appel à cette méthode (avec un argument quelconque) plutôt que d'appeler lui-même makeAnimalPage. Vérifier que tout fonctionne toujours comme avant, et corriger votre code si ce n'est pas le cas.
  3. Ajouter un attribut $animalsTab au contrôleur, contenant le tableau suivant :
    array(
    	'medor' => array('Médor', 'chien'),
    	'felix' => array('Félix', 'chat'),
    	'denver' => array('Denver', 'dinosaure'),
    );
    
    (et ce que vous voulez d'autre…). Utiliser ce tableau dans showInformation pour que le site affiche que Médor est un chien, Félix un chat, Denver un dinosaure, etc., en fonction de l'argument $id passé à la méthode, mais affiche un message d'erreur (« Animal inconnu ») s'il ne connaît pas l'identifiant. Tester depuis le routeur en passant différents identifiants.
  4. Si ce n'est déjà fait, faire en sorte que le message d'erreur mentionné à la question précédente soit généré par une méthode makeUnknownAnimalPage() de la vue.

Routeur

  1. Modifier le routeur pour qu'il passe en argument à la méthode showInformation du contrôleur la valeur du paramètre id de l'URL.
  2. Tester : votre site devrait dire « Médor est un animal de l'espèce chien » si on met le paramètre id=medor dans l'URL, « Denver est un animal de l'espèce dinosaure » pour id=denver, etc., et devrait afficher le message d'erreur pour des identifiants inconnus.
  3. Que se passe-t-il s'il n'y a pas de paramètre id dans l'URL ? Faire en sorte qu'une page d'accueil s'affiche, en modifiant le routeur et la vue.

Modèle

On voudrait à présent donner plus d'information sur chaque animal : outre son espèce, la page d'un animal doit maintenant donner son âge. Cela reste possible d'ajouter un argument age à la méthode makeAnimalPage de la vue, mais on voit bien les limites de cette approche : que se passerait-il si on avait des dizaines d'informations à donner sur chaque animal (taille, poids, nourriture préférée, etc.) ?

La solution est de manipuler les animaux comme des instances d'une classe Animal.

  1. Dans un répertoire src/model, créer une classe Animal, qui a comme attributs (au minimum) un nom, une espèce et un âge. Créer le constructeur et les accesseurs appropriés (pas besoin de mutateurs, on ne modifiera pas les instances).
  2. Modifier le tableau $animalsTab dans le contrôleur pour qu'il contienne des instances de Animal, et changer la méthode makeAnimalPage de la vue pour qu'elle ne prenne plus qu'un Animal en argument. Vérifier que tout fonctionne toujours.
  3. Modifier la vue pour qu'elle affiche l'âge de l'animal.

Liste des animaux

On voudrait à présent faire une page spéciale, d'URL animaux.php?liste, qui affiche la liste de tous les animaux dont le site connaît l'espèce.

  1. Ajouter une méthode makeListPage() à la vue (qui pour l'instant affiche un contenu quelconque), une méthode showList() au contrôleur qui appelle makeListPage(), et faire en sorte que le routeur appelle showList() lorsque l'URL contient un paramètre liste (quelle que soit sa valeur). Vérifier que ça fonctionne.
  2. Faire en sorte que makeListPage prenne en argument un tableau d'instances de Animal et affiche une liste des noms.
  3. Modifier showList dans le contrôleur pour qu'elle passe à makeListPage la liste de tous les animaux.
  4. Faire en sorte que chaque nom dans la liste soit un lien vers la page de l'animal en question.
  5. Pour faire la question précédente, avez-vous pris garde à ce que la vue ne fasse pas d'hypothèses sur la façon dont sont construites les URL ? Si ce n'est déjà fait, créer une méthode getAnimalURL($id) dans le routeur, qui renvoie l'URL de la page d'un animal. Attention, la vue doit donc avoir accès à une instance du routeur : l'ajouter comme attribut et comme argument au constructeur.

Stockage

Pour l'instant, c'est le contrôleur qui « décide » des animaux connus par le site. Généralement, ce type d'information est plutôt contenu dans une base de données. On ne va pas utiliser de BD dans ce TP, mais on peut déjà adapter le contrôleur pour qu'il fonctionne dans tous les cas, BD ou non.

  1. Créer une interface AnimalStorage dans src/model ; elle aura pour méthodes
    • read($id), dont les implémentations doivent renvoyer l'instance de Animal ayant pour identifiant celui passé en argument, ou null si aucun animal n'a cet identifiant ; et
    • readAll(), dont les implémentations doivent renvoyer un tableau associatif identifiant ⇒ animal contenant tous les animaux de la « base ».
  2. Créer une classe AnimalStorageStub dans src/model qui implémente l'interface AnimalStorage en utilisant des données écrites « en dur » (reprendre le tableau $animalsTab du contrôleur).
  3. Ajouter un argument de type AnimalStorage au constructeur du contrôleur. Le contrôleur doit enregistrer cette instance dans un attribut, et l'utiliser dans ses méthodes showInformation et showList à la place du tableau $animalsTab.
  4. Modifier le code du routeur pour qu'il passe une nouvelle instance de AnimalStorageStub au constructeur du contrôleur.

À présent, le contrôleur est complètement indépendant de la façon dont sont stockées les données. C'est le routeur qui décide d'utiliser un AnimalStorageStub, mais il aurait pu utiliser un AnimalStorageMySQL, par exemple, sans que le contrôleur ne s'en rende compte.

Compléments (optionnel)

Les questions suivantes sont destinées aux plus rapides et à celles et ceux qui voudraient approfondir leur travail. Si vous avez eu beaucoup de mal à faire ce qui précède, il vaut mieux tout recommencer une deuxième fois plutôt que s'attaquer à ces questions moins guidées.

  1. Ce n'est pas spécialement propre que ce soit le routeur qui prenne la décision de créer un AnimalStorageStub. Faire en sorte que l'instance de AnimalStorage que le routeur utilise pour créer le contrôleur soit elle-même récupérée dans le constructeur du routeur (ou mieux, comme paramètre du main). Ce sera donc animaux.php, c'est-à-dire le véritable point d'entrée de l'application, qui va prendre cette décision. (S'il prend plusieurs décisions de ce type, ce qui arrive très rapidement sur un site modérément compliqué, il peut être plus naturel de les déporter à nouveau dans un fichier de configuration, que animaux.php se contente d'inclure.)
  2. Comme on l'a vu en cours, les paramètres d'URL ne sont pas destinés à faire du routage. Il est relativement simple de modifier le code pour que les URL soient de la forme animaux.php/denver, en utilisant la variable $_SERVER['PATH_INFO'], qui contient la « partie virtuelle » de l'URL (le « chemin » qui se situe après le nom du script). Faire en sorte que ça soit le cas. Si votre code est propre, vous ne devriez avoir à changer que les méthodes main et getAnimalURL du routeur.