HTTP et programmation côté serveur avec PHP

Licence Informatique 3ème année

Alexandre Niveau — Jean-Marc Lecarpentier

Enseignement des technologies du Web

 

HTTP et programmation côté serveur avec PHP

Notes de cours

  • HTTP, serveurs web, pages dynamiques et PHP
    • HTTP et URL
    • Fonctionnement d’un serveur web
    • Pages dynamiques et CGI
    • Interaction entre PHP et HTTP
    • Envoi de données avec HTTP
    • Méthode GET et méthodes non safe
    • Paramètres d’URL vs corps d’un POST
    • Petits détails de PHP sur lesquels je voulais revenir

Travail personnel

Objectifs

L'objectif de cette première séance est de se remettre dans le bain de la programmation côté serveur avec PHP.

Exercice 1 — Mise en place de l’environnement de travail : Hello world #

Cet exercice a déjà été fait dans le cadre du cours « Technologies web 1 » en L1, puis « Technologies web 3 » en L2. Il est donc principalement destiné à celles et ceux qui viennent de rejoindre la licence. Pour les autres, il ne s'agit que de se rafraîchir la mémoire : n'y passez pas 10 minutes !

Questions

  1. Le département maths-info met à votre disposition un espace personnel sur le web. Son fonctionnement est expliqué sur cette page de la FAQ du département maths-info. Si vous n'avez pas encore lu cette page, faites-le (pas la peine de lire les « Informations complémentaires pour les plus avancé·es »).
  2. Si vous avez un éditeur de texte favori, ouvrez-le et créez un nouveau fichier ; sinon, cliquez sur l'icône tout en haut à gauche de l'écran, tapez « éditeur » dans le champ de recherche qui apparaît, et choisissez l'application qui s'appelle tout simplement « Éditeur de texte » (voir capture d'écran ci-dessous)
    Capture d’écran
    Capture d’écran
    (cliquer pour accéder à l’image pleine taille)
  3. Écrivez n'importe quoi dedans, par exemple Hello world.
  4. Enregistrez le fichier sous le nom hello.html dans le répertoire www-dev sur le serveur. Si vous ne savez pas faire, avec « Éditeur de texte » il faut faire comme suit :
    1. cliquez sur l'icône menu, puis faites « Enregistrer sous »
      Capture d’écran
      Capture d’écran
      (cliquer pour accéder à l’image pleine taille)
    2. cherchez votre serveur dans la colonne de gauche, cliquez dessus, puis allez dans www-dev (pas dans www-prod !), et ensuite modifiez le nom proposé pour mettre hello.html et cliquez sur « Enregistrer ».
      Capture d’écran
      Capture d’écran
      (cliquer pour accéder à l’image pleine taille)
  5. Ouvrez Firefox (on n'utilisera que Firefox dans ce module). Si vous ne trouvez pas l'icône, cliquez sur « Activités » en haut à gauche et tapez « firefox » dans le champ texte. Avec Firefox, rendez vous à l'URL https://dev-LOGIN.users.info.unicaen.fr/, où LOGIN est votre login de connexion (par exemple dupont227).

    Vous devriez voir une page intitulée « Index of / », qui montre en fait le contenu du répertoire www-dev. Normalement, il ne devrait y avoir qu'un fichier « hello.html ». En cliquant sur ce nom, vous devriez voir ce que vous avez écrit dans le fichier. Si ce n'est pas le cas, demandez de l'aide.

    Capture d’écran de la page
    Capture d’écran de la page
    (cliquer pour accéder à l’image pleine taille)

    Notez que le contenu de la barre d'adresse de Firefox a changé quand vous avez cliqué sur « hello.html ». L'URL est à présent https://dev-LOGIN.users.info.unicaen.fr/hello.html.

  6. Créez un répertoire test dans www-dev. Vous devriez le voir apparaître dans le listing si vous retournez à l'URL https://dev-LOGIN.users.info.unicaen.fr/ dans le navigateur. Observez bien comment la barre d'adresse du navigateur est modifiée si vous cliquez sur test, ou sur le fichier hello.html. Assurez-vous d'avoir compris que le serveur sert le contenu de www-dev à la racine de votre site https://dev-LOGIN.users.info.unicaen.fr : n'hésitez pas à demander des explications à votre chargé·e de TP si ce n'est pas clair pour vous !

Attention à bien organiser votre serveur web, et attention aussi à ne pas y mettre tous vos fichiers, mais seulement ceux qui ont des raisons d'y être !

NB: votre site web https://dev-LOGIN.users.info.unicaen.fr/ n'est malheureusement pas accessible depuis l'extérieur de l'université. C'est uniquement un outil pédagogique.

Exercice 2 — Premiers pas avec PHP côté serveur #

Cet exercice vise à vous familiariser avec les pages dynamiques et le fonctionnement de PHP. Pour rappel, une page PHP est un script, qu'il faut exécuter pour en voir le résultat. C'est le serveur qui exécute le script quand un client y accède via HTTP : si vous essayez d'ouvrir une page PHP locale avec votre navigateur, ça ne fonctionnera pas (le navigateur vous proposera probablement de la télécharger).

Exécution d'un script par le serveur

  1. Créez un script PHP hello.php contenant le code suivant :
    <?php
    echo "Hello\n";
    echo "<strong>\n";
    echo "World!\n";
    
    Exécutez-le dans un terminal : trois lignes doivent s'afficher.
  2. Copiez le script à la racine de votre www-dev, et allez à l'URL https://dev-LOGIN.users.info.unicaen.fr/hello.php. Qu'est-ce qui s'affiche ? Quelles sont les différences par rapport au terminal, et pourquoi ?
  3. Affichez la source de la page (dans Firefox, clic-droit puis « Afficher la source », ou Ctrl-U). Que voyez-vous :
    • le contenu du fichier PHP, ou bien
    • les trois lignes qu'affichait le terminal ?
    ⇒ Que signifie le terme « source » ici ?
  4. Le navigateur peut-il savoir que la page a été générée par un script PHP ?
  5. Que se passe-t-il si vous faites une erreur dans le script ?
  6. Que se passe-t-il si vous écrivez quelque chose (par exemple TEST) avant le <?php ? (Enlevez-le après avoir testé, sinon la suite ne marchera pas !)

Accès au contexte HTTP depuis le script

Le serveur ne se contente pas d'exécuter le script PHP : il lui fournit diverses informations sur la requête HTTP, effectuée par le client, qui a conduit à cette exécution. D'autre part, le script ne se contente pas d'écrire le corps de la réponse HTTP : il peut demander au serveur de renvoyer tel champ d'en-tête, ou même tel code de statut.

  1. Avant de faire la suite, attention : il ne doit rien y avoir dans le fichier avant la balise ouvrante de PHP (<?php), même pas une espace ou un saut de ligne ! Vérifiez bien !
  2. Ajoutez la ligne suivante juste avant le premier echo :
    http_response_code(404);
    Elle permet de changer le code de statut de la réponse HTTP.
  3. Actualisez la page : vous ne devriez voir aucune différence ! En revanche, si vous actualisez après avoir ouvert l'onglet « Réseau » des outils développeur (raccourci Ctrl-Shift-E), vous devriez constater que la réponse envoyée est bien une 404.
  4. Ajoutez la ligne suivante juste en-dessous de l'autre :
    header("Content-Type: text/plain");
    et actualisez la page. Que se passe-t-il, et pourquoi ? Cliquez sur la réponse HTTP dans l'onglet « Réseau », pour voir les en-têtes de la réponse : voyez-vous la différence avec ou sans cette ligne ?
  5. PHP refuse de modifier l'en-tête de la réponse HTTP après avoir commencé à en écrire le corps. Vérifiez cela en lui faisant afficher quelque chose avant l'appel à http_response_code (et enlevez-le après avoir testé).
  6. Avec une fonction de debug type var_export, affichez le résultat de la fonction getallheaders(), et comparez à ce que vous voyez dans l'onglet « Réseau ».
  7. Avec une fonction de debug type var_export, affichez le contenu de la variable $_GET (attention à l'underscore) : il devrait être vide. Que se passe-t-il si vous modifiez la barre d'adresse afin d'aller à l'URL https://dev-LOGIN.users.info.unicaen.fr/hello.php?toto=12&blabla=truc ?
  8. Optionnel : faire en sorte que la page s'affiche « en mode texte » si on passe dans l'URL la valeur texte au paramètre mode, et en « mode visuel » (HTML interprété) sinon.

Exercice 3 — Manipulation de fichiers, inclusion et redirection #

Téléchargez l'archive de l'exercice, qui contient un fragment de HTML et un fragment de PHP, ainsi qu'un script inclusion.php qui utilise ces fragments.

Inclusion avec include/require

  1. Placez le contenu de l'archive sur votre serveur, regardez bien le contenu des scripts, observez le résultat. Assurez-vous de bien comprendre ce qui se passe.
  2. Remplacez les trois include par des require, et regardez à nouveau le résultat. Quelle est la différence ?
  3. Enlevez l'inclusion du fragment inexistant et vérifiez que cette fois la conclusion s'affiche bien.
  4. Dupliquez le contenu du body (copiez le contenu, du début jusqu'à la conclusion, et collez-le après la conclusion). Chaque inclusion aura donc lieu deux fois. Que se passe-t-il, et pourquoi ?
  5. Remplacez les quatre require par des require_once, et expliquez le résultat.

Il existe aussi include_once, mais il est moins utile. À moins d'avoir une bonne raison, il vous est recommandé d'utiliser include pour inclure un fragment de contenu (HTML ou PHP), et require_once pour inclure un fichier PHP contenant des déclarations (constantes, fonctions, classes…).

Récupération du contenu d'un fichier dans une variable

Les instructions include et require exécutent immédiatement le code : elles sont en quelque sorte remplacées par le résultat de l'exécution. Il est souvent utile de récupérer ce résultat avant affichage, pour le modifier ou pour le passer à une fonction.

  1. Dans un nouveau script PHP, récupérez avec la fonction file_get_contents le contenu du fragment HTML dans une variable $frg et l'afficher.
  2. Faites de même avec le fragment PHP. Que se passe-t-il, et pourquoi ?
  3. Pour récupérer le résultat de l'exécution du fragment PHP, il faut utiliser un include/require en activant l'output buffering (voir cours). Faites-le.

Redirection et header

La fonction PHP header permet de manipuler les en-têtes HTTP de la réponse envoyée par le serveur.

  1. Dans une nouvelle page PHP, mettez le contenu suivant :
    <?php
    header('X-Mon-Entete-Perso: lorem ipsum dolor sit amet');
    ?>
    
    Ouvrez l'onglet « Réseau » des outils développeur de Firefox (raccourci : control-shift-E), accédez à la page, et vérifiez que l'en-tête custom a bien été transmis. La spec de HTTP autorise tous les en-têtes commençant par X- — elle garantit qu'ils ne seront jamais utilisés dans une future version. Cela permet d'étendre HTTP pour des usages spécifiques à une application sans s'exposer à de futures incompatibilités.
  2. La fonction header ajoute (ou modifie) des en-têtes HTTP, mais dans certains cas très particuliers elle va un peu plus loin.

    Mettez le contenu suivant dans une nouvelle page PHP :

    <?php
    header('Location: https://fr.wikipedia.org/wiki/Chien');
    ?>
    <p>Voilà du <strong>HTML</strong> qu'on n'aura pas le temps de voir !</p>
    
    
    Comprenez-vous ce qui se passe ? Observez le déroulement avec l'outil « Réseau » de Firefox, et vérifiez que header n'a pas simplement ajouté un en-tête, mais a aussi modifié le code de statut de la réponse HTTP.

Exercice 4 — Pseudo-forum #

Récupérer cette archive. Elle contient un répertoire forum qui lui-même contient un fichier CSS ainsi qu'un fichier donnees.php, qui déclare un tableau $donnees contenant les messages d'une sorte de pseudo-forum. Chaque message est un tableau associatif avec trois champs :

  • le pseudo
  • le texte du message
  • la date (une instance de la classe DateTime)

Le but de l'exercice est de réaliser un site présentant ces messages. Bien sûr, un vrai site ne stockerait pas les messages de cette façon (dans un tableau écrit en dur dans un script PHP) — l'objectif est de vous faire travailler plusieurs notions (et notamment réviser les formulaires), indépendamment des problèmes spécifiques aux BD (connexion, erreurs de requêtes, etc.).

Site de base

  1. Placer le répertoire forum sur votre serveur, et créer un fichier liste.php dans le répertoire. Inclure le fichier donnees.php au début du script liste.php : nous pourrons dès lors accéder aux messages dans la variable $donnees, qui constituera notre pseudo-base de données.

  2. Afficher une liste de tous les messages de la base. NB: les messages ne sont pas dans l'ordre chronologique. C'est normal. Cela fera l'objet d'une question ultérieure.

  3. Améliorer la présentation, en vous appuyant sur le fichier CSS fourni : le résultat doit être similaire à cette page (dont il n'est pas interdit de regarder le code source HTML !).

  4. S'assurer que le HTML résultant est valide, grâce au validateur du W3C.

  5. Modifier le script pour qu'il réagisse à un paramètre chercher dans l'URL, en n'affichant que les messages dont le texte contient la chaîne de caractères valeur du paramètre. Par exemple, liste.php?chercher=blabla doit afficher tous les messages dont le texte (et seulement le texte — pas le pseudo) contient la chaîne blabla, et aucun autre.

  6. Ajouter un menu en haut de la page, avec trois liens :

    • « Tous les messages » doit pointer vers liste.php
    • « Filtrer » doit pointer vers filtre.php
    • « Ajouter » doit pointer vers ajout.php

    S'assurer que le HTML est toujours valide.

  7. Créer le script filtre.php. Il doit contenir un formulaire avec un champ texte servant à faire une recherche dans le texte des messages, en s'appuyant sur le paramètre chercher implémenté précédemment dans liste.php.

  8. S'assurer que le HTML généré par filtre.php est valide.

  9. Créer le script ajout.php. Il doit contenir un formulaire d'ajout de message, avec un champ pour le pseudo et un champ pour le texte. La soumission du formulaire doit envoyer le message sous la forme d'une requête POST vers liste.php.

  10. S'assurer que le HTML généré par ajout.php est valide.

  11. Modifier liste.php pour que, lorsque la requête est POST, un nouveau message soit créé (avec le pseudo et le texte donnés, et comme date la date courante) et ajouté au tableau $donnees, avant que la liste ne soit affichée.

  12. Normalement, vous devez bien voir votre nouveau message dans la liste (a priori, tout en bas)… mais il doit disparaître quand vous cliquez sur « Tous les messages » ! Assurez-vous d'avoir compris pourquoi. Autrement dit : si vous ne comprenez pas ce qui se passe, demandez des explications à votre chargé·e de TP !

L'ajout de message nécessite de mettre en place de la persistance de données. On abordera cela dans les prochaines semaines — ce n'est pas l'objectif de cet exercice. On va à présent améliorer notre filtre.

Améliorations du filtre

  1. Ajouter un paramètre pseudo à la page liste.php, qui permet de n'afficher que les messages dont le champ « pseudo » correspond exactement au pseudo donné.

    Lors de vos tests, n'oubliez pas de vérifier que les deux paramètres fonctionnent bien ensemble : on doit pouvoir rechercher une chaîne dans les messages postés par un pseudo fixé.

  2. Ajouter au formulaire de filtre.php un champ texte permettant d'utiliser le paramètre pseudo que vous venez d'implémenter.

    Une fois que ça fonctionne, remplacer le champ texte par un menu déroulant contenant tous les pseudos de la base de messages. (Il faudra bien sûr inclure la « base de données » dans le script filtre.php.)

  3. Ajouter un paramètre après à la page liste.php, qui prend une date et une heure au format YYYY-MM-DDThh:mm (par exemple 2025-10-16T05:40:24). La liste des messages ne doit alors contenir que des messages qui datent d'après la date indiquée dans le paramètre après.

  4. Ajouter à liste.php un paramètre avant qui fonctionne comme après, mais dans l'autre sens.

    Bien sûr, avant et après doivent pouvoir fonctionner ensemble — et avec chercher et pseudo. Tous ces critères sont orthogonaux et donc compatibles.

  5. Ajouter deux champs de type datetime-local au formulaire de filtre.php, pour exploiter les paramètres avant et après que vous venez d'implémenter.

  6. Vérifiez que filtre.php est bien toujours valide !

Encore des améliorations, plus compliquées

  1. Ajouter un paramètre ordre à liste.php.

    1. Faire en sorte qu'il ne puisse prendre que la valeur pseudo : lui donner toute autre valeur doit générer une page d'erreur avec comme statut HTTP 400 Bad Request.

    2. Lorsque ordre=pseudo, les messages doivent être ordonnés dans l'ordre alphabétique du pseudo de leur auteur/trice. Il y a plusieurs façons de faire, je n'en connais pas qui soit vraiment satisfaisante.

    3. Ajouter à présent une autre valeur possible, ordre=texte, permettant d'obtenir les messages classés dans l'ordre alphabétique de leur texte.

    4. Ajouter encore une autre valeur possible, ordre=chronologique : elle doit avoir pour effet que les messages soient classés dans l'ordre chronologique (du plus ancien au plus récent). NB: les opérateurs de comparaison numérique fonctionnent sur les instances de DateTime. Une autre possibilité est d'utiliser leur méthode getTimestamp().

  2. Ajouter trois boutons radio dans le formulaire filtre.php pour choisir l'ordre d'affichage de la liste via le paramètre ordre. On regroupera les boutons dans un élément fieldset, et on vérifiera une nouvelle fois la validité de la page de formulaire.

  3. Ajouter un dernier paramètre à liste.php : inverser. Lorsqu'il est présent avec la valeur oui, l'ordre des messages doit être inversé (par exemple, avec les paramètres ordre=chronologique&inverser=oui, l'ordre sera du plus récent au plus ancien). Toute autre valeur que oui doit générer une page d'erreur avec comme statut HTTP 400 Bad Request.

  4. Ajouter une case à cocher au formulaire de filtre.php permettant d'inverser l'ordre d'affichage des messages.

  5. Faire en sorte que lors de l'utilisation du paramètre chercher, le script liste.php surligne la chaîne de caractère recherchée à l'intérieur des messages listés. L'élément HTML approprié pour cela est mark.

    Bien vérifier que le surlignement est correct dans les messages contenant des caractères non-ASCII avant la chaîne recherchée. Penser également à vérifier la validité d'une page avec des résultats de recherche.