Nessun output prima di inviare le intestazioni!
Le funzioni che inviano / modificano le intestazioni HTTP devono essere invocate prima di effettuare qualsiasi output .
summary ⇊
Altrimenti la chiamata fallisce:
Avvertenza: impossibile modificare le informazioni dell'intestazione - intestazioni già inviate (output avviato dallo script: riga )
Alcune funzioni che modificano l'intestazione HTTP sono:
L'output può essere:
Intenzionale:
print
, echo
E altre funzioni di uscita producendo
- Prime
<html>
sezioni prima <?php
di codice.
Perché succede?
Per capire perché le intestazioni devono essere inviate prima dell'output, è necessario esaminare una tipica
risposta HTTP . Gli script PHP generano principalmente contenuto HTML, ma trasmettono anche un set di intestazioni HTTP / CGI al server 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>
La pagina / output segue sempre le intestazioni. PHP deve prima passare le intestazioni al server web. Può farlo solo una volta. Dopo la doppia interruzione di riga non può più modificarli.
Quando PHP riceve la prima uscita ( print
, echo
, <html>
) sarà
svuotare tutte le intestazioni raccolti. Successivamente può inviare tutto l'output che desidera. Ma inviare ulteriori intestazioni HTTP è impossibile allora.
Come puoi scoprire dove si è verificato l'output prematuro?
L' header()
avviso contiene tutte le informazioni utili per individuare la causa del problema:
Avvertenza: impossibile modificare le informazioni dell'intestazione - intestazioni già inviate da
(output avviato a / www / usr2345 / htdocs / auth.php: 52 ) in /www/usr2345/htdocs/index.php sulla riga 100
Qui "riga 100" si riferisce allo script in cui l' header()
invocazione non è riuscita.
La nota " output iniziato alle " tra parentesi è più significativa. Indica la fonte dell'output precedente. In questo esempio è auth.php
e linea52
. È lì che dovevi cercare risultati prematuri.
Cause tipiche:
Stampa, eco
L'output intenzionale da print
e le echo
istruzioni terminerà l'opportunità di inviare le intestazioni HTTP. Il flusso dell'applicazione deve essere ristrutturato per evitarlo. Usa funzioni
e schemi di template. Assicurarsi che le header()
chiamate avvengano prima che i messaggi vengano scritti.
Le funzioni che producono output includono
print
, echo
, printf
,vprintf
trigger_error
, ob_flush
, ob_end_flush
, var_dump
,print_r
readfile
, passthru
, flush
, imagepng
,imagejpeg
tra l'altro e funzioni definite dall'utente.
Aree HTML non elaborate
Anche le sezioni HTML non analizzate in un .php
file vengono emesse direttamente. Condizioni di script che attiveranno una header()
chiamata è necessario rispettare, prima di qualsiasi prime <html>
blocchi.
<!DOCTYPE html>
<?php
// Too late for headers already.
Utilizzare uno schema di modello per separare l'elaborazione dalla logica di output.
- Posiziona il codice di elaborazione del modulo in cima agli script.
- Utilizzare variabili di stringa temporanee per rinviare i messaggi.
- La logica di output effettiva e l'output HTML mescolato devono seguire per ultimi.
Spazio prima <?php
per gli avvisi "script.php linea 1 "
Se l'avvertimento si riferisce all'output in linea 1
, allora è principalmente spazio bianco , testo o HTML prima del <?php
token di apertura .
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
Allo stesso modo può verificarsi per gli script o le sezioni di script aggiunti:
?>
<?php
PHP in realtà mangia una singola interruzione di riga dopo aver chiuso i tag. Ma non compenserà più newline o tab o spazi spostati in tali spazi vuoti.
UTF-8 BOM
Le interruzioni di riga e gli spazi da soli possono essere un problema. Ma ci sono anche sequenze di caratteri "invisibili" che possono causare questo. Il più famoso è la
BOM UTF-8 (Byte-Order-Mark)
che non viene visualizzata dalla maggior parte degli editor di testo. È la sequenza di byte EF BB BF
, che è facoltativa e ridondante per i documenti codificati UTF-8. PHP deve tuttavia trattarlo come output non elaborato. Può apparire come i caratteri 
nell'output (se il client interpreta il documento come Latin-1) o simile "spazzatura".
In particolare gli editor grafici e gli IDE basati su Java sono ignari della sua presenza. Non lo visualizzano (obbligato dallo standard Unicode). La maggior parte dei programmatori e degli editor di console tuttavia fanno:
Lì è facile riconoscere il problema all'inizio. Altri editor possono identificare la sua presenza in un menu file / impostazioni (Notepad ++ su Windows può identificare e
risolvere il problema ). Un'altra opzione per controllare la presenza delle distinte materiali è ricorrere a un hexeditor . Sui sistemi * nix hexdump
è generalmente disponibile, se non una variante grafica che semplifica il controllo di questi e altri problemi:
Una soluzione semplice è impostare l'editor di testo per salvare i file come "UTF-8 (no BOM)" o simile nomenclatura simile. Spesso i nuovi arrivati ricorrono altrimenti alla creazione di nuovi file e alla semplice copia e incolla del codice precedente.
Utilità di correzione
Esistono anche strumenti automatizzati per esaminare e riscrivere file di testo ( sed
/awk
o recode
). Per PHP in particolare c'è il phptags
tag tidier . Riscrive i tag di chiusura e apertura in forme lunghe e brevi, ma risolve facilmente anche i problemi di spazi bianchi, Unicode e BOM di Unicode e UTF-x:
phptags --whitespace *.php
È consigliabile utilizzarlo su un'intera directory di inclusione o progetto.
Spazio bianco dopo ?>
Se l'origine dell'errore viene indicata come dietro la
chiusura,?>
è qui che vengono scritti alcuni spazi bianchi o testo non elaborato. Il marker di fine PHP non termina l'esecuzione dello script a questo punto. Qualsiasi carattere di testo / spazio dopo verrà scritto come contenuto della pagina.
È comunemente consigliato, in particolare ai nuovi arrivati, che i ?>
tag di chiusura PHP finali dovrebbero essere omessi. Ciò evita una piccola parte di questi casi. (Molto spesso gli include()d
script sono il colpevole.)
Origine errore indicata come "Sconosciuto sulla riga 0"
In genere è un'estensione PHP o un'impostazione php.ini se non viene concretizzata alcuna fonte di errore.
- Occasionalmente è l'
gzip
impostazione di codifica dello stream
oob_gzhandler
.
- Ma potrebbe anche essere qualsiasi
extension=
modulo doppiamente caricato che genera un messaggio di avvio / avviso PHP implicito.
Messaggi di errore precedenti
Se un'altra istruzione o espressione PHP provoca la stampa di un messaggio di avviso o di un avviso, anche questo viene considerato come output prematuro.
In questo caso è necessario evitare l'errore, ritardare l'esecuzione dell'istruzione o sopprimere il messaggio con es.
isset()
O @()
- quando uno dei due non ostacola il debug in seguito.
Nessun messaggio di errore
Se hai error_reporting
o display_errors
disabilitato per php.ini
, quindi non verrà visualizzato alcun avviso. Ignorare gli errori non risolverà il problema. Le intestazioni non possono ancora essere inviate dopo un output prematuro.
Quindi, quando i header("Location: ...")
reindirizzamenti falliscono silenziosamente, è consigliabile sondare gli avvisi. Riattivarli con due semplici comandi in cima allo script di invocazione:
error_reporting(E_ALL);
ini_set("display_errors", 1);
O set_error_handler("var_dump");
se tutto il resto fallisce.
Parlando di intestazioni di reindirizzamento, dovresti spesso usare un linguaggio come questo per i percorsi del codice finale:
exit(header("Location: /finished.html"));
Preferibilmente anche una funzione di utilità, che stampa un messaggio utente in caso di header()
guasti.
Buffering dell'output come soluzione alternativa
Il buffering dell'output di PHP
è una soluzione alternativa per alleviare questo problema. Spesso funziona in modo affidabile, ma non deve sostituire la corretta strutturazione dell'applicazione e la separazione dell'output dalla logica di controllo. Il suo vero scopo è ridurre al minimo i trasferimenti in blocco al server web.
L' output_buffering=
impostazione può comunque aiutare. Configuralo in php.ini
o tramite .htaccess
o anche .user.ini su moderne configurazioni FPM / FastCGI.
Abilitarlo consentirà a PHP di bufferizzare l'output invece di passarlo istantaneamente al server web. PHP può quindi aggregare le intestazioni HTTP.
Allo stesso modo può essere impegnato con una chiamata in ob_start();
cima allo script di invocazione. Che tuttavia è meno affidabile per diversi motivi:
Anche se <?php ob_start(); ?>
avvia il primo script, gli spazi bianchi o una DBA potrebbero essere mescolati prima, rendendolo inefficace .
Può nascondere spazi bianchi per l'output HTML. Ma non appena la logica dell'applicazione tenta di inviare contenuto binario (ad esempio un'immagine generata), l'output esterno bufferizzato diventa un problema. (Necessitante ob_clean()
come soluzione alternativa più lontana.)
Il buffer ha dimensioni limitate e può essere facilmente superato se lasciato alle impostazioni predefinite. E non è nemmeno un caso raro, difficile da rintracciare
quando succede.
Entrambi gli approcci pertanto potrebbero diventare inaffidabili, in particolare quando si passa da configurazioni di sviluppo e / o server di produzione. Questo è il motivo per cui il buffering dell'output è ampiamente considerato solo una stampella / rigorosamente una soluzione alternativa.
Vedi anche l' esempio di utilizzo di base
nel manuale e per ulteriori pro e contro:
Ma ha funzionato sull'altro server !?
Se non hai ricevuto prima le avvertenze delle intestazioni, l' impostazione php.ini del buffering dell'output
è cambiata. Probabilmente non è configurato sul server attuale / nuovo.
Verifica con headers_sent()
Puoi sempre usare headers_sent()
per sondare se è ancora possibile ... inviare intestazioni. Ciò è utile per stampare in modo condizionale un'informazione o applicare un'altra logica di fallback.
if (headers_sent()) {
die("Redirect failed. Please click on this link: <a href=...>");
}
else{
exit(header("Location: /user.php"));
}
Le soluzioni utili di fallback sono:
<meta>
Tag HTML
Se l'applicazione è strutturalmente difficile da correggere, un modo semplice (ma un po 'poco professionale) per consentire i reindirizzamenti è l'iniezione di un <meta>
tag HTML
. Un reindirizzamento può essere ottenuto con:
<meta http-equiv="Location" content="http://example.com/">
O con un breve ritardo:
<meta http-equiv="Refresh" content="2; url=../target.html">
Questo porta a HTML non valido se utilizzato oltre la <head>
sezione. La maggior parte dei browser lo accetta ancora.
Reindirizzamento JavaScript
In alternativa, è
possibile utilizzare un reindirizzamento JavaScript per i reindirizzamenti di pagina:
<script> location.replace("target.html"); </script>
Sebbene questo sia spesso più conforme all'HTML della <meta>
soluzione alternativa, fa affidamento su client abilitati per JavaScript.
Entrambi gli approcci rendono comunque accettabili fallback quando le autentiche chiamate dell'intestazione HTTP () falliscono. Idealmente dovresti sempre combinarlo con un messaggio intuitivo e un link cliccabile come ultima risorsa. (Che ad esempio è ciò che fa l' estensione http_redirect ()
PECL.)
Perché setcookie()
e session_start()
sono anche interessati
Entrambi setcookie()
e session_start()
devono inviare Set-Cookie:
un'intestazione HTTP. Si applicano quindi le stesse condizioni e messaggi di errore simili verranno generati per situazioni di output prematuro.
(Naturalmente sono inoltre interessati dai cookie disabilitati nel browser o persino dai problemi del proxy. La funzionalità della sessione ovviamente dipende anche dallo spazio libero su disco e da altre impostazioni di php.ini, ecc.)
Ulteriori collegamenti