Parcours d’apprentissage web – Part VI : PHP, structurer et traiter côté serveur
Passer du navigateur au traitement des données. Jusqu’ici, nous avons travaillé dans un environnement où le navigateur interprète directement nos fichiers. Avec PHP, nous changeons de point de vue : le code n’est plus exécuté côté client, mais côté serveur, avant même que la page ne soit envoyée.
PHP ne sert pas à “faire des pages”, mais à organiser, transformer et préparer des données. Il agit comme une couche intermédiaire entre le navigateur et les ressources du serveur : fichiers, base de données, sessions, traitements métiers.
Dans cette partie, l’objectif n’est pas d’apprendre PHP ligne par ligne, mais de comprendre où il intervient, pourquoi il est utile, et comment il s’articule avec le reste. Nous sommes ici dans la continuité directe de la Part V consacrée à la relation client serveur, au déploiement et à l’infrastructure web. Là où nous avons posé le décor, PHP vient maintenant prendre place concrètement dans ce dialogue, côté serveur. Cette étape marque une transition importante dans le parcours : après le navigateur et le serveur, nous entrons ici dans une logique applicative où PHP prend en charge le traitement, l’organisation et la circulation des données côté serveur.
Ce qui change réellement avec PHP
Avec HTML, CSS et JavaScript, nous manipulons des langages que le navigateur comprend directement. Le fichier est lu et affiché sans détour. Avec PHP, une bascule s’opère. Le code ne peut plus être transmis tel quel, il doit d’abord être exécuté côté serveur, puis transformé en une réponse que le navigateur pourra lire. Le navigateur ne lit donc plus un fichier, il interroge un serveur. Le script s’exécute, fabrique le contenu, puis disparaît. Ce qui arrive côté navigateur n’est plus le code d’origine, mais uniquement le résultat prêt à être affiché. Prenons un cas très simple, mais en le regardant comme on le vivrait réellement côté serveur.
<?php
// Une valeur “métier” arrive dans le script
$album = "Amon Düül II – Yeti";
// Une date réelle issue des données
$release = "1970-04-01";
// Formatage de la date côté PHP (approche moderne avec IntlDateFormatter)
$formatter = new IntlDateFormatter(
'fr_FR',
IntlDateFormatter::LONG,
IntlDateFormatter::NONE,
null,
null,
'MMMM yyyy'
);
$date = $formatter->format(new DateTime($release));
// On prépare ce que l’on veut afficher
$title = "Discographie";
$content = "L’album $album est sorti le $date";
// Puis on construit la réponse qui sera envoyée au navigateur
$html = <<<HTML
<html>
<head><title>$title</title></head>
<body>
<h1>$content</h1>
</body>
</html>
HTML;
echo $html;
?>Si l’on se place côté navigateur, tout le script PHP, son exécution et les données manipulées ont déjà disparu. Il ne reste qu’un fragment prêt à être affiché, le code précédent rendrait la sortie suivante :
<h1>L’album Amon Düül II – Yeti est sorti en avril 1970</h1>Ce décalage est essentiel. D’un côté, un script qui s’exécute, manipule des données, prend des décisions. De l’autre, une page générée côté serveur, sans trace de ce qui s’est passé en amont. C’est précisément cette étape intermédiaire qui fait toute la différence : le contenu n’est plus lu, il est fabriqué.
Le cycle d’une requête côté serveur
Lorsqu’un utilisateur saisit une URL vers une page .php, il ne récupère pas un fichier comme avec un .html. Il déclenche un passage côté serveur. Le script s’exécute, va chercher des données, prend des décisions, compose une réponse, puis s’arrête. Ce qui traverse ensuite le réseau n’est déjà plus du PHP, mais du HTML prêt à être affiché.
Si l’on se place côté navigateur, tout est déjà « consommé ». Il ne reste aucune trace du passage côté serveur :
- même en ouvrant les outils du navigateur, on ne trouve aucune trace du PHP, simplement parce qu’il n’a jamais quitté le serveur
- ce qui s’affiche à l’écran correspond à quelque chose qui a déjà été préparé en amont, comme si la page arrivait « prête à l’emploi »
- d’une visite à l’autre, le contenu peut changer sans que l’on voie pourquoi, car tout dépend de ce qui s’est passé côté serveur, de l’utilisateur connecté, du moment ou des données disponibles
On peut le visualiser simplement : (1) une requête part du navigateur, arrive sur le serveur, (2) le serveur exécute le script PHP et accède à ses ressources (base de données, fichiers, sessions), puis (3) une réponse HTML est renvoyée vers le navigateur. Entre les deux, il y a une phase côté serveur, invisible pour le navigateur, celle où tout se joue.

C’est dans ce bloc central que PHP agit. C’est là que l’on décide : qui voit quoi, à quel moment, avec quelles règles. Et c’est précisément cette séparation qui apporte un avantage majeur en termes de sécurité. Les informations sensibles ne quittent jamais le serveur : identifiants de base de données, clés, règles métier, contrôles d’accès. Même en affichant le code source dans le navigateur, il est impossible d’y accéder, car elles n’ont jamais été envoyées.
Le navigateur ne manipule que la surface. Toute la logique, et surtout la logique métier, reste masquée derrière, côté serveur. Ce fonctionnement reste théorique tant que l’environnement n’est pas en place. C’est ce cadre que nous allons maintenant poser.
Mettre en place l’environnement PHP
PHP ne fonctionne jamais seul. Concrètement, on le rencontre dans divers contextes très différents.
- D’un côté, l’environnement local. On installe une stack pour travailler tranquillement sur sa machine : un serveur web comme Apache, PHP, souvent MySQL pour les données, et des outils comme PHPMyAdmin pour manipuler la base. Tout est regroupé, prêt à l’emploi, c’est un véritable serveur que l’on exécute sur sa propre machine et que l’on contrôle entièrement. C’est généralement par là que l’on commence, pour tester, casser, recommencer sans risque, Installer et configurer un serveur web en local.
- De l’autre côté, l’environnement distant. Le plus courant reste celui proposé par l’hébergeur : on ne touche pas directement à l’installation, on passe par une interface d’administration, souvent visuelle, comme Plesk. On y choisit une version de PHP, on active des options, on ajuste des paramètres sans forcément voir ce qui se passe derrière. Le projet vit alors dans un cadre déjà en place (voir l’illustration ci-dessous).
- … excepté les cas de serveurs dédiés où il nous faut alors installer et configurer nous-mêmes le serveur web, choisir les versions et ajuster PHP et ses extensions ; on ne se contente plus d’utiliser un environnement, on l’administre directement.

Dans tous les cas, PHP ne fonctionne jamais « seul ». Il s’inscrit dans un cadre qui influence directement son comportement. Prenons un cas concret. On exécute un script, tout semble fonctionner, puis une limite apparaît sans prévenir : un fichier trop volumineux, un temps d’exécution dépassé, une mémoire insuffisante. Rien ne change dans le code, et pourtant le comportement diffère d’un environnement à l’autre. Ce décalage vient souvent d’un fichier que l’on ne voit pas au premier abord : php.ini. Ce fichier central regroupe des réglages qui pilotent le fonctionnement de PHP. Quelques exemples très concrets :
[PHP]
;;;;;;;;;;;;;;;;;;;;;;;
; À propos de php.ini ;
;;;;;;;;;;;;;;;;;;;;;;;
; Le fichier d’initialisation de PHP, généralement appelé php.ini, est
; responsable de la configuration de nombreux aspects du comportement de PHP.
; taille maximale d’un fichier envoyé
upload_max_filesize = 10M
; taille maximale des données POST
post_max_size = 12M
; mémoire maximale utilisable par un script
memory_limit = 128M
; durée maximale d’exécution d’un script
max_execution_time = 30Ces lignes, on ne les écrit pas pour « faire fonctionner » le site au sens visible. Et pourtant, ce sont elles qui décident jusqu’où on peut aller. On s’en rend compte le jour où un fichier refuse de passer, où une page met trop de temps à répondre, ou coupe sans prévenir. C’est souvent à ce moment-là que l’on découvre que le problème ne vient pas du code, mais du cadre dans lequel il s’exécute. En local, tout passe. Une fois en ligne, ça bloque. Ou l’inverse. Rien n’a changé dans le fichier, mais l’environnement, lui, n’est pas le même. On retombe exactement sur ce que l’on a commencé à entrevoir dans la Part V : ce que fait une application ne dépend pas uniquement de ce que l’on écrit, mais aussi de l’endroit où elle tourne.

