Brak danych wyjściowych przed wysłaniem nagłówków!
Funkcje wysyłające/modyfikujące nagłówki HTTP muszą zostać wywołane przed wykonaniem jakichkolwiek danych wyjściowych. podsumowanie ⇊ W przeciwnym razie połączenie nie powiedzie się:
Ostrzeżenie: nie można modyfikować informacji nagłówka — nagłówki zostały już wysłane (wyjście rozpoczęło się od script:line)
Niektóre funkcje modyfikujące nagłówek HTTP to:
Dane wyjściowe mogą być:
Zamierzone:
print
, echo
and other functions producing output
- Surowe
<html>
sekcje przed kodem <?php
.
Dlaczego tak się dzieje?
Aby zrozumieć, dlaczego nagłówki muszą zostać wysłane przed wyjściem, należy przyjrzeć się typowej odpowiedzi HTTP. Skrypty PHP generują głównie treść HTML, ale przekazują także zestaw nagłówków HTTP/CGI do serwera WWW:
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>
Strona/wyjście zawsze podąża za nagłówkami. PHP musi najpierw przekazać nagłówki do serwera WWW. Może to zrobić tylko raz. Po podwójnym podziale linii nie można już ich zmieniać.
Kiedy PHP otrzyma pierwsze wyjście (print
, echo
, <html>
), opróżni wszystkie zebrane nagłówki. Następnie może wysłać dowolne dane wyjściowe. Jednak wysłanie dalszych nagłówków HTTP jest wtedy niemożliwe.
Jak dowiedzieć się, gdzie wystąpiło przedwczesne wyjście?
Ostrzeżenie header()
zawiera wszystkie istotne informacje umożliwiające zlokalizowanie przyczyny problemu:
Ostrzeżenie: nie można modyfikować informacji nagłówka — nagłówki zostały już wysłane przez (wyjście rozpoczęło się od /www/usr2345/htdocs/auth.php:52) w /www/usr2345/htdocs/index.php w linii 100
Tutaj linia 100 odnosi się do skryptu, w którym header()
wywołanie nie powiodło się.
Wyjście rozpoczynające się od notatki w nawiasie jest bardziej znaczące. Oznacza źródło poprzedniego wyniku. W tym przykładzie jest to auth.php
i linia 52
. To tam trzeba było szukać przedwczesnych wyników.
Typowe przyczyny:
Drukuj, echo
Zamierzone wyjście z instrukcji print
i echo
zakończy możliwość wysyłania nagłówków HTTP. Aby tego uniknąć, należy zrestrukturyzować przepływ aplikacji. Używaj funkcji i schematów szablonów. Upewnij się, że header()
wywołania mają miejsce zanim wiadomości zostaną zapisane.
Funkcje generujące dane wyjściowe obejmują
print
, echo
, printf
, vprintf
trigger_error
, ob_flush
, ob_end_flush
, var_dump
, print_r
readfile
, passthru
, flush
, imagepng
, imagejpeg
i funkcje zdefiniowane przez użytkownika.
Surowe obszary HTML
Nieprzeanalizowane sekcje HTML w pliku .php
również są bezpośrednim wyjściem. Warunki skryptu, które wyzwolą wywołanie header()
, muszą zostać zapisane przed jakimkolwiek nieprzetworzonym <html>
blokiem.
<!DOCTYPE html>
<?php
// Too late for headers already.
Użyj schematu szablonów, aby oddzielić przetwarzanie od logiki wyjściowej.
- Place form processing code atop scripts.
- Użyj tymczasowych zmiennych łańcuchowych, aby odroczyć wiadomości.
- Rzeczywista logika wyjściowa i zmieszane wyjście HTML powinny znajdować się na końcu.
Białe znaki przed <?php
dla ostrzeżeń linia 1 script.php
Jeśli ostrzeżenie odnosi się do wbudowanego wyjścia 1
, wówczas przed otwierającym tokenem <?php
jest to głównie biała spacja, tekst lub kod HTML.
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
Podobnie może się to zdarzyć w przypadku dołączonych skryptów lub sekcji skryptów:
?>
<?php
PHP faktycznie zjada pojedynczy podział linii po zamykających tagach. Ale nie zrekompensuje to wielu znaków nowej linii, tabulatorów lub spacji przesuniętych w takie luki.
- #P64# #P23# #P24# #P25# #P26# #P65# #P27#
phptags --whitespace *.php
#P28#
Białe znaki po ?>
Jeśli źródło błędu jest wymienione jako znajdujące się za zamykaniem ?>
, to w tym miejscu pojawiły się białe znaki lub nieprzetworzony tekst wypisane. Znacznik końca PHP nie kończy w tym momencie wykonywania skryptu. Wszelkie znaki tekstowe/spacje po nim zostaną zapisane jako treść strony.
Powszechnie zaleca się, zwłaszcza nowicjuszom, aby pomijać końcowe znaczniki zamykające ?>
PHP. To pomija niewielką część tych przypadków. (Często winowajcą są include()d
skrypty.)
Źródło błędu wymienione jako Nieznane w linii 0
Zwykle jest to rozszerzenie PHP lub ustawienie php.ini, jeśli nie jest określone żadne źródło błędu.
- It's occasionally the
gzip
stream encoding setting
or the ob_gzhandler
.
- Ale może to być również dowolny podwójnie załadowany moduł
extension=
generujący ukryty komunikat ostrzegawczy/ostrzegawczy PHP.
Poprzednie komunikaty o błędach
Jeśli inna instrukcja lub wyrażenie PHP powoduje wydrukowanie komunikatu ostrzegawczego lub powiadomienia, liczy się to również jako przedwczesne wyjście.
W takim przypadku należy uniknąć błędu, opóźnić wykonanie instrukcji lub ukryć komunikat za pomocą np. isset()
lub @()
< /a> - gdy którykolwiek z nich nie utrudnia późniejszego debugowania.
Brak komunikatu o błędzie
Jeśli masz wyłączone error_reporting
lub display_errors
na php.ini
, nie pojawi się żadne ostrzeżenie. Ale ignorowanie błędów nie sprawi, że problem zniknie. Nagłówków nadal nie można wysłać po przedwczesnym wyjściu.
Zatem gdy przekierowania header("Location: ...")
po cichu zawodzą, bardzo wskazane jest sprawdzenie ostrzeżeń. Włącz je ponownie za pomocą dwóch prostych poleceń znajdujących się na skrypcie wywołania:
error_reporting(E_ALL);
ini_set("display_errors", 1);
Lub set_error_handler("var_dump");
, jeśli wszystko inne zawiedzie.
Mówiąc o nagłówkach przekierowań, często powinieneś używać takiego idiomu dla końcowych ścieżek kodu:
exit(header("Location: /finished.html"));
Najlepiej nawet funkcja narzędziowa, która wypisuje komunikat użytkownika w przypadku header()
awarii.
Buforowanie wyjścia jako obejście
buforowanie danych wyjściowych w PHP jest obejściem tego problemu. Często działa niezawodnie, ale nie powinno zastępować odpowiedniego strukturyzacji aplikacji i oddzielania danych wyjściowych od logiki sterującej. Jego rzeczywistym celem jest minimalizacja fragmentarycznych transferów do serwera WWW.
Niemniej jednak ustawienie output_buffering=
może być pomocne. Skonfiguruj go w php.ini lub przez .htaccess lub nawet .user.ini w nowoczesnych konfiguracjach FPM/FastCGI.
Włączenie tej opcji pozwoli PHP buforować dane wyjściowe zamiast przekazywać je do serwera WWW natychmiast. PHP może w ten sposób agregować nagłówki HTTP.
Można go również wywołać poprzez wywołanie ob_start();
na górze skryptu wywołania. Który jednak jest mniej niezawodny z wielu powodów:
Nawet jeśli <?php ob_start(); ?>
uruchomi pierwszy skrypt, białe znaki lub BOM mogą zostać wcześniej przetasowane, co czyni go nieskutecznym.
Może ukrywać białe znaki w wynikach HTML. Jednak gdy tylko logika aplikacji spróbuje wysłać zawartość binarną (na przykład wygenerowany obraz), buforowane zewnętrzne wyjście staje się problemem. (Wymagane jest dalsze obejście problemu ob_clean()
.)
Bufor ma ograniczony rozmiar i może łatwo zostać przepełniony, jeśli pozostawi się go do ustawień domyślnych. I to też nie jest rzadkie zjawisko, trudne do wyśledzenia kiedy to się stanie.
Dlatego oba podejścia mogą stać się zawodne - w szczególności podczas przełączania między konfiguracjami programistycznymi i/lub serwerami produkcyjnymi. Dlatego buforowanie wyjścia jest powszechnie uważane za pomocną/ściśle obejście.
Zobacz także podstawowy przykład użycia w podręczniku i więcej plusy i minusy:
Ale zadziałało na innym serwerze!?
Jeśli wcześniej nie pojawiło się ostrzeżenie dotyczące nagłówków, skorzystaj z ustawienia buforowania danych wyjściowych w pliku php.ini uległo zmianie. Prawdopodobnie nie jest skonfigurowany na bieżącym/nowym serwerze.
Sprawdzanie za pomocą headers_sent()
Zawsze możesz użyć headers_sent()
, aby sprawdzić, czy nadal możliwe jest... wysyłanie nagłówków. Co jest przydatne do warunkowego drukowania informacji lub stosowania innej logiki awaryjnej.
if (headers_sent()) {
die("Redirect failed. Please click on this link: <a href=...>");
}
else{
exit(header("Location: /user.php"));
}
Przydatne rozwiązania awaryjne to:
Znacznik HTML <meta>
Jeśli struktura Twojej aplikacji jest trudna do naprawienia, prostym (ale nieco nieprofesjonalnym) sposobem zezwolenia na przekierowania jest wstrzyknięcie tagu HTML <meta>
. Przekierowanie można osiągnąć za pomocą:
<meta http-equiv="Location" content="http://example.com/">
Lub z krótkim opóźnieniem:
<meta http-equiv="Refresh" content="2; url=../target.html">
Prowadzi to do nieprawidłowego kodu HTML, jeśli jest używany poza sekcją <head>
. Większość przeglądarek nadal to akceptuje.
Przekierowanie JavaScript
Alternatywnie można zastosować przekierowanie JavaScript w przypadku przekierowań stron:
<script> location.replace("target.html"); </script>
Chociaż jest to często bardziej zgodne z HTML niż obejście <meta>
, wiąże się z koniecznością polegania na klientach obsługujących JavaScript.
Obydwa podejścia zapewniają jednak akceptowalne rozwiązania awaryjne, gdy oryginalne wywołania nagłówka HTTP nie powiodą się. Idealnie byłoby, gdybyś w ostateczności zawsze łączył to z przyjazną dla użytkownika wiadomością i klikalnym linkiem. (Co na przykład robi rozszerzenie http://php.net/http_redirect PECL.)
Dlaczego dotyczy to również setcookie()
i session_start()
Zarówno setcookie()
, jak i session_start()
muszą wysłać Set-Cookie:
nagłówek HTTP. Obowiązują zatem te same warunki i podobne komunikaty o błędach będą generowane w przypadku przedwczesnych sytuacji wyjściowych.
(Oczywiście mają na nie wpływ także wyłączone pliki cookie w przeglądarce lub nawet problemy z serwerem proxy. Funkcjonalność sesji zależy oczywiście również od wolnego miejsca na dysku i innych ustawień php.ini itp.)
Dalsze linki
person
mario
schedule
06.11.2011
ob_start
iob_end_clean()
). Następnie możesz ustawić plik cookie lub sesję równąob_get_contents()
, a następnie użyćob_end_clean()
do wyczyszczenia bufora. - person Jack   schedule 04.04.2014safeRedirect
w mojej bibliotece PHP: github.com/heinkasner/PHP -Biblioteka/blob/master/extra.php - person heinkasner   schedule 24.07.2014UTF-8
, aleUTF-8 (Without BOM)
~~~~~~~~~~~ - person T.Todua   schedule 19.09.2014