Cours sur HTTP * url : https://en.wikipedia.org/wiki/Uniform_Resource_Identifier avec des exemples. query, fragment. chemin : pas forcément un vrai chemin. * header fields https://en.wikipedia.org/wiki/List_of_HTTP_header_fields * stateless * safe methods * idempotent methods * cacheable * status code & reason phrase https://en.wikipedia.org/wiki/List_of_HTTP_status_codes * cache : forward/reverse ; browser/machine/proxy/ISP https://en.wikipedia.org/wiki/Web_cache * compression https://en.wikipedia.org/wiki/HTTP_compression * auth * HTTPS https://en.wikipedia.org/wiki/HTTPS serveurs web : un programme qui sait répondre à une requête HTTP par une réponse HTTP. exemples : apache, nginx.
HyperText Transfer Protocol ; c'est le langage que parlent le serveur web et le navigateur web pour se communiquer les pages
Élément le plus fondamental du web, et aussi le plus caché pour le grand public
HTTPS est la version sécurisée de HTTP : les messages sont chiffrés, et donc illisibles pour quiconque les intercepterait entre le client et le serveur ex: wifi public
Le web est un ensemble de ressources (documents — ou autre !), liées entre elles
Chaque ressource a une adresse (ce qui permet de la récupérer, et de la lier à d'autres ressources)
Les adresses sont appelées URL (ou plus généralement URI)
La syntaxe des URL a été créée pour le web, mais est utilisée dans de nombreux autres contextes
userinfo host port ┌───────┴───────┐ ┌────┴────────┐ ┌┴┐ http://john.doe:password@www.example.com:123/forum/questions/?tag=networking&order=newest#top └─┬─┘ └───────────┬───────────────────────┘└─┬─────────────┘└────────┬─────────────────┘└┬─┘ scheme authority path query fragment
La partie « scheme » indique le protocole à utiliser pour récupérer la ressource
La partie « authority » indique à qui il faut s'adresser pour récupérer la ressource
La partie « path » indique le chemin vers la ressource sur le serveur
La partie « query » permet d'indiquer des paramètres (pour filtrer ou mettre en forme la ressource)
La partie « fragment » identifie une sous-partie de la ressource (par exemple une section d'une page HTML)
Autres exemples de la syntaxe URI
Remarque : il y a de nombreuses variations pour les noms des différentes parties d'une URL
Commandes : GET, POST, HEAD, PUT, DELETE…
Champs d'en-tête : Host, User-Agent, Accept… seul Host est obligatoire
http://www.toto.fr/blog/posts/243.html?order=newest#topIl la découpe en morceaux pour identifier
www.toto.fr
/blog/posts/243.html?order=newest
#top
www.toto.fr
(après avoir récupéré son adresse IP via un serveur DNS) avec comme contenu
GET /blog/posts/243.html?order=newest HTTP/1.1 Host: www.toto.fr Accept: text/html Accept-Charset: utf-8 Connection: keep-alive(et sans doute d'autres champs d'en-tête).
Codes : 200 (OK), 404 (not found), 500 (internal server error)…
Champs de réponse : Content-type, Last-Modified, Location…
HTTP/1.1 200 OK
Date: Mon, 05 Jan 2015 12:12:12 GMT
Last-Modified: Wed, 02 Jan 2013 18:18:18 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 323
Connection: close
<html>
<head> <title>Le blog de Toto</title> </head>
<body> <p>Bienvenue sur mon blog !</p> </body>
</html>
netcat example.com 80 <demo/requete.txt
ça devrait donner qqch comme demo/reponse.txt
Il existe de nombreux serveurs web (au sens logiciel), les plus utilisés étant Apache et nginx
Rôle du programme serveur : reçoit des requêtes HTTP et renvoie des réponses HTTP (qui peuvent contenir n'importe quoi)
En pratique, on a souvent besoin que les URL correspondent à des fichiers
un des rôles principaux du programme serveur est d'interpréter le chemin demandé dans la requête comme un chemin dans le système de fichiers
Interprétation pas directe ! On ne veut pas que tout le système de fichiers de la machine soit accessible sur le web…
le chemin demandé est « traduit »
/
)
correspond à un répertoire fixé de son système de fichiers,
appelé « web root » ou « document root »
/var/www/html
sous Linux
C:\Program Files\Apache\htdocs
sous Windows
La « traduction » est plus ou moins une simple concaténation des chemins : la requête HTTP
GET /bidule/truc/machin.html?blabla=blibli
sera interprétée comme correspondant au fichier suivant sur le serveur :
/var/www/html/bidule/truc/machin.html
(les paramètres ne font pas partie du chemin : ce qu'en fait le serveur dépend de sa configuration)
On dit que le serveur web « sert » le contenu du répertoire /var/www/html
à la racine du site
Exemple : le serveur web de la fac sert le contenu du répertoire
/users/durand222/www-dev
à la racine du site https://dev-durand222.users.info.unicaen.fr/
Si le chemin traduit de l'URL correspond à un fichier (HTML, ou image, PDF, audio…), c'est lui que le serveur envoie en réponse.
Si le chemin traduit ne correspond à rien, le serveur enverra une erreur 404 (on peut configurer ce comportement)
Si le chemin correspond à un répertoire le serveur regarde s'il contient un fichier index.html
si oui, c'est ce fichier qu'il sert
sinon, ça dépend de la configuration : il peut renvoyer une 404, une 403, ou générer une page pour montrer le contenu du répertoire
Si besoin que le contenu d'un site soit dynamique, par exemple dépende d'une base de données, le serveur ne peut pas se contenter de renvoyer le contenu de fichiers écrits à l'avance
il faut que le serveur génère du HTML
Il va déléguer cette tâche à d'autres programmes — on parle de « programmation côté serveur »
Très rapidement, aux débuts du web, est apparu le besoin d'une interface standard entre les serveurs web et les programmes de génération de HTML
En effet, il y avait plusieurs serveurs logiciels, et les programmes de génération de HTML étaient écrits dans des langages divers
il était souhaitable que tout cela soit interopérable, c'est-à-dire qu'on voulait pouvoir faire exécuter n'importe quel programme de génération par n'importe quel serveur logiciel
il fallait donc se mettre d'accord sur la façon dont le serveur fournit la requête au programme, et recupère la réponse
REQUEST_METHOD, REQUEST_URI, HTTP_HOST, HTTP_COOKIE
…)
Status
)
directory index https://en.wikipedia.org/wiki/Webserver_directory_index .htaccess ? https://en.wikipedia.org/wiki/.htaccess CGI https://en.wikipedia.org/wiki/Common_Gateway_Interface /cgi-bin/ lancer script shell avec env|sort et date (ou java !) historiquement ça marchait comme ça autre technique : serveur exécute en fonction de l'extension du fichier -& php
PHP est un langage conçu au départ pour écrire des scripts CGI
L'idée était de pouvoir mettre des bouts de programme au milieu d'une page HTML
<!DOCTYPE html>
<html>
<head><title>Ma page</title><meta charset="UTF-8" /></head>
<body>
<h2>Les nombres</h2>
<p>Voici les nombres de 1 à 10000 :
<?php
for ($i = 1; $i <= 10000; $i++) {
echo "$i, ";
}
?>
</p>
</body>
</html>
C'est une page HTML avec des blocs d'instructions encadrées
par <?php
et ?>
<?
?>
permettent de définir des PI (« processing
instruction ») ;php
indique que ces instructions sont du PHP.Ce qui est en dehors de ces blocs est affiché tel quel
Les blocs sont remplacés par le résultat de l'exécution des instructions
Regarder le code source de la page : noter bien qu'il ne subsiste aucune trace du code PHP !
nombres.php
nombres.php
Le navigateur ne voit jamais le code PHP : il ne récupère que de l'HTML !
Les navigateurs ne savent d'ailleurs pas interpréter un fichier PHP. Si vous essayez d'en ouvrir un en local avec Firefox, il devrait vous proposer de le télécharger, comme pour tout type de fichier qu'il ne connaît pas.
Contrairement à un script CGI normal, si on exécute le script PHP en ligne de commande, il n'y a aucun champ d'en-tête
PHP a plusieurs modes de fonctionnement, appelés SAPI (server API)
Quand on lance l'interpréteur en ligne de commande, le SAPI est CLI
(command line interface) : l'interpréteur comprend qu'il n'est pas en train d'interagir avec un serveur web et n'écrit ni ligne de statut ni en-têtes
Avec les autres SAPI, l'interpréteur fournit bien statut et en-têtes ; mais pour les modifier, on ne peut pas simplement les afficher, il faut faire appel à des fonctions dédiées.
http_response_code(404)
header("Content-Type: text/plain");
Attention : il ne doit pas y avoir d'espace entre le nom de l'en-tête et le deux-points qui suit. Le serveur ne comprend pas et renvoie une erreur 500 au client !
Bien qu'il faille utiliser des fonctions dédiées pour modifier les en-têtes HTTP, PHP continue à se comporter comme s'il fallait avoir écrit tous les en-têtes avant de commencer à écrire le corps de la réponse
Comme http_response_code
et header
modifient les en-têtes HTTP, elles n'ont aucun effet si elles sont utilisées après que le programme a commencé à écrire le contenu
Cela génère un warning « Headers already sent » : comprendre « j'ai déjà envoyé les en-têtes au serveur, je ne peux plus les modifier »
Attention,
même un simple saut de ligne ou une espace avant l'ouverture de la balise <?php
suffisent à « envoyer » les en-têtes !
démo et explications
Certaines installations bufferisent la sortie pour éviter ça, mais toutes ne le font pas : c'est donc une fausse sécurité (un peu comme désactiver les erreurs d'un compilateur…)
Il est parfois utile de rediriger le client vers une autre URL
Quand il reçoit une réponse avec un de ces statuts, le navigateur va chercher la nouvelle URL dans le champ d'en-tête Location
et immédiatement faire une nouvelle requête à cette URL, sans même afficher le contenu de la réponse.
Location
, par exemple
header("Location: http://example.com");
(PHP se charge alors de passer un code 302)En CGI, les informations provenant de la requête HTTP sont données dans l'entrée standard (corps de le requête) et dans des variables d'environnement (tout le reste).
$_ENV
… mais il est désactivé sur les serveurs de l'université
$_SERVER
. On y trouve notamment :
$_SERVER['REQUEST_URI']
)$_SERVER['PATH_INFO']
).C'est suffisant pour récupérer les paramètres d'URL, mais ce n'est pas très pratique ; PHP nous fournit donc un autre tableau superglobal, $_GET
, qui contient les couples clefs-valeurs des paramètres d'URL (et ce, quelle que soit la méthode HTTP utilisée, même POST !)
Démo GET
file_get_contents('php://input')
. C'est utile s'il contient par exemple du JSON, mais si le format des données est le format standard renvoyé par les formulaires HTML (application/x-www-form-urlencoded
), alors PHP fournit également les couples clefs-valeurs dans un autre tableau superglobal, $_POST
.
Rappel : phpinfo()
affiche la configuration PHP de votre serveur et la valeur de toutes les superglobales (Voir)
Sur le web moderne les internautes ont généralement de nombreuses possibilités d'interaction avec les sites web
Iels peuvent faire des actions, notamment des modifications de contenu
Cela implique notamment d'envoyer des données du client au serveur
On va maintenant voir comment cela se passe au niveau HTTP
GET
,
qui permet de récupérer une ressource
Caractéristique importante : c'est une « safe method ⇒ elle est définie comme devant n'avoir aucun effet de bord
elle n'est pas censée modifier l'état de l'application (hormis pour des détails secondaires, comme un compteur de visites)
elle est « sûre », au sens où on peut l'utiliser sans crainte ⇒ important par exemple pour les robots indexeurs !
On peut donner des paramètres à l'URL demandée avec GET
, qui vont affecter
la façon dont la ressource est récupérée, ou le type de contenu
C'est le standard (RFC 7231) qui impose que GET soit une safe method, mais en pratique il n'y a aucune contrainte technique qui oblige à respecter cette contrainte
il est possible d'utiliser les paramètres d'URL pour faire des actions (qui modifient l'état) sur un site web… mais c'est une très mauvaise idée !
Pour faire des actions, il est préférable d'utiliser d'autres méthodes
En pratique les deux dernières sont rarement utilisées (voir plus loin), c'est en général POST qui est utilisée pour tous les cas de figure non couverts par GET
Pour envoyer des données avec la méthode POST
de HTTP,
on les met
dans le
corps de la requête
Le format des données est libre, mais en général il suit la même syntaxe que les paramètres d'URL (à quelques détails près)
formellement, il s'agit du format MIME
application/x-www-form-urlencoded
POST /chemin/pagederecup HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded Content-Length: 21 nom=Toto&couleur=vert+clair
Couples clef-valeur séparés par des &
Espaces remplacées par +
Encodage particulier des caractères spéciaux et non-ASCII
HTML fournit une interface permettant aux internautes de construire « interactivement » ces couples clef-valeur : les formulaires
Ils sont principalement utiles pour envoyer des données (méthode POST
) :
poster un commentaire, s'authentifier sur un site…
Mais ils permettent aussi de construire une URL paramétrée : champ de recherche, contrôle de l'affichage du contenu (ordre, filtrage)…
Ils fonctionnent exactement de la même manière pour les deux usages ; dans les deux cas le navigateur utilise le contenu du formulaire pour construire une chaîne de couples clef-valeur
POST
, le navigateur met la chaîne dans
une requête POST
qu'il envoie à l'URL fournieGET
, le navigateur met la chaîne à la fin de l'URL fournie
après un ?
, et il envoie une requête GET
à l'URL paramétrée ainsi obtenueGET
vs POST
Dans les deux cas, cliquer sur le bouton submit
construit les couples clef-valeur et les communique au serveur
N'importe quel programme peut être utilisé pour les récupérer : il suffit qu'il comprenne le protocole HTTP, ou qu'il puisse être lancé par le serveur lorsqu'un client demande l'URL en question (ex. : script PHP)
method="GET"
) : limités en pratique à environ 2000 caractères (voire 255) ; ne devraient être utilisés que pour filtrer et recherchermethod="POST"
) : pas de limite de taille, et pas visible par l'internauteNB : il est parfaitement possible d'envoyer des données avec POST
à une URL paramétrée !
Si hésitation entre paramètres d'URL et méthode POST, se demander si l'URL paramétrée aurait un sens en tant que lien. Il n'est envisageable d'utiliser la méthode GET que si la réponse est oui.
POST
ne sont pas affichées par les navigateurs, mais elles ne sont pas cachées !
Ne pas penser que POST
est plus sécurisé que GET
Les paramètres d'URL sont souvent utilisés abusivement pour le routage (mauvaises pratiques qui se sont répandues notamment par le biais de PHP), par ex.
http://toto.fr/forum?post=754
Ce n'est pas non plus leur rôle !
L'URL du post 754 devrait être quelque chose comme http://toto.fr/forum/posts/754
Les paramètres, comme leur nom l'indique, servent à paramétrer une page
http://toto.fr/forum?date=2015-02-23&sort=title http://toto.fr/forum?q=glace+vanille
On a très souvent besoin d'inclure du code d'un autre fichier PHP (ou HTML)
include
: génère simplement un warning en cas d'échecrequire
: arrête le programme avec une erreur fatale en cas d'échec,
donc rien ne s'afficheInstructions include_once
et require_once
pour éviter une inclusion multiple
La gestion des chemins (quels accès sont autorisés ou non, quelle est la référence des chemins relatifs dans un script inclus depuis un autre script…) est relativement infernale : ne pas essayer de faire des choses subtiles, ou prévoir un stock d'aspirine
<pre>
<?php
echo "One\n";
ob_start(); /* on active l'output buffering :
* tout ce qui suit sera enregistré
* dans un buffer au lieu d'être affiché
*/
echo "Two\n";
include "demo/fichier.txt";
$var = ob_get_clean(); /* on désactive l'OB, on
* récupère le contenu du
* buffer, et on le vide.
*/
echo "Three\n";
echo "Contenu du buffer :\n « " . $var . " »\n";
?>
</pre>
One Three Contenu du buffer : « Two Coucou, je suis un fichier ! Au revoir »
Si on utilise une variable qui n'existe pas, un avertissement (notice) est lancé
il n'y a pas d'erreur, PHP considère que la variable a la valeur null
Ne pas s'appuyer là-dessus : s'il y a un avertissement, c'est pour une bonne raison (code pas propre, plus difficile à maintenir)
isset
if (isset($toto))
echo "La variable \$toto existe, voilà son contenu : $toto";
else
echo "La variable \$toto n'existe pas";
Le code précédent ne génère aucun avertissement.A priori, dans du code propre et bien organisé, on n'a jamais besoin de isset()
!
isset
est très souvent utilisée pour vérifier que les tableaux
$_GET
ou $_POST
contiennent un paramètre donné
key_exists
, qui risque moins
de cacher une erreur dans le code :
if (key_exists('toto', $_GET))
$toto = $_GET['toto'];
else
$toto = '';
array_key_exists est un peu plus lent (genre 2 fois) à cause de l'appel de fonction. c'est pas bien méchant. ça reste O(1) normalement.empty()
empty
permet de vérifier qu'une variable est « vide »,
c'est-à-dire que
false
Attention, dans la liste des valeurs égales à false
, il y a notamment la chaîne '0'
!
même si vous savez que votre variable contient une chaîne, empty()
ne vous permet pas de vérifier qu'elle est vraiment vide !
si vraiment besoin de manipuler des variables
qui n'existent pas forcément,
il est conseillé d'utiliser plutôt isset
et un test explicite
(hors cas très particuliers).
Il est également très fortement déconseillé d'utiliser empty($maVariable)
si la variable
est censée exister : cela peut cacher des erreurs dans le code (erreur dans le nom de variable, par exemple).
si besoin de tester qu'une variable est « vide », utiliser plutôt !$maVariable
ou $maVariable == false
(comparaison volontairement faible),
ou mieux, comparer explicitement à la valeur vide qui vous intéresse : $maChaine === ''
ou $monTableau === []
.
Pour résumer, il est encore plus déconseillé d'utiliser empty
que isset
.
Détails sur isset
et empty
, leur relation avec null
, et quand les utiliser : the definitive guide to PHP’s isset
and empty