PHP propose aussi un moyen simple de rendre cet environnement visible : la fonction phpinfo(). Exécutée dans un fichier dédié, elle affiche un aperçu complet de la configuration active : version de PHP, extensions chargées, chemins utilisés, fichier php.ini réellement pris en compte, ainsi que de nombreuses variables internes. C’est souvent l’un des premiers réflexes pour analyser un comportement inattendu, comparer un environnement local et distant, ou vérifier qu’une option est bien active. Autrement dit, même lorsque le code reste inchangé, phpinfo() permet de comprendre concrètement dans quel cadre il s’exécute.
Avec PHP, on apprend donc progressivement à regarder aussi autour du code. Ces réglages font partie du projet, même s’ils restent discrets. On ne les voit pas toujours, mais ce sont eux qui fixent les limites du terrain.
Versions de PHP et compatibilité
En pratique, deux environnements ne sont jamais parfaitement identiques. Un projet peut fonctionner en local, puis se comporter différemment une fois déployé. Une des raisons tient aux versions de PHP utilisées.
- Pendant longtemps, beaucoup de projets ont tourné en PHP 5.6. Cette version a largement structuré l’écosystème, mais elle reposait sur des bases aujourd’hui considérées comme fragiles, notamment en termes de performance et de gestion des erreurs.
- Avec PHP 7, un cap est franchi. Le moteur est profondément optimisé, les performances font un bond significatif, et le langage gagne en rigueur. Beaucoup de projets encore actifs aujourd’hui reposent sur cette génération.
- PHP 8 poursuit cette évolution, avec un langage plus expressif, plus strict, et des outils qui permettent d’écrire un code plus lisible et plus fiable.
Dans la réalité, il n’est pas rare de rencontrer encore des sites en production sous PHP 5.6. Non pas par choix, mais parce qu’ils reposent sur des bases anciennes, parfois difficiles à faire évoluer.
| Version PHP | Période d’usage dominante | Fin de support | Repères clés |
|---|---|---|---|
| 5.6 | ~2014 à 2018 | 2018 | ancien socle, tolérant, performances limitées |
| 7.x | ~2016 à 2022 | 2022 | gros gain de performance, typage renforcé |
| 8.x | 2020 à aujourd’hui | actif | syntaxe moderne, plus strict, outils avancés |
C’est là que les écarts apparaissent. Un code peut fonctionner parfaitement dans un environnement moderne, et échouer ailleurs, ou inversement. Rien ne change dans le fichier, mais le contexte d’exécution, lui, n’est pas le même. Dans les interfaces d’hébergement, le choix de la version de PHP est souvent accessible directement. En local, il dépend de la stack installée. Dans les deux cas, ce réglage fait partie du projet, même s’il n’apparaît pas dans le code.

