Pas de sortie avant l'envoi des en-têtes !
Les fonctions qui envoient/modifient les en-têtes HTTP doivent être invoquées. avant toute sortie . <a href="https://stackoverflow.com/a/8028979/345031"><strong>summary </strong></a> Sinon, l'appel échoue :
Avertissement : Cannot modify header information - headers already sent (output started at script:ligne )
Certaines fonctions modifiant l'en-tête HTTP sont :
La sortie peut être :
-
Non intentionnel :
-
Intentionnel :
-
print
, echo
et d'autres fonctions produisant des résultats
- Brut
<html>
sections précédentes <?php
code.
Pourquoi cela se produit-il ?
Pour comprendre pourquoi les en-têtes doivent être envoyés avant la sortie, il faut d'examiner un exemple typique HTTP réponse. Les scripts PHP génèrent principalement du contenu HTML, mais passent également un ensemble d'en-têtes HTTP/CGI au serveur web :
HTTP/1.1 200 OK
Powered-By: PHP/5.3.7
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
<html><head><title>PHP page output page</title></head>
<body><h1>Content</h1> <p>Some more output follows...</p>
and <a href="http://stackoverflow.com/"> <img src=internal-icon-delayed> </a>
La page/la sortie est toujours suit les en-têtes. PHP doit d'abord passer les les en-têtes au serveur web d'abord. Il ne peut le faire qu'une seule fois. Après le double saut de ligne, il ne peut plus jamais les modifier.
Lorsque PHP reçoit la première sortie ( print
, echo
, <html>
), elle affleurant tous les en-têtes collectés. Ensuite, il peut envoyer tout ce qu'il veut qu'il veut. Mais l'envoi d'autres en-têtes HTTP est alors impossible.
Comment pouvez-vous savoir où la sortie prématurée s'est produite ?
El header()
L'avertissement contient toutes les informations pertinentes pour localiser la cause du problème :
Avertissement : Impossible de modifier les informations d'en-tête - en-têtes déjà envoyés par (la sortie a commencé à /www/usr2345/htdocs/ auth.php:52 ) dans /www/usr2345/htdocs/index.php sur la ligne 100
Ici, la "ligne 100" se réfère au script où la header()
invocation échoué.
Le " La sortie a commencé à La note " entre parenthèses " est plus significative. Elle dénomme la source de la sortie précédente. Dans cet exemple, c'est auth.php
et ligne 52
. C'est là qu'il fallait chercher une sortie prématurée.
Causes typiques :
-
Impression, écho
Sortie intentionnelle de print
y echo
mettront fin à la possibilité d'envoyer des en-têtes HTTP. Le flux de l'application doit être restructuré pour éviter cela. Utilisez fonctions et des schémas de modélisation. Assurez-vous que header()
des appels ont lieu avant Les messages sont écrits.
Les fonctions qui produisent des résultats comprennent
-
print
, echo
, printf
, vprintf
-
trigger_error
, ob_flush
, ob_end_flush
, var_dump
, print_r
-
readfile
, passthru
, flush
, imagepng
, imagejpeg
entre autres, et des fonctions définies par l'utilisateur.
-
Zones HTML brutes
Les sections HTML non analysées dans un .php
sont également des sorties directes. script conditions qui déclencheront une header()
l'appel doit être noté avant tout brut <html>
blocs.
<!DOCTYPE html>
<?php
// Too late for headers already.
Utilisez un schéma de modélisation pour séparer le traitement de la logique de sortie.
- Placez le code de traitement du formulaire au sommet des scripts.
- Utilisez des variables temporaires de type chaîne pour différer les messages.
- La logique de sortie réelle et la sortie HTML mélangée devraient suivre en dernier.
-
Espace blanc avant <?php
pour "script.php ligne 1 "Avertissements
Si l'avertissement fait référence à une sortie en ligne 1
alors c'est surtout leader espace blanc , du texte ou du HTML avant l'ouverture <?php
jeton.
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
De même, cela peut se produire pour les sections scripts ou scripts annexées :
?>
<?php
PHP consomme en fait un simple rupture de ligne après les balises fermées. Mais il ne pourra pas compensera pas les multiples sauts de ligne, tabulations ou espaces décalés dans ces espaces.
-
NOMENCLATURE UTF-8
Les ruptures de ligne et les espaces seuls peuvent être un problème. Mais il existe aussi des séquences de caractères "invisibles" qui peuvent causer ce problème. Le plus connu est le NOMENCLATURE UTF-8 (marque d'ordre d'octet) qui n'est pas affiché par la plupart des éditeurs de texte. Il s'agit de la séquence d'octets EF BB BF
qui est facultatif et redondant pour les documents codés en UTF-8. PHP doit cependant le traiter comme une sortie brute. Il peut apparaître sous la forme des caractères dans la sortie (si le client interprète le document en Latin-1) ou d'autres "déchets" similaires.
En particulier, les éditeurs graphiques et les IDE basés sur Java sont inconscients de son présence. Ils ne le visualisent pas (obligés par la norme Unicode). La plupart des éditeurs de programmeurs et de consoles le font cependant :
Là, il est facile de reconnaître le problème dès le début. D'autres éditeurs peuvent identifier sa présence dans un menu de fichiers/paramètres (Notepad++ sous Windows peut identifier et corriger le problème). remédier au problème ), Une autre option pour inspecter la présence des nomenclatures est de recourir à un outil de contrôle de la qualité. éditeur hexagonal . Sur les systèmes *nix hexdump
est généralement disponible, si ce n'est une variante graphique qui simplifie l'audit de ces questions et d'autres :
Une solution simple consiste à configurer l'éditeur de texte pour qu'il enregistre les fichiers en "UTF-8 (sans BOM)". ou une nomenclature similaire. Les nouveaux venus ont souvent recours à la création de nouveaux fichiers et au copier-coller du code précédent.
Utilitaires de correction
Il existe également des outils automatisés permettant d'examiner et de réécrire des fichiers texte ( sed
/ awk
o recode
). Pour PHP en particulier, il existe l'option phptags
étiquette plus propre . Il réécrit les balises close et open en formes longue et courte, mais aussi facilement corrige facilement les problèmes d'espacement en fin de ligne, d'Unicode et de BOM UTF-x :
phptags --whitespace *.php
Il peut être utilisé en toute sécurité sur un répertoire entier d'inclusion ou de projet.
-
Espace blanc après ?>
Si la source de l'erreur est mentionnée comme étant derrière le fermeture du site ?>
alors c'est là que certains espaces ou le texte brut ont été écrits. Le marqueur de fin PHP ne met pas fin à l'exécution du script à ce stade. Tout texte/espace après lui sera écrit comme contenu de page encore.
Il est généralement conseillé, en particulier aux nouveaux arrivants, que le trailing ?>
PHP les balises de fermeture doivent être omises. Ce site évite le site une petite partie de ces cas. (Assez couramment include()d
scripts sont les coupables).
-
La source de l'erreur est mentionnée comme "Inconnu sur la ligne 0".
C'est typiquement une extension PHP ou un paramètre du php.ini si aucune source d'erreur n'est concrétisée.
- C'est parfois le
gzip
réglage du codage du flux ou le ob_gzhandler
.
- Mais il peut aussi s'agir de n'importe quelle charge double
extension=
module générant un message implicite de démarrage/alerte de PHP.
-
Messages d'erreur précédents
Si une autre déclaration ou expression PHP provoque un message d'avertissement ou une erreur de la part de l'utilisateur. est imprimé, cela compte aussi comme une sortie prématurée.
Dans ce cas, vous devez éviter l'erreur, retarder l'exécution de l'instruction, ou supprimer le message en utilisant, par exemple, la fonction isset()
o @()
- quand l'un ou l'autre n'entrave pas le débogage ultérieur.
Aucun message d'erreur
Si vous avez error_reporting
o display_errors
handicap par php.ini
, alors aucun avertissement n'apparaîtra. Mais ignorer les erreurs ne fera pas disparaître le problème. problème. Les en-têtes ne peuvent toujours pas être envoyés après une sortie prématurée.
Alors quand header("Location: ...")
redirections échouent silencieusement, c'est très conseillé de vérifier les avertissements. Réactivez-les avec deux commandes simples au sommet de l'invocation script :
error_reporting(E_ALL);
ini_set("display_errors", 1);
Ou set_error_handler("var_dump");
si tout le reste échoue.
En parlant d'en-têtes de redirection, vous devriez souvent utiliser une idiome comme ceci pour les chemins de code finaux :
exit(header("Location: /finished.html"));
De préférence même une fonction utilitaire, qui imprime un message utilisateur en cas de header()
échecs.
Mise en mémoire tampon de la sortie comme solution de rechange
PHPs mise en mémoire tampon des sorties est une solution de contournement pour pallier à ce problème. Elle fonctionne souvent de manière fiable, mais ne doit pas se substituer à une structuration correcte de l'application et à la séparation de la sortie et de la de contrôle. Son but réel est de minimiser les transferts de données en morceaux vers le serveur web.
-
El output_buffering=
Le réglage peut néanmoins aider. Configurez-le dans le php.ini ou via .htaccess ou même .user.ini sur les configurations modernes de FPM/FastCGI.
L'activer permet à PHP de mettre en mémoire tampon la sortie au lieu de la transmettre instantanément au serveur web. PHP peut ainsi agréger les en-têtes HTTP.
-
Il peut également être engagé avec un appel à ob_start();
au sommet de l'invocation script. Ce qui est cependant moins fiable pour de multiples raisons :
-
Même si <?php ob_start(); ?>
commence le premier script, un espace blanc ou un BOM peuvent être mélangés avant, le rendant inefficace .
-
Il peut dissimuler les espaces blancs pour la sortie HTML. Mais dès que la logique applicative tente d'envoyer un contenu binaire (une image générée par exemple), la sortie étrangère mise en mémoire tampon devient un problème. (Nécessité de ob_clean()
comme solution de rechange supplémentaire).
-
La taille de la mémoire tampon est limitée et peut facilement déborder si elle est laissée à ses valeurs par défaut. Et ce n'est pas un cas rare non plus, difficile à trouver quand ça arrive.
Les deux approches peuvent donc devenir peu fiables, notamment lorsqu'on passe d'un mode à l'autre. les configurations de développement et/ou les serveurs de production. C'est pourquoi la mise en tampon de la sortie est largement considéré comme une simple béquille / une solution de contournement.
Voir aussi le exemple d'utilisation de base dans le manuel, et pour d'autres avantages et inconvénients :
Mais ça a marché sur l'autre serveur ! ?
Si vous n'avez pas reçu l'avertissement sur les en-têtes avant, alors l'option mise en mémoire tampon de la sortie Paramètres du php.ini a changé. Il est probablement non configuré sur le serveur actuel/nouveau.
Vérification avec headers_sent()
Vous pouvez toujours utiliser headers_sent()
pour vérifier si il est encore possible... d'envoyer des en-têtes. Ce qui est utile pour imprimer conditionnellement des informations ou appliquer une autre logique de repli.
if (headers_sent()) {
die("Redirect failed. Please click on this link: <a href=...>");
}
else{
exit(header("Location: /user.php"));
}
Les solutions de rechange utiles sont les suivantes :
-
HTML <meta>
étiquette
Si votre application est structurellement difficile à réparer, alors une solution facile (mais peu professionnelle) pour autoriser les redirections est d'injecter un fichier HTML <meta>
tag. Une redirection peut être réalisée avec :
<meta http-equiv="Location" content="http://example.com/">
Ou avec un court délai :
<meta http-equiv="Refresh" content="2; url=../target.html">
Cela conduit à des HTML non valides lorsqu'ils sont utilisés au-delà de la date limite de réception. <head>
section. La plupart des navigateurs l'acceptent encore.
-
Redirection JavaScript
Comme alternative a Redirection JavaScript peut être utilisé pour les redirections de pages :
<script> location.replace("target.html"); </script>
Bien que cette méthode soit souvent plus conforme au HTML que l'option <meta>
solution de contournement, elle implique une dépendance à l'égard des clients compatibles avec JavaScript.
Les deux approches constituent cependant des solutions de repli acceptables lorsque les appels HTTP header() authentiques échouent. Idéalement, vous devriez toujours combiner cette approche avec un message convivial et un lien cliquable en dernier recours. un lien cliquable en dernier recours. (Ce qui, par exemple, est ce que le http_redirect() L'extension PECL le fait).
Pourquoi setcookie()
y session_start()
sont également concernés
Les deux sites setcookie()
y session_start()
doivent envoyer un Set-Cookie:
En-tête HTTP. Les mêmes conditions s'appliquent donc, et des messages d'erreur similaires seront générés pour les situations de sortie prématurée.
(Bien entendu, ils sont en outre affectés par les cookies désactivés dans le navigateur. ou même des problèmes de proxy. La fonctionnalité de session dépend évidemment aussi de l'espace l'espace disque disponible et d'autres paramètres du php.ini, etc.)
Autres liens
0 votes
Lire : stackoverflow.com/questions/1912029/
0 votes
Assurez-vous qu'aucun texte n'est édité (
ob_start
yob_end_clean()
peut s'avérer utile ici). Vous pourriez alors définir un cookie ou une session égale àob_get_contents()
et ensuite utiliserob_end_clean()
pour vider le tampon.0 votes
Utilisez le
safeRedirect
dans ma bibliothèque PHP : github.com/heinkasner/PHP-Library/blob/master/extra.php5 votes
\
~~Votre fichier ENCODING ne doit pas êtreUTF-8
peroUTF-8 (Without BOM)
~~~