L'erreur headers already sent

L'interpréteur PHP envoie les données au serveur web au fur et à mesure qu'il les écrit. Il ne sait pas si le serveur web les envoie au client en temps réel ou non (c'est rarement le cas, en général le server web utilise un buffer — mais PHP ne le sait pas). Il n'y a donc pas de « retour en arrière » possible. Cela se manifeste pour les modifications d'en-tête HTTP : comme les en-têtes HTTP sont au début du message, avant le HTML, il est impossible de les modifier avec header() une fois que du contenu a été écrit.

Un piège classique est de ne pas ouvrir le bloc d'instructions PHP au tout début du fichier :

 <?php
 
// Noter l'espace avant l'ouverture du bloc <?php 
header("Content-type: text/plain");
print 
"Coucou <strong>c'est moi</strong><br/>";
?>

Résultat (image) :

Encore plus vicieux : attention à l'inclusion de fichiers dont le bloc d'instructions est fermé avant la dernière ligne ! Exemple :

<?php
// Un fichier à inclure, avec des fonctions utiles
// (une seule ici)

function factorielle($n) {
    if (
$n == 0)
        return 
1;
    return 
$n factorielle($n 1);
}

// Noter la ligne en trop après la fermeture du bloc PHP :
?>

Ce fichier est inclus dans ce script, qui utilise ensuite header :

<?php

// Je commence par inclure mon fichier de fonctions utiles
require("fichier_inclus.php");

// J'utilise header
header("Content-type: text/plain");

echo 
"Contenu de ma page. <strong>Le code HTML est bien interprété</strong> : ";
echo 
"l'appel à <code>header</code> n'a pas eu d'effet.";

?>

Résultat (image) :

Remarque importante sur la configuration de PHP

Attention : les versions relativement récentes de PHP utilisent par défaut un buffer local, qui permet d'envoyer des en-têtes même après avoir commencé à écrire, à condition qu'on n'ait pas trop écrit. Cela revient à masquer des erreurs, qui peuvent avoir des conséquences imprévues quand on s'y attend le moins : une page peut fonctionner parfaitement pendant des années, jusqu'au jour où les données qu'elle doit envoyer sont plus grosses que d'habitude… Malheureusement il ne semble pas possible d'avoir en même temps le buffer local et un warning si on modifie les en-têtes trop tard. Il est donc fortement conseillé de désactiver le buffer local (option output_buffering = Off dans le php.ini) sur votre serveur de développement personnel, si vous en utilisez un.