Écrire du PHP, c’est donc aussi composer avec une époque, un environnement, et parfois un héritage technique bien réel.
PHP dans le parcours : comprendre plutôt qu’apprendre
Ici, l’enjeu n’est pas d’entrer dans le détail du langage, mais de comprendre comment PHP s’inscrit concrètement dans un projet et dans un enchaînement réel. PHP possède sa propre syntaxe, ses variables, et ses fonctions. Mais il interagit également avec son environnement, à travers des variables spécifiques fournies par le serveur.
Dans cette série, on ne cherche donc pas à « apprendre PHP » au sens classique du terme. Il existe déjà des ressources très complètes pour cela, qui vont beaucoup plus loin dans le détail du langage. Ici, l’idée est différente : replacer PHP dans son usage réel, comprendre à quel moment il intervient, ce qu’il permet, et comment il s’intègre dans un projet. Si l’objectif est de se former au langage en lui-même, quelques références solides peuvent servir de point d’appui :
- la documentation officielle avec Manuel.php
- les tutoriels très progressifs de Grafikart
- les parcours structurés d’OpenClassrooms
Ces ressources permettent d’entrer en profondeur dans la syntaxe, les fonctions, les bonnes pratiques. De notre côté, on les garde en toile de fond. Ce que l’on fait ici ressemble davantage à ce que l’on a déjà traversé dans les chapitres précédents : on ne s’arrête pas sur chaque mot du langage, on observe à quel moment PHP intervient, ce qu’il débloque concrètement, et comment il s’insère dans un enchaînement réel. On avance donc comme sur le reste du parcours : par situations, par usages, en reliant les briques entre elles. PHP devient une étape dans un flux, pas une fin en soi.
Structurer un fichier PHP sans le mélanger au HTML
Au début, on a tendance à tout écrire au même endroit. On ouvre une page .php, on affiche du HTML, puis on insère du PHP au milieu, puis on revient au HTML. Ça fonctionne jusqu’au moment où il faut relire, corriger, ou faire évoluer. Regardons ce que ça donne quand tout est mélangé :
<?php
// Affichage direct sans préparation
?>
<!DOCTYPE html>
<html>
<head>
<title><?php echo "Discographie"; ?></title>
</head>
<body>
<h1>
<?php
// Données récupérées dans la page
$album = getAlbum(); // ex : Amon Düül II – Yeti
echo $album['title'];
?>
</h1>
</body>
</html>Le code fonctionne, mais la lecture devient rapidement difficile dès que la page s’étoffe. On mélange récupération de données et affichage. On peut faire un pas de côté et organiser les choses autrement :
<?php
// 1) On prépare les données en amont
$title = "Discographie";
$album = getAlbum(); // ['title' => 'Amon Düül II – Yeti']
?>
<!DOCTYPE html>
<html>
<head>
<!-- 2) Le HTML se contente d’afficher -->
<title><?= $title ?></title>
</head>
<body>
<h1><?= $album['title'] ?></h1>
</body>
</html>La lecture devient tout de suite plus fluide. En haut, le PHP prépare ce qui doit être affiché : il récupère, calcule, décide. Puis le HTML prend le relais et se contente de présenter ce qui a déjà été préparé. Ce n’est pas une règle stricte, mais une manière de garder le fil quand le projet commence à s’étoffer. On évite de se perdre, et on sait où regarder selon que l’on cherche un calcul ou un affichage. On peut presque le voir comme une mise en scène : le PHP travaille en coulisses, organise ce qui doit apparaître, puis le HTML arrive sur scène pour donner forme au résultat. Le navigateur, lui, n’assiste qu’à cette dernière étape, sans jamais voir ce qui s’est passé en amont.
Organiser la logique métier
On a déjà séparé ce qui se prépare et ce qui s’affiche. Une étape supplémentaire consiste maintenant à isoler la logique elle-même. À ce stade, une autre question se pose naturellement : où placer cette logique. Au début, tout passe dans la page. On récupère des données, on boucle, on affiche, on conditionne. Cela fonctionne, mais très vite, la page devient un point de passage obligé pour tout. Prenons un cas simple.
<?php
// page.php
$albums = getAlbums();
foreach ($albums as $album) {
if ($album['year'] < 1970) {
echo $album['title'] . ' (période Amon Düül)';
} else {
echo $album['title'] . ' (période Amon Düül II)';
}
}
?>Ce bloc illustre une première approche où la logique métier et l’affichage sont directement mêlés dans la page. Le code fonctionne, mais il centralise à la fois la récupération des données, la décision et la restitution. Ici, la page fait tout : elle récupère, elle décide, elle affiche. Tant que le contexte reste simple, cela tient. Mais dès que la logique évolue, que les règles changent, que d’autres pages ont besoin du même traitement, on commence à dupliquer, puis à se perdre. On peut alors déplacer cette logique ailleurs, sans changer ce que la page affiche.
<?php
// functions.php
function getPeriodLabel(array $album): string {
return $album['year'] < 1970
? 'période Amon Düül'
: 'période Amon Düül II';
}
?><?php
// page.php
require_once 'functions.php';
$albums = getAlbums();
foreach ($albums as $album) {
echo $album['title'] . ' (' . getPeriodLabel($album) . ')';
}
?>La page ne change presque pas visuellement, mais la lecture est différente. Elle ne porte plus la logique, elle s’appuie dessus. On reste volontairement simple ici. L’idée n’est pas de basculer dans une architecture complète, mais de sentir le mouvement : ce qui relève du calcul, de la décision, de la règle, peut vivre ailleurs que dans la page elle-même.
C’est dans la continuité directe de ce que l’on a vu plus haut. On ne découpe pas seulement le HTML et le PHP, on commence aussi à isoler ce qui fait sens dans le projet. Avec le temps, cela permet de faire évoluer une règle sans toucher à l’affichage, de réutiliser une même logique à plusieurs endroits, et surtout de garder une lecture claire quand le projet s’étoffe. On ne parle pas encore d’architecture complexe. On pose simplement une base : la page affiche, la logique décide, et les deux commencent à vivre à des endroits différents.
Charger une capacité, puis l’utiliser
Dans la pratique, un projet ne se limite donc jamais à un seul fichier. On retrouve rapidement plusieurs éléments qui collaborent : certains préparent les données, d’autres les structurent, d’autres encore les affichent ; d’autres encore portent des éléments mutualisés comme la connexion à la base de données, la gestion de session ou la configuration, tandis que certains restent dédiés à des besoins plus spécifiques comme des récupérations ciblées ou des fonctionnalités propres à une page. Cette organisation permet de mieux comprendre comment les différentes parties dépendent les unes des autres, et comment elles peuvent être chargées et utilisées au bon moment.
C’est précisément ce que l’on met en place ici : un fichier qui expose une capacité, et un autre qui l’utilise. Cette séparation rend le code plus lisible, plus réutilisable, et surtout plus simple à faire évoluer.
<?php
// renderAlbumRow.php
function renderAlbumRow(array $album): void {
$row = <<<HTML
<tr>
<td>{$album['artist']}</td>
<td>{$album['title']}</td>
<td>{$album['year']}</td>
</tr>
HTML;
echo $row;
}
?><?php
// présentation des albums
require_once 'renderAlbumRow.php';
echo '<h2>Discographie</h2>';
echo '<table border="1" cellpadding="5">';
echo '<tr><th>Artiste</th><th>Album</th><th>Année</th></tr>';
$albums = [
['artist' => 'Amon Düül II', 'title' => 'Yeti', 'year' => 1970],
['artist' => 'Amon Düül II', 'title' => 'Phallus Dei', 'year' => 1969],
['artist' => 'Amon Düül II', 'title' => 'Tanz der Lemminge', 'year' => 1971],
['artist' => 'Amon Düül II', 'title' => 'Carnival in Babylon', 'year' => 1972],
['artist' => 'Amon Düül II', 'title' => 'Wolf City', 'year' => 1972],
['artist' => 'Amon Düül II', 'title' => 'Vive La Trance', 'year' => 1973],
['artist' => 'Amon Düül II', 'title' => 'Hijack', 'year' => 1974],
['artist' => 'Amon Düül', 'title' => 'Psychedelic Underground', 'year' => 1969]
];
foreach ($albums as $album) {
renderAlbumRow($album);
}
echo '</table>';
?>Dans cet exemple, le fichier n’est pas un simple fragment HTML. Il expose une capacité réutilisable. require_once garantit qu’elle est disponible, une seule fois, avant toute utilisation. On ne dépend plus d’un fichier qui produit immédiatement du contenu, mais d’une logique déclenchée explicitement. Cela permet de positionner les éléments avec précision, de les réutiliser, et d’éviter les effets de bord.
Le fichier inclus devient une ressource fonctionnelle, et non plus un simple morceau de page.
Inclure, mutualiser, organiser : penser en modules
Une fois cette première séparation en place, on peut aller plus loin : au-delà d’une simple capacité réutilisable, comment organiser l’ensemble du projet entre éléments partagés et éléments spécifiques sans se perdre ni dupliquer ? Autrement dit, où vivent les dépendances communes (configuration, base, session) et où se placent les besoins propres à une page (récupérations ciblées, fonctionnalités dédiées). C’est là que des instructions comme include, require, _once, puis plus tard autoload, prennent un autre sens. Elles ne servent pas seulement à « charger des fichiers ». Elles traduisent des choix très concrets : ce dont la page a besoin, ce qu’elle peut tolérer, et la manière dont le projet va évoluer.
En prenant un peu de recul, ces instructions que l’on croise partout en PHP cessent d’être des détails techniques pour devenir quelque chose de beaucoup plus pragmatique : elles expriment les dépendances du code, sa tolérance aux manques, et sa capacité à évoluer dans le temps. Prenons une page issue de l’application Music, dédiée à l’affichage d’albums. Cet extrait, volontairement simple, sert de point de départ.
<?php
// index.php
/* ==============================
Fondations du système
============================== */
require_once 'config.php';
require_once 'db.php';
require_once 'functions.php'; // ex : getAlbums
/* ==============================
Affichage de la page
============================== */
include 'header.php';
echo '<h2>Albums</h2>';
/* ==============================
Récupération des données
============================== */
$albums = getAlbums();
/* ==============================
Rendu des données
============================== */
foreach ($albums as $album) {
echo $album['artist'] . ' – ' . $album['title'] . '<br>';
}
/* ==============================
Éléments complémentaires
============================== */
include_once 'filters.php';
include 'footer.php';
?>À première vue, rien ne semble particulier. Pourtant, on distingue déjà ce qui fait tenir la page… et ce qui peut disparaître sans tout remettre en cause. Ici, on n’inclut pas simplement des fichiers, on affirme une intention. Certains éléments sont indispensables : configuration, base, fonctions métier. Sans eux, la page ne produit rien de fiable. require_once devient un contrat de fiabilité, on préfère arrêter net plutôt que de continuer avec des données incohérentes.
Plus bas, la logique change. Les include ajoutent des couches utiles mais non vitales. Si un fichier manque, la page continue. Les données restent justes, seule une partie de l’interface disparaît. include devient une tolérance maîtrisée. Le suffixe _once impose une règle essentielle : une brique ne doit être chargée qu’une seule fois. Cela évite les redéfinitions et les effets de bord. Le _once impose une règle simple et essentielle : certaines briques ne doivent exister qu’une seule fois dans l’exécution.
À ce stade, on peut résumer : ce qui est indispensable, ce qui est toléré, et ce qui doit rester cohérent quand le projet évolue. Ces notions ne sont pas de simples variantes techniques : elles décrivent trois intentions ; la dépendance réelle du code, sa tolérance aux manques, et sa capacité à évoluer.
- require_once, poser la base : ce sans quoi la page ne tient pas. On charge la configuration, la connexion, les fonctions métier. S’il manque une brique, on arrête : continuer produirait un résultat incohérent. C’est un contrat de fiabilité.
- include, ajouter sans bloquer : ce qui enrichit sans conditionner. En-tête, modules, blocs d’interface… s’ils disparaissent, la page reste utilisable. On accepte une dégradation contrôlée sans toucher au cœur métier.
- _once, garantir l’intégrité : ce qui ne doit exister qu’une seule fois. On évite redéfinitions et effets de bord. Ce n’est pas une optimisation, mais une sécurité structurelle : une même brique n’est chargée qu’une seule fois.
Au final, l’idée reste simple : ce qui est indispensable, ce qui est toléré, et ce qui doit rester cohérent quand le projet grandit.
Se repérer dans les chemins en PHP
Lorsque l’on commence à organiser le code sur plusieurs fichiers, une difficulté apparaît rapidement : savoir où PHP va réellement chercher les fichiers. Pour bien comprendre ce qui se passe, il faut poser un cadre simple dès le départ. Le navigateur manipule des URL, visibles et partageables, alors que PHP travaille avec des chemins sur le système de fichiers du serveur. On se retrouve ainsi avec deux représentations d’une même ressource, qui se ressemblent en apparence, mais qui ne sont ni utilisées au même moment, ni interprétées de la même manière.
// Exemple d’équivalence entre URL (navigateur) et chemin (serveur)
https://monsite.com/tmpl/header.php
// Côté serveur, PHP ne voit pas l’URL mais le chemin réel correspondant
/var/www/vhosts/monsite.com/httpdocs/tmpl/header.phpDès que l’on inclut un fichier, il ne faut plus raisonner en URL mais en chemin système, en se plaçant du point de vue de PHP. Tant que l’on reste sur des chemins relatifs, c’est-à-dire construits à partir du fichier appelant, le comportement reste généralement cohérent et prévisible.
<?php
// index.php
include 'header.php';
// PHP cherche header.php dans le même dossier que index.php
// /var/www/site/index.php
// /var/www/site/header.php
include 'tmpl/header.php';
// PHP cherche header.php dans le dossier tmpl situé au même niveau
// /var/www/site/index.php
// /var/www/site/tmpl/header.php
?>PHP cherche le fichier par rapport au script courant. Ce fonctionnement est implicite et reste fiable tant que le fichier est exécuté directement, avec une structure simple. On parle alors de chemin relatif, c’est-à-dire d’un chemin dont le point de départ dépend du script initial exécuté par le serveur. Cette logique peut sembler évidente au début, mais elle devient moins lisible dès que les fichiers s’incluent entre eux. Le point de départ ne correspond plus forcément au fichier dans lequel le include est écrit, mais au script principal. Prenons une structure simple :
/ index.php
|__ tmpl/
| |_ header.php
|__ php/
|_ config.phpSi index.php inclut tmpl/header.php, puis que header.php inclut ../php/config.php, l’intention semble claire. Pourtant, PHP ne part pas de header.php, mais de index.php. Le chemin relatif est donc interprété depuis la racine d’exécution, ce qui peut conduire à un décalage. Pour éviter cette ambiguïté, on peut s’appuyer sur le fichier lui-même grâce à __DIR__, qui fixe explicitement le point de départ au dossier dans lequel le code est écrit, et ce quelque soit l’imbrication des fichiers entre eux.
<?php
// tmpl/header.php
// Inclusion fiable du fichier de configuration
include __DIR__ . '/../php/config.php';
// /var/www/site/tmpl/../php/config.php
// → /var/www/site/php/config.php
?>Dans cette écriture, le segment '/../' peut surprendre. Il ne s’agit pas d’une erreur, mais d’une construction de chemin. __DIR__ fournit déjà un chemin absolu se terminant par un dossier (/var/www/site/tmpl). En ajoutant '/../php/config.php', on indique simplement : « remonter d’un niveau, puis entrer dans php/ ».
// en partant du dossier courant
/var/www/site/tmpl
// on applique le déplacement
/var/www/site/tmpl/../php/config.phpLa présence du / avant .. est simplement liée à la concaténation : on ajoute un sous-chemin (/../php/...) à un chemin existant. Écrire seulement ../php/... fonctionnerait aussi si l’on gère correctement les séparateurs, mais la forme __DIR__ . '/../php/... reste la plus claire et la plus cohérente pour construire un chemin absolu à partir d’un dossier connu. Le segment .. signifie « dossier parent ». Le système de fichiers résout alors ce chemin en supprimant tmpl/.. :
/var/www/site/tmpl/../php/config.php
→ /var/www/site/php/config.phpAprès avoir stabilisé les chemins à partir du fichier lui-même avec __DIR__, une autre approche consiste à s’appuyer sur un point d’ancrage côté serveur, via $_SERVER['DOCUMENT_ROOT']. Ici, on ne part plus du fichier, mais de la racine du site telle qu’elle est configurée sur le serveur.
<?php
// Depuis n'importe quel fichier du projet
include $_SERVER['DOCUMENT_ROOT'] . '/php/config.php';
// /var/www/vhosts/monsite.com/httpdocs/php/config.php
?>Dans cette écriture, le chemin est construit à partir de la racine publique du site. Contrairement au relatif, il ne dépend pas du script appelant. Contrairement à __DIR__, il ne dépend pas non plus de l’emplacement du fichier dans l’arborescence, mais d’un repère global côté serveur. Cela permet de retrouver un même fichier depuis plusieurs points d’entrée, tant que l’on reste dans le périmètre du site.
Cette question prend une autre dimension dès que l’on change d’environnement, ce qui arrive très vite entre un poste local et un serveur de production. Le point clé n’est pas seulement le chemin, mais le repère utilisé.
local : /var/www/site/
production : /var/www/vhosts/monsite.com/httpdocs/Avec $_SERVER['DOCUMENT_ROOT'], on s’appuie sur la racine publique du site. Ce repère existe en local comme en production, même si sa valeur change.
<?php
// Depuis n'importe quel fichier du site
include $_SERVER['DOCUMENT_ROOT'] . '/php/config.php';
// local : /var/www/site/php/config.php
// production : /var/www/vhosts/monsite.com/httpdocs/php/config.php
?>Le code reste identique, seul le chemin réel évolue selon l’environnement. C’est précisément l’intérêt de DOCUMENT_ROOT : un point d’ancrage stable à l’échelle du site. La situation évolue dès que l’on introduit des sous-domaines et des ressources partagées.
principal : /var/www/vhosts/monsite.com/httpdocs/
app : /var/www/vhosts/monsite.com/subdomains/app/httpdocs/
shared : /var/www/vhosts/monsite.com/subdomains/shared/Dans ce cas, DOCUMENT_ROOT ne suffit plus toujours, car chaque sous-domaine possède sa propre racine. On introduit alors un repère plus haut dans l’arborescence, côté serveur.
<?php
// Accès à une ressource partagée entre plusieurs sous-domaines
include_once $_SERVER['HOME'] . '/subdomains/shared/php/config.php';
// /var/www/vhosts/monsite.com/subdomains/shared/php/config.php
?>Ici, on ne se base plus sur un site, mais sur la structure globale du serveur. HOME permet de remonter à un niveau commun, au-dessus des différents points d’entrée, pour accéder à des fichiers mutualisés. On distingue ainsi deux usages complémentaires : DOCUMENT_ROOT pour travailler à l’échelle d’un site, et HOME pour accéder à des ressources partagées entre plusieurs applications.
Un même fichier peut alors être accessible depuis plusieurs racines différentes. Un chemin correct dans un contexte peut devenir incorrect dans un autre, sans que le code ait changé. Pour stabiliser ces différences, on s’appuie sur des points d’ancrage clairement identifiés.
| Référence | Point de départ | Exemple |
|---|---|---|
__DIR__ |
fichier courant | /var/www/…/page |
DOCUMENT_ROOT |
racine du site | /var/www/…/httpdocs |
HOME |
racine serveur | /var/www/… |
Lorsque les chemins sont construits dynamiquement, une dernière question apparaît : le fichier visé existe-t-il réellement à cet emplacement. Une solution consiste à employer realpath() qui retourne le chemin absolu réel si le fichier existe, et false dans le cas contraire. On passe ainsi d’un simple calcul de chemin à une vérification concrète avant inclusion.
<?php
$path = realpath(__DIR__ . '/../tmpl/header.php');
// /var/www/vhosts/monsite.com/httpdocs/tmpl/header.php
if ($path !== false) {
include $path;
}
?>À ce stade, les éléments s’articulent de manière cohérente. PHP ne manipule pas des URL mais des chemins réels. Les chemins relatifs facilitent l’écriture mais restent sensibles au contexte. Les chemins absolus stabilisent l’exécution en s’appuyant sur un point d’ancrage. L’environnement influence directement ces choix, et certaines fonctions permettent d’ajouter un niveau de sécurité.
Ce travail est directement lié à la fiabilité du projet. Un chemin mal maîtrisé entraîne des erreurs difficiles à identifier. Un chemin bien construit permet au code de rester cohérent quel que soit l’environnement.
Entrées de données : $_GET, $_POST, $_FILES, php://input
Une fois le code structuré, une autre dimension devient centrale : les données qui entrent dans l’application. La page ne se contente plus d’afficher ; elle reçoit, interprète et renvoie. Un lien, un formulaire, un envoi de fichier, ou un appel JavaScript déclenchent tous le même phénomène : des informations arrivent côté serveur.
- GET : données visibles dans l’URL, utilisées pour des paramètres simples, partageables ou bookmarkables, souvent liées à la navigation ou à la consultation (ex. afficher un artiste comme Amon Düül II).
- POST : données envoyées dans le corps de la requête, non visibles dans l’URL, adaptées aux formulaires et aux informations plus sensibles ou plus volumineuses.
- FILES : données binaires transmises via formulaire, utilisées pour l’envoi de fichiers (images, pochettes d’album…), avec des métadonnées associées.
- php://input : flux brut de la requête, utilisé principalement avec JavaScript (fetch, API), lorsque les données sont envoyées en JSON ou dans un format structuré.
Ces différentes méthodes ne changent pas la nature des données, mais le chemin qu’elles empruntent pour arriver jusqu’à PHP. Ces données sont également visibles dans les outils de développement du navigateur, notamment dans l’onglet Réseau. Selon le type de requête, elles apparaissent soit dans les paramètres d’URL, soit dans le corps de la requête, ce qui permet d’observer concrètement leur transmission.
$_GET : paramètres transmis dans l’URL
Avant de passer au code, posons le cadre. Une requête de type GET permet d’envoyer des paramètres directement dans l’URL, que PHP rend ensuite accessibles via $_GET. Dans une URL, certains caractères ne peuvent pas être utilisés tels quels. Ils sont donc encodés. Un espace devient %20, et les caractères accentués sont convertis en UTF-8 puis représentés en hexadécimal. Cet encodage est automatique côté navigateur et décodé par PHP.
Lorsque l’on construit une URL côté PHP, il est recommandé d’encoder explicitement les valeurs, par exemple avec urlencode() ou rawurlencode() :
<?php
$artist = 'Amon Düül II';
$url = 'infos.php?artist=' . urlencode($artist);
// infos.php?artist=Amon+D%C3%BC%C3%BCl+II
// Variante RFC3986 (espaces en %20)
$url2 = 'infos.php?artist=' . rawurlencode($artist);
// infos.php?artist=Amon%20D%C3%BC%C3%BCl%20II
?>Amon Düül II
→ Amon%20D%C3%BC%C3%BCl%20IILe mécanisme est alors simple : une page construit une URL, une autre lit les paramètres. Lorsque plusieurs paramètres sont transmis, ils sont enchaînés avec & (esperluette). PHP reconstruit automatiquement un tableau associatif :
infos.php?artist=Amon%20D%C3%BC%C3%BCl%20II&album=Yeti
$_GET['artist'] = 'Amon Düül II';
$_GET['album'] = 'Yeti';Illustration complète, côté client puis côté serveur :
<!-- index.php : envoi de la requête GET -->
<a href="infos.php?artist=Amon%20D%C3%BC%C3%BCl%20II&album=Yeti">Voir les infos</a><?php
// infos.php : réception et utilisation
$artist = $_GET['artist'] ?? 'inconnu';
$album = $_GET['album'] ?? 'inconnu';
echo "Votre requête : $artist, $album";
// Votre requête : Amon Düül II, Yeti
?>Ici, l’URL devient un vecteur de données : l’information est visible, partageable et directement exploitable via $_GET. Ce même principe de transmission par paires clé/valeur se retrouve avec les formulaires, mais sans passer par l’URL.

