“Headers already sent” en PHP

Vous êtes en train de tester votre application PHP puis rencontrez le message d’avertissement suivant :

Warning: Cannot modify header information – headers already sent

Quelle est la signification de ce message d’avertissement ? Et comment l’éviter ?

Présentation du problème

Tout d’abord, il faut bien comprendre de quoi est composée une requête de réponse HTTP : Celle ci est tout d’abord composée de l’entête (informations complémentaires retournées par le serveur) puis ensuite du contenu (le code HTML de la réponse, si présent).

Rentrons maintenant dans la pratique :

<html><head><title>Mon titre</title></head>
<body>
<?php
header('Content-Type: text/html; charset=UTF-8');
?>
</body>
</html>

Le code précédent définit à la ligne 4 une valeur (Content-Type) de l’entête de la réponse alors que du contenu (HTML) a déjà été défini. Or comme nous l’avons vu précédemment une réponse HTTP doit d’abord être composée de l’entête puis ensuite du contenu. C’est pour cette raison que ce code va générer le message d’avertissement suivant :

Warning: Cannot modify header information – headers already sent by (output started at test.php:1) in test.php on line 4

Voici un deuxième exemple :

  <?php
header('Content-Type: text/html; charset=UTF-8');
?>
<html><head><title>Mon titre</title></head>
<body>
</body>
</html>

Avec ce deuxième exemple on pourrait s’attendre à un résultat sans ce message d’avertissement. Et pourtant il sera bien présent : En observant de plus près le code, on observe des espaces (et donc du contenu !) avant la balise d’ouverture <?php

Enfin un autre cas pouvant présenter ce message d’avertissement : Dans le cas où le fichier est encodé avec le format UTF8 avec BOM, le début de fichier contient un caractère invisible (et donc du contenu !). Donc l’encodage UTF8 sans BOM est à privilégier. Pour en savoir plus.

Attention ! La fonction PHP header n’est pas seule concernée par ce problème: Il ne faut pas oublier que d’autres fonctions PHP (comme setcookie et session_start) provoquent l’envoi de données dans les entêtes et sont donc aussi concernées.

Solutions

Maintenant que vous avons vu l’origine de ce message d’avertissement, nous allons voir plusieurs solutions pour l’éviter.

Solution n°1 : Commencer obligatoirement par les entêtes

La solution la plus simple à comprendre est tout simplement de faire en sorte que son code source commence par définir les entêtes et seulement après définir le contenu de la réponse (attention aux espaces en début de fichier ou caractères invisibles comme le UTF8 avec BOM).
La fonction PHP headers_sent peut être utilisée pour savoir si les entêtes ont déjà été envoyées.

Solution n°2: Utiliser la bufferisation de sortie PHP

Une deuxième solution est d’utiliser la bufferisation de sortie PHP. Il existe plusieurs manières d’utiliser cette bufferisation.

Les fonctions de bufferisation de sortie vous permettent de contrôler quand les données ont été envoyées par le script. Cela peut être utile dans certaines situations, notamment si vous devez envoyer des en-têtes au navigateur après avoir envoyé des données.

Source: php.net

La solution la plus simple est de l’activer par défaut dans le fichier de configuration de PHP :

output_buffering = on

Ou alors de lui fixer une limite (mais attention à sa taille !) :

output_buffering = 4096

Dans le cas où vous utilisez le mode PHP d’Apache (ne fonctionne pas avec FPM/FastCGI), vous pouvez activer cette fonctionnalité dans la configuration de votre VHOST ou dans votre fichier .htaccess :

php_value output_buffering On

Une autre alternative est d’utiliser la bufferisation de sortie manuellement avec les fonctions ob_start et ob_flush :

<?php
//Ouverture du buffer avec ob_start
//Attention au contenu déjà envoyé avant !
ob_start();
header('Content-Type: text/html; charset=UTF-8');
?>
<html><head><title>Mon titre</title></head>
<body>
</body>
</html>
<?php
//Envoi du contenu du buffer avec ob_flush
//A partir d'ici les entetes ne pourront plus être définies
ob_flush();
?>

Solution n°3: Utiliser un framework PHP (solution conseillée)

Et enfin une 3ème solution (celle que je conseille) : Utiliser un framework PHP (comme Symfony). C’est lui qui aura le rôle de construire la réponse HTTP. Il saura séparer l’entête du contenu de chaque réponse.

Maintenant vous avez 3 solutions pour résoudre ce problème.
Pour conclure : vous n’utilisez aucune de ces solutions et vous n’avez pas le message d’avertissement ? Cela peut s’expliquer par plusieurs raisons :

  • La bufferisation de sortie PHP (voir solution n°2) est activée par défaut dans votre configuration
  • Votre configuration PHP n’affiche pas les messages d’avertissements

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Étiquettes :