Nicio ieșire înainte de a trimite anteturi!
Funcțiile care trimit/modifică anteturile HTTP trebuie să fie invocate înainte de a se realiza orice ieșire. rezumat ⇊ În caz contrar, apelul eșuează:
Avertisment: Nu se pot modifica informațiile antetului - anteturile deja trimise (ieșirea a început la script:line)
Unele funcții care modifică antetul HTTP sunt:
Ieșirea poate fi:
Intenționat:
print
, echo
and other functions producing output
- Secțiuni brute
<html>
înainte de codul <?php
.
De ce se întâmplă?
Pentru a înțelege de ce anteturile trebuie trimise înainte de a ieși, este necesar să priviți un răspuns tipic HTTP. Scripturile PHP generează în principal conținut HTML, dar transmit și un set de anteturi HTTP/CGI către serverul 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="/"> <img src=internal-icon-delayed> </a>
Pagina/ieșirea urmează întotdeauna anteturile. PHP trebuie să treacă mai întâi anteturile către serverul web. Poate face asta o singură dată. După dubla întrerupere de linie, nu le mai poate modifica niciodată.
Când PHP primește prima ieșire (print
, echo
, <html>
) va șterge toate anteturile colectate. După aceea, poate trimite toate rezultatele dorite. Dar trimiterea de anteturi HTTP suplimentare este imposibilă atunci.
Cum puteți afla unde a avut loc ieșirea prematură?
Avertismentul header()
conține toate informațiile relevante pentru a localiza cauza problemei:
Avertisment: Nu se pot modifica informațiile din antet - anteturile deja trimise de (ieșirea a început la /www/usr2345/htdocs/auth.php:52) în /www/usr2345/htdocs/index.php pe linia 100
Aici linia 100 se referă la scriptul în care header()
invocarea a eșuat.
ieșirea începută la nota din paranteză este mai semnificativă. Denumește sursa ieșirii anterioare. În acest exemplu, este auth.php
și linia 52
. Acolo trebuia să cauți rezultate premature.
Cauzele tipice:
Imprimare, ecou
Ieșirea intenționată din instrucțiunile print
și echo
va pune capăt oportunității de a trimite anteturi HTTP. Fluxul de aplicare trebuie restructurat pentru a evita acest lucru. Folosiți funcții și scheme de șabloane. Asigurați-vă că header()
apelurile au loc înainte de să fie scrise mesajele.
Funcțiile care produc rezultate includ
print
, echo
, printf
, vprintf
trigger_error
, ob_flush
, ob_end_flush
, var_dump
, print_r
readfile
, passthru
, flush
, imagepng
, imagejpeg
printre altele și funcții definite de utilizator.
Zone HTML brute
Secțiunile HTML neanalizate dintr-un fișier .php
sunt, de asemenea, rezultate directe. Condițiile de script care vor declanșa un apel header()
trebuie notate înainte de orice blocuri brute <html>
.
<!DOCTYPE html>
<?php
// Too late for headers already.
Utilizați o schemă de șablon pentru a separa procesarea de logica de ieșire.
- Place form processing code atop scripts.
- Utilizați variabile șir temporare pentru a amâna mesajele.
- Logica reală de ieșire și ieșirea HTML amestecată ar trebui să urmeze ultima.
Spațiu înainte de <?php
pentru avertismentele script.php linia 1
Dacă avertismentul se referă la ieșirea în linie 1
, atunci este în mare parte spații albe, text sau HTML înainte de simbolul <?php
de deschidere.
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
În mod similar, poate apărea și pentru scripturile atașate sau secțiunile de script:
?>
<?php
PHP consumă de fapt o singura întrerupere de linie după etichetele apropiate. Dar nu va compensa mai multe linii noi sau file sau spații mutate în astfel de goluri.
- #P64# #P23# #P24# #P25# #P26# #P65# #P27#
phptags --whitespace *.php
#P28#
Spații albe după ?>
Dacă sursa de eroare este menționată ca în spatele închiderea ?>
, atunci aici au apărut niște spații albe sau textul brut scrise. Marcatorul de sfârșit PHP nu oprește execuția scriptului în acest moment. Orice caractere de text/spațiu după acesta vor fi scrise în continuare ca conținut de pagină.
În mod obișnuit, se recomandă, în special noilor veniți, ca etichetele de închidere ?>
PHP să fie omise. Acest lucru evită o mică parte din aceste cazuri. (Destul de frecvent, scripturile include()d
sunt vinovate.)
Sursa de eroare menționată ca Necunoscută pe linia 0
Este de obicei o extensie PHP sau o setare php.ini dacă nu se concretizează nicio sursă de eroare.
- It's occasionally the
gzip
stream encoding setting
or the ob_gzhandler
.
- Dar ar putea fi și orice modul
extension=
încărcat dublu care generează un mesaj implicit de pornire/avertizare PHP.
Mesajele de eroare precedente
Dacă o altă instrucțiune sau expresie PHP determină tipărirea unui mesaj de avertizare sau a unei notificări, aceasta contează și ca rezultat prematur.
În acest caz, trebuie să evitați eroarea, să întârziați execuția instrucțiunii sau să suprimați mesajul cu, de ex. isset()
sau @()
< /a> - când nici unul nu împiedică depanarea mai târziu.
Niciun mesaj de eroare
Dacă aveți error_reporting
sau display_errors
dezactivat pentru php.ini
, atunci nu va apărea niciun avertisment. Dar ignorarea erorilor nu va face problema să dispară. Anteturile încă nu pot fi trimise după o ieșire prematură.
Deci, atunci când redirecționările header("Location: ...")
eșuează în tăcere, este foarte recomandabil să cercetați avertismente. Reactivați-le cu două comenzi simple deasupra scriptului de invocare:
error_reporting(E_ALL);
ini_set("display_errors", 1);
Sau set_error_handler("var_dump");
dacă toate celelalte nu reușesc.
Vorbind despre anteturile de redirecționare, ar trebui să folosiți adesea un limbaj ca acesta pentru căile finale ale codului:
exit(header("Location: /finished.html"));
De preferință chiar și o funcție de utilitate, care imprimă un mesaj de utilizator în cazul erorilor header()
.
Buffering de ieșire ca o soluție
PHP-ul buffering de ieșire este o soluție pentru a atenua această problemă. Adesea funcționează fiabil, dar nu ar trebui să înlocuiască structurarea adecvată a aplicației și separarea ieșirii de logica de control. Scopul său real este de a minimiza transferurile în bucăți către serverul web.
Cu toate acestea, setarea output_buffering=
poate ajuta. Configurați-l în php.ini sau prin .htaccess sau chiar .user.ini pe setările moderne FPM/FastCGI.
Activarea acestuia va permite PHP să tamponeze ieșirea în loc să o transmită serverului web imediat. PHP poate astfel agrega anteturi HTTP.
De asemenea, poate fi angajat cu un apel către ob_start();
deasupra scriptului de invocare. Care, totuși, este mai puțin de încredere din mai multe motive:
Chiar dacă <?php ob_start(); ?>
pornește primul script, spațiile albe sau o BOM ar putea fi amestecate înainte, făcându-l ineficient.
Poate ascunde spații albe pentru ieșirea HTML. Dar de îndată ce logica aplicației încearcă să trimită conținut binar (o imagine generată de exemplu), ieșirea străină tamponată devine o problemă. (Necesită ob_clean()
ca o soluție suplimentară.)
Buffer-ul este limitat în dimensiune și poate depăși cu ușurință atunci când este lăsat la valorile implicite. Și nici asta nu este o întâmplare rară, dificil de urmărit când se întâmplă.
Prin urmare, ambele abordări pot deveni nesigure - în special atunci când comutați între setările de dezvoltare și/sau serverele de producție. Acesta este motivul pentru care tamponarea de ieșire este considerată în general doar o cârjă / strict o soluție.
Consultați și exemplul de utilizare de bază din manual și pentru mai multe argumente pro şi contra:
Dar a funcționat pe celălalt server!?
Dacă nu ați primit avertismentul de antet înainte, atunci setarea php.ini de buffering de ieșire s-a schimbat. Este probabil neconfigurat pe serverul actual/nou.
Se verifică cu headers_sent()
Puteți utiliza oricând headers_sent()
pentru a verifica dacă mai este posibil să... trimiteți anteturi. Ceea ce este util pentru a imprima condiționat informații sau pentru a aplica altă logică de rezervă.
if (headers_sent()) {
die("Redirect failed. Please click on this link: <a href=...>");
}
else{
exit(header("Location: /user.php"));
}
Soluțiile alternative utile sunt:
Etichetă HTML <meta>
Dacă aplicația dvs. este greu de remediat din punct de vedere structural, atunci o modalitate ușoară (dar oarecum neprofesională) de a permite redirecționări este injectarea unei etichete HTML <meta>
. O redirecționare poate fi realizată cu:
<meta http-equiv="Location" content="http://example.com/">
Sau cu o scurtă întârziere:
<meta http-equiv="Refresh" content="2; url=../target.html">
Acest lucru duce la HTML nevalid atunci când este utilizat după secțiunea <head>
. Majoritatea browserelor încă îl acceptă.
redirecționare JavaScript
Ca alternativă, poate fi utilizată o redirecționare JavaScript pentru redirecționări de pagină:
<script> location.replace("target.html"); </script>
Deși aceasta este adesea mai compatibilă cu HTML decât soluția <meta>
, aceasta implică o dependență de clienți capabili de JavaScript.
Ambele abordări fac totuși alternative acceptabile atunci când apelurile autentice HTTP header() eșuează. În mod ideal, ați combina întotdeauna acest lucru cu un mesaj ușor de utilizat și un link pe care se poate face clic ca ultimă soluție. (Ceea ce, de exemplu, este ceea ce face extensia PECL http://php.net/http_redirect.)
De ce sunt afectate și setcookie()
și session_start()
Atât setcookie()
, cât și session_start()
trebuie să trimită un antet HTTP Set-Cookie:
. Prin urmare, se aplică aceleași condiții și vor fi generate mesaje de eroare similare pentru situații de ieșire prematură.
(Desigur, acestea sunt în plus afectate de cookie-urile dezactivate în browser sau chiar de problemele proxy. Funcționalitatea sesiunii depinde, evident, și de spațiul liber pe disc și alte setări php.ini etc.)
Link-uri suplimentare
person
mario
schedule
06.11.2011
ob_start
șiob_end_clean()
se pot dovedi utile aici). Apoi puteți seta un cookie sau o sesiune egală cuob_get_contents()
și apoi să utilizațiob_end_clean()
pentru a șterge tamponul. - person Jack   schedule 04.04.2014safeRedirect
din biblioteca mea PHP: github.com/heinkasner/PHP -Library/blob/master/extra.php - person heinkasner   schedule 24.07.2014UTF-8
, ciUTF-8 (Without BOM)
~~~~~~~~~~~ - person T.Todua   schedule 19.09.2014