$_POST : données envoyées par formulaire
La même information peut être transmise par un formulaire. Le canal change, pas la logique : on ne passe plus par l’URL, mais par le corps de la requête. Les champs du formulaire sont sérialisés puis encodés selon le type d’envoi. Par défaut (application/x-www-form-urlencoded), le navigateur applique un encodage proche de celui vu avec GET (espaces en +, caractères UTF-8 encodés), puis PHP décodera automatiquement ces valeurs dans $_POST. Exemple dans le contexte de l’application Music, avec une recherche d’artiste et d’album.
<!-- index.php : envoi POST -->
<form method="post" action="infos.php">
<input type="text" name="artist" value="Amon Düül II">
<input type="text" name="album" value="Yeti">
<button type="submit">Rechercher</button>
</form>Côté serveur, PHP reconstruit un tableau associatif à partir des paires clé/valeur transmises.
<?php
// infos.php : réception POST
$artist = $_POST['artist'] ?? 'inconnu';
$album = $_POST['album'] ?? 'inconnu';
echo "Recherche : $artist, $album";
// Recherche : Amon Düül II, Yeti
?>Si plusieurs champs sont envoyés, ils sont simplement additionnés dans la requête, sans apparaître dans l’URL. PHP les expose tous dans $_POST avec la même clé que côté formulaire (name).

$_FILES : envoi de fichiers via formulaire
Lorsque l’on doit envoyer des données non textuelles (images, pochettes d’album), on change d’encodage de formulaire avec multipart/form-data. Le navigateur segmente alors la requête en parties et transmet, pour chaque fichier, des métadonnées (nom, type, taille) ainsi qu’un fichier temporaire côté serveur. PHP reconstruit ces informations dans $_FILES. Exemple dans le contexte de l’application Music, avec l’envoi d’une pochette pour Yeti.
<!-- index.php : envoi d’un fichier -->
<form method="post" enctype="multipart/form-data" action="upload.php">
<input type="hidden" name="artist" value="Amon Düül II">
<input type="hidden" name="album" value="Yeti">
<input type="file" name="cover">
<button type="submit">Envoyer la pochette</button>
</form><?php
// upload.php : réception fichier
$artist = $_POST['artist'] ?? 'inconnu';
$album = $_POST['album'] ?? 'inconnu';
$file = $_FILES['cover'] ?? null;
if ($file && $file['error'] === UPLOAD_ERR_OK) {
$tmp = $file['tmp_name'];
$name = $file['name'];
$size = $file['size'];
echo "Upload pour $artist, $album : $name ($size octets)";
}
?>Les champs de type hidden ne sont pas affichés dans le formulaire, mais ils sont bien envoyés avec la requête. Ils permettent de transmettre des informations déjà connues, comme ici l’artiste et l’album, sans intervention de l’utilisateur. Ces données restent visibles dans les outils de développement et ne doivent pas être considérées comme sécurisées.

Ici, on ne reçoit plus une simple chaîne, mais un ensemble structuré : name, type, size, tmp_name, error. Le fichier est d’abord stocké dans un emplacement temporaire, puis peut être déplacé de manière contrôlée avec move_uploaded_file()
php://input : lecture du flux brut (JSON / API)
Dans un contexte JavaScript (API, fetch), les données sont souvent envoyées au format JSON avec un en-tête Content-Type: application/json. Elles ne transitent alors ni par $_GET ni par $_POST, mais par le flux brut accessible via php://input.
// client : envoi JSON
fetch('infos.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json; charset=utf-8' },
body: JSON.stringify({ artist: 'Amon Düül II', album: 'Yeti' })
});<?php
// infos.php : réception JSON
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
$artist = $data['artist'] ?? 'inconnu';
$album = $data['album'] ?? 'inconnu';
echo "API : $artist, $album";
?>Le principe reste identique (paires clé/valeur), mais le canal change : ici, c’est le corps brut de la requête qui est lu, puis décodé. Cette approche est adaptée aux échanges structurés entre client et serveur (API). Les données ne transitent ni par $_GET ni par $_POST, mais sont lues directement dans le flux brut php://input. Même clé, même donnée, autre point d’entrée. À ce stade, le rôle de PHP évolue. Il ne produit plus seulement du HTML ; il devient un point de passage pour les données. Ce qui compte n’est pas la variable en elle-même, mais le chemin qu’elle emprunte pour arriver jusqu’au serveur.

En résumé sur les entrées de données
Dans la pratique, ces approches sont rarement mélangées de cette manière, mais cette écriture permet de comprendre… Une même page peut accepter artist via l’URL, un formulaire ou une requête JSON. On rencontre alors des écritures qui unifient ces sources.
<?php
$artist = $_POST['artist']
?? $_GET['artist']
?? 'inconnu';
echo "Artiste : $artist";
?>Ou une forme plus explicite :
<?php
$artist = isset($_POST['artist'])
? $_POST['artist']
: (isset($_GET['artist']) ? $_GET['artist'] : 'inconnu');
?>Ces écritures servent surtout à montrer qu’une même donnée peut arriver par plusieurs chemins. Avec le temps, ces flux sont séparés et structurés. Mais à ce stade, ils permettent de comprendre concrètement comment les données circulent dans une application PHP. Chaque méthode transporte les données différemment, mais toutes deviennent visibles dans l’onglet Réseau du navigateur.
Déboguer en PHP : comprendre ce que l’on ne voit pas
C’est souvent une situation déroutante au début. Tout semble correct côté JavaScript, aucune erreur visible dans la console… et pourtant, la requête échoue. On se retrouve face à un statut 500, sans explication immédiate. La raison est simple : contrairement à JavaScript, les erreurs PHP ne s’affichent pas dans la console du navigateur. Elles se produisent côté serveur, et restent invisibles tant que l’on ne va pas les chercher. Prenons un cas très courant. Une requête fetch est envoyée, mais la réponse ne correspond pas à ce que l’on attend.
fetch('api.php')
.then(res => res.json())
.then(data => console.log(data));
Dans la console, on peut voir une erreur, mais elle reste vague :

Ce message ne vient pas directement de PHP. Il indique simplement que JavaScript a reçu quelque chose qu’il ne comprend pas. Souvent, cela signifie que PHP a généré une erreur… et que celle-ci a été renvoyée sous forme de HTML. Pour comprendre ce qui se passe réellement, il faut changer de point de vue. Dans les outils de développement du navigateur, l’onglet Network devient essentiel. En inspectant la requête, on peut voir la réponse brute du serveur : message d’erreur PHP, warning, ou sortie inattendue.

Côté PHP, on peut aussi activer temporairement l’affichage des erreurs :
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
?>Cela permet de voir immédiatement ce qui bloque, surtout en phase de développement.

Un autre point très fréquent concerne les sorties parasites. Un espace avant <?php, un echo oublié, ou un warning suffisent à corrompre une réponse JSON.
<?php
// Mauvaise pratique
echo "debug";
echo json_encode(['status' => 'ok']);
// Ici, la réponse n’est plus un JSON valide. JavaScript reçoit un mélange de texte et de données, et l’analyse échoue.
?>Enfin, certaines erreurs ne viennent pas du code lui-même, mais du contexte : fichier introuvable, permissions incorrectes, dépendance manquante. Dans ces cas, PHP ne renvoie pas une erreur claire côté navigateur, mais un statut générique. Avec l’habitude, on apprend donc à adopter un réflexe simple : quand la console JavaScript ne suffit plus, on remonte côté serveur.
Ce déplacement de regard est essentiel. Il permet de comprendre que tout ne se joue pas dans le navigateur, et que PHP, parce qu’il s’exécute ailleurs, demande une manière différente de déboguer.
Gérer l’état côté serveur : sessions, cookies et continuité
Le protocole HTTP ne conserve aucune mémoire. Chaque requête arrive comme si elle était la première. Dans la pratique, cela pose rapidement un problème. Comment savoir si un utilisateur est connecté ? Comment conserver une information d’une page à l’autre ?
Les sessions apportent une réponse simple. Elles permettent de stocker des informations côté serveur et de les retrouver lors des requêtes suivantes. C’est sur ce mécanisme que reposent la plupart des systèmes d’authentification. Dans les faits, ce mécanisme s’appuie très souvent sur un cookie de session côté navigateur. Celui-ci ne contient pas les données sensibles, mais un identifiant permettant au serveur de retrouver la session correspondante. C’est ce lien discret entre le navigateur et le serveur qui rend possible la continuité.
À ce stade, on reste volontairement sur le principe. Dès que l’on cherche à aller plus loin, les questions deviennent vite plus concrètes : durée de session, sécurité, gestion des identifiants, attaques possibles, invalidation… Ces sujets sont largement détaillés dans plusieurs articles dédiés du blog Puce & Média, qui permettent d’approfondir chaque aspect :
- Comprendre les sessions PHP : fondements, usages et cas particuliers
- Connexion sécurisée : PHP, sessions, mot de passe et attaques courantes
- Sessions PHP : sécuriser les identifiants, durées et invalidations
- Sessions PHP – Contextes modernes et intégrations avancées
- Sessions PHP – Authentification et gestion avancée
Certains de ces points prennent encore plus de sens dès que l’on introduit une base de données, ce que nous abordons dans la Part VIII et la relation PHP/MySQL. Ici, l’objectif est simplement de poser le mécanisme, pour comprendre comment l’état peut être maintenu d’une requête à l’autre.
Répondre à JavaScript : AJAX, XMLHttpRequest et Fetch
Une fois les données reçues, il faut aussi savoir y répondre. C’est ici que PHP rejoint ce que nous avons vu en Part IV sur les interactions côté navigateur, notamment autour de XMLHttpRequest. Dans ce dialogue, la réponse doit être clairement interprétable. PHP peut alors produire des données directement exploitables par JavaScript, ce qui permet un échange fluide, sans rechargement de page.

Concrètement, une requête part du navigateur, PHP traite, puis renvoie une réponse que JavaScript va utiliser pour mettre à jour l’interface. Ici, l’enjeu n’est plus la mécanique HTTP (vue en Part V), ni les en-têtes (vus juste après), mais la forme et la fiabilité de l’échange côté PHP. Pour approfondir les aspects protocolaires, on pourra se référer à deux lectures complémentaires : Les Méthodes HTTP dans REST : Une approche pratique et Statuts et Gestion HTTP : Comprendre les Codes de Statut et Leur Utilisation Efficace.
Structurer la réponse : données lisibles et exploitables
Un premier point très concret concerne la structure de la réponse. Côté JavaScript, on ne manipule pas du HTML, mais des données. Il devient donc utile de renvoyer des structures prévisibles. Cette structure simple permet à JavaScript de savoir immédiatement où regarder et comment exploiter la réponse, sans avoir à deviner ni à reconstruire le contenu.
<?php
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'status' => 'ok',
'data' => [
'artist' => [
'name' => 'Amon Düül II'
],
'album' => [
'title' => 'Yeti',
'year' => 1970,
'cover' => 'yeti-cover.jpg'
]
]
]);
?>Gérer les erreurs : statut, message et cohérence
Un second point concerne la gestion des erreurs. Une requête peut réussir techniquement, mais échouer fonctionnellement. PHP peut alors renvoyer une réponse explicite. Côté JavaScript, cela permet de distinguer clairement un cas normal d’un cas d’erreur, sans ambiguïté. Côté PHP, cette gestion passe souvent par des conditions simples : selon le cas, on ajuste le code de réponse et le message associé. Le code HTTP donne un état technique à la réponse, tandis que le message précise le contexte fonctionnel.
<?php
header('Content-Type: application/json; charset=utf-8');
// Cas 1 : erreur serveur (ex. traitement impossible)
if ($erreurServeur ?? false) {
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => 'Erreur interne'
// ici les données...
]);
exit;
}
// Cas 2 : accès refusé
if ($accesRefuse ?? false) {
http_response_code(403);
echo json_encode([
'status' => 'error',
'message' => 'Accès refusé'
// ici les données...
]);
exit;
}
// Cas 3 : requête valide
http_response_code(200);
echo json_encode([
'status' => 'ok',
'message' => 'Requête traitée'
// ici les données...
]);
?>Comprendre le contexte : requête, cookies et contraintes
Un autre point, souvent invisible, entre en jeu : le contexte de la requête. Selon les cas, la requête peut transporter des cookies (sessions), des en-têtes spécifiques, ou être bloquée par des règles de sécurité comme la politique de même origine. Sans entrer dans les détails ici, cela explique des comportements fréquents :
- une session qui « disparaît » parce que les cookies ne sont pas envoyés
- une requête refusée car elle provient d’un autre domaine
- un appel qui fonctionne en local mais pas en production
Une dernière attention concerne la stabilité de la réponse, côté PHP, il est souvent utile de garder une réponse simple et déterministe. Une requête JavaScript attend une réponse claire, rapide, sans sortie parasite. Un echo inattendu, un espace en trop, ou un warning peuvent suffire à casser l’échange.
Ce mécanisme prolonge ce que l’on a vu jusqu’ici. La page ne se contente plus de s’afficher, elle dialogue en continu avec le serveur. PHP devient alors un point de passage pour des échanges courts, précis, souvent répétés. C’est ce qui permet aujourd’hui de construire des interfaces plus dynamiques, où tout ne passe plus par un rechargement complet de la page.
Réponse PHP et en-têtes HTTP
Ici, il ne s’agit plus de la réponse elle-même, mais de tout ce qui l’entoure au moment de la requête. Au-delà du contenu lui-même, PHP agit aussi sur la manière dont la réponse est interprétée. Concrètement, on ne revient pas ici sur HTTP lui-même (déjà vu en Part V, notamment dans « Le protocole HTTP et HTTPS : comment les navigateurs dialoguent avec les serveurs » et « Les réponses HTTP et les codes de statut »), mais sur ce que PHP en fait au moment de répondre, au moment précis où le serveur fabrique ce qui va repartir vers le navigateur.
Ce moment est souvent invisible. Pourtant, c’est là que tout se joue. PHP ne se contente pas d’envoyer du contenu, il donne aussi des indications sur la manière dont ce contenu doit être compris. Avant même d’écrire une ligne de code, deux points très concrets méritent d’être posés, car ils sont souvent à l’origine de comportements incompréhensibles :
- le fichier PHP doit être encodé en UTF-8 sans BOM
- aucun caractère ne doit être envoyé avant <?php (pas d’espace, pas de retour à la ligne, pas de caractère invisible)
Dans le cas contraire, PHP envoie déjà des données au navigateur avant même d’avoir pu définir les en-têtes. Résultat : erreurs du type « headers already sent », ou réponses JSON corrompues. Considérons maintenant un cas très courant : renvoyer du JSON.
<?php
// api.php
$data = ['album' => 'Yeti'];
// On indique explicitement le type de réponse
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data);
?>Sans cette ligne header(…), le navigateur recevrait bien quelque chose, mais sans vraiment savoir comment l’interpréter. JavaScript pourrait lire une simple chaîne de caractères, là où l’on attend une structure exploitable. Ici, PHP ne change pas le contenu. Il précise la manière de le lire. C’est comme une indication ajoutée à la réponse : « ce que tu reçois n’est pas du HTML, c’est du JSON ». On peut aussi agir sur l’état de la réponse, sans revenir sur le détail des codes HTTP.
<?php
// ressource introuvable
http_response_code(404);
echo json_encode([
'status' => 'error',
'message' => 'Album introuvable',
'data' => [
'artist' => 'Amon Düül II',
'album' => null
]
]);
?>Dans ce cas, PHP ne se contente pas d’envoyer un message. Il indique aussi que quelque chose ne s’est pas déroulé comme prévu. Le contenu et le statut vont dans le même sens. Dans la pratique, ces deux gestes deviennent très vite naturels. On précise ce que l’on envoie, et on indique dans quel état se trouve la réponse. Ce sont des détails discrets, mais essentiels pour éviter toute ambiguïté.
C’est souvent à ce moment que le lien avec JavaScript devient évident. Sans ces indications, le dialogue fonctionne… mais reste fragile. Avec elles, l’échange devient clair, fiable, et exploitable.
Rôle du serveur web : Apache, .htaccess, permissions
Pour comprendre complètement ce que PHP reçoit, il faut revenir un instant sur ce qui se passe juste avant lui, dans la continuité directe de la Part V. Comme vu précédemment, PHP ne travaille jamais seul : il s’exécute dans un cadre d’exécution, et ce cadre, c’est le serveur web. Concrètement, avant même que PHP n’intervienne, c’est le serveur qui reçoit la requête, décide quoi en faire, et oriente vers le bon fichier.
Dans les environnements basés sur Apache, une partie de ce comportement peut être ajustée directement depuis le projet, grâce au fichier .htaccess. On ne modifie pas le serveur global, on agit localement, au niveau d’un dossier. (Voir « Le rôle du fichier .htaccess » dans la Part V.). On ne reprend pas ici le détail des règles serveur. Ce qui nous intéresse côté PHP, c’est l’effet concret de cette couche en amont.
Le point vraiment utile à garder est le suivant : le serveur peut transformer la requête avant que PHP ne la voie. Par exemple, une URL « propre » liée à l’application Music peut être réécrite en paramètres directement exploitables par PHP.
# /album/amondul-ii/yeti
# → album.php?artist=Amon%20D%C3%BC%C3%BCl%20II&album=Yeti
RewriteRule ^album/([a-zA-Z0-9-]+)/([a-zA-Z0-9-]+)$ album.php?artist=$1&album=$2 [L]Côté PHP, on ne récupère pas l’URL d’origine, mais le résultat déjà préparé. Comme détaillé dans l’article « Optimisation des APIs RESTful : URLs, bonnes pratiques », l’URL peut être pensée pour être lisible et structurée côté utilisateur, tout en étant transformée en paramètres exploitables côté serveur. PHP travaille alors normalement, sans se soucier de la réécriture effectuée en amont.
<?php
// URL demandée (lisible) : /album/amondul-ii/yeti
// présentée au script ous la forme de album.php?artist=Amon%20D%C3%BC%C3%BCl%20II&album=Yeti
// Voir Bonnes pratiques d’URL RESTful (cité précédemment)
$artist = $_GET['artist'] ?? '';
$album = $_GET['album'] ?? '';
echo "Album : $artist, $album";
// Album : Amon Düül II, Yeti
?>On travaille donc sur des données déjà interprétées par le serveur. Au-delà de la réécriture, d’autres effets peuvent apparaître : certaines décisions peuvent empêcher PHP de s’exécuter. Un accès protégé, des permissions incorrectes, ou une règle serveur bloquante, et le script ne sera tout simplement jamais appelé.
C’est là que se situe l’apport réel pour PHP : il ne reçoit jamais une requête « brute ». Elle a déjà été filtrée, transformée ou validée. Dans la pratique, cela explique des situations fréquentes : une variable $_GET vide à cause d’une réécriture mal configurée, un script inaccessible malgré un code correct, ou un comportement différent entre local et production.
On peut donc garder une lecture simple : le serveur prépare et oriente, PHP traite ce qu’il reçoit. Ce lien suffit ici pour comprendre comment ces règles impactent directement le comportement de nos scripts, sans revenir sur leur configuration détaillée.
Autoload, laisser PHP charger au bon moment
Jusqu’ici, nous avons vu comment charger des capacités (require, include) et comment organiser le code en modules. Nous avons aussi vu que le serveur prépare la requête avant que PHP ne la traite. Reste une question : comment faire évoluer ce chargement quand le projet grandit et que le nombre de fichiers explose, notamment avec une approche orientée classes ?
C’est ici qu’intervient une autre approche : l’autoload. Multiplier les require_once devient vite lourd, fragile et difficile à maintenir. Plutôt que de lister à l’avance tout ce dont le script aura besoin, on change de logique : on laisse PHP charger au moment où une classe est réellement utilisée. On ne dit plus à PHP « voici tout ce dont tu auras besoin », mais « si tu rencontres une classe, va la chercher ». Le chargement ne se fait plus en amont, mais au moment exact où il devient nécessaire. Cette logique prépare directement l’usage d’outils comme Composer, qui s’appuient précisément sur ce mécanisme pour gérer les dépendances.
<?php
// src/Album.php
// ici aucun include
class Album {
public function afficher(string $artist, string $title): void {
echo "Album : $artist, $title";
// ici les données (pochette, année, etc.)
}
}
?><?php
// index.php
// Avant (approche classique)
// require_once __DIR__ . '/src/Album.php';
// require_once __DIR__ . '/src/Artist.php';
// require_once __DIR__ . '/src/Cover.php';
// → chaque nouvelle dépendance doit être ajoutée manuellement
// Enregistre un autoloader simple basé sur le nom de la classe
spl_autoload_register(function ($class) {
// PHP tentera de charger automatiquement chaque classe utilisée
require __DIR__ . '/src/' . $class . '.php';
});
// Aucune inclusion explicite ici
// Si Album dépend d'autres classes (Artist, Cover...),
// elles seront chargées automatiquement au moment où elles sont utilisées
$album = new Album();
$album->afficher('Amon Düül II', 'Yeti');Dans ce cas, le chargement du fichier ne se fait plus de manière anticipée, en listant l’ensemble des dépendances en début de script. Il se produit au moment précis où la classe est instanciée. PHP détecte l’utilisation de MatchGame, appelle automatiquement la fonction d’autoload, puis charge le fichier correspondant.
Dans un projet plus structuré, cela permet d’éviter une accumulation de require_once difficiles à maintenir. On ne se préoccupe plus de savoir si une classe a été chargée, ni dans quel ordre, puisque ce mécanisme s’en charge au moment opportun. L’intérêt devient particulièrement visible lorsque le nombre de classes augmente. Le code reste lisible, centré sur les usages, et non sur la gestion des fichiers. On manipule des objets, des services, des entités, sans avoir à déclarer explicitement leur chargement.
Dans ce contexte, l’autoload ne remplace pas require, mais en modifie l’usage. Le chargement n’est plus une étape préalable systématique, il devient une conséquence naturelle de l’exécution du code. On change alors de logique. Ce mécanisme sera repris et automatisé dans le chapitre Dépendances et usage de Composer, qui s’appuie précisément sur ce principe pour charger les dépendances d’un projet.
Dépendances et usage de Composer
À mesure que les projets évoluent, certaines fonctionnalités reposent sur des bibliothèques externes. Plutôt que de copier du code ou de multiplier les fichiers manuellement, Composer permet de déclarer ces dépendances, de les installer et de maintenir la cohérence du projet.
Dans la pratique, cela permet d’intégrer rapidement des outils déjà éprouvés. Par exemple, PhpSpreadsheet permet de lire et générer des fichiers Excel, Dompdf de produire des documents PDF à partir de HTML, ou encore des bibliothèques de QR Code pour générer des images encodant des informations. Ces exemples sont parmi les plus courants, mais l’écosystème est bien plus large : gestion des emails (PHPMailer), manipulation d’images (Intervention Image), clients HTTP pour APIs (Guzzle), validation de données, authentification, etc. L’idée n’est pas de tout connaître, mais de comprendre que, dans la majorité des cas, une solution fiable existe déjà, évitant de réinventer des briques techniques éprouvées.
Ces briques ne remplacent pas le code, elles viennent compléter ce que PHP sait déjà faire, en apportant des capacités précises sans repartir de zéro. Concrètement, on décrit ce dont le projet a besoin dans un fichier, puis Composer se charge de récupérer les bonnes versions, de les organiser et de les rendre disponibles. Concrètement, cette logique se met en place en quelques étapes.. On initialise généralement cela avec une commande simple :
composer initqui génère un fichier composer.json décrivant le projet. On y retrouve par exemple :
{
"name": "mon-projet/demo",
"require": {
"php": ">=8.1",
"phpoffice/phpspreadsheet": "^3.5",
"dompdf/dompdf": "^3.1"
}
}À partir de là, un composer install permet de récupérer automatiquement les dépendances et de les rendre disponibles dans le projet. Sans entrer dans le détail ici, deux lectures complémentaires permettent d’approfondir selon le contexte :
Dans cette partie, on retient surtout l’idée : on ne code plus tout seul, on s’appuie sur un écosystème structuré, et Composer en devient le point d’entrée.
Relier PHP au reste : données, dépendances et continuité
Avec ces briques en place, PHP prend une autre dimension et ne se limite plus à produire des pages. Il devient un point de passage central entre plusieurs éléments : des données, des services, et des briques externes. D’un côté, il s’appuie sur des bibliothèques externes, souvent installées via Composer. Cela permet d’ajouter des fonctionnalités sans tout réécrire, tout en conservant un projet structuré, évolutif et maintenable. Voir à titre d’exemple « Maîtriser l’envoi d’e-mails avec PHPMailer ».
Dans la pratique, PHP est presque toujours relié à des sources de données : base de données, fichiers, APIs. C’est ce lien qui permet de stocker, récupérer, croiser et faire évoluer les informations. On sort alors progressivement d’un simple enchaînement de pages pour entrer dans une logique applicative. Les données ne sont plus seulement affichées : elles sont manipulées, organisées, et parfois partagées entre plusieurs parties du projet.
Cette évolution change profondément la manière de concevoir une application. PHP ne se contente plus de répondre à une requête, il orchestre un ensemble d’éléments qui interagissent entre eux.
Conclusion : de PHP vers les données
À ce stade, PHP apparaît comme un point de liaison : il reçoit une requête, traite des informations, s’appuie sur des dépendances, puis renvoie une réponse structurée. Il relie le navigateur, le serveur et les différentes briques du projet. Mais une question devient rapidement centrale : où vivent réellement ces données ? Jusqu’ici, elles ont été simulées, transmises ou manipulées ponctuellement. Dans une application réelle, elles doivent être stockées de manière fiable, structurée et interrogeable.
C’est précisément le rôle d’une base de données relationnelle. La prochaine étape du parcours consistera donc à comprendre comment organiser ces données avec MySQL : tables, relations, structure. Nous quitterons progressivement le flux de traitement pour entrer dans la logique de stockage. Dans un second temps, nous verrons comment PHP interagit avec MySQL, pour lire, écrire et transformer ces données dans une application complète. Ce sera l’objet de l’article suivant, où la relation entre PHP et MySQL prendra tout son sens.
Ce passage marque une étape importante : on ne manipule plus seulement des variables, mais un système de données durable, au cœur de l’application.
