GYIK: „Cannot modify header information – headers already sent”
Közzétéve:
A fórumban és e-mailben is ez a legtöbbször feltett PHP kérdés, ezért külön GYIK-ot kapott. A hibaüzenet így néz ki:
Warning: Cannot modify header information - headers already sent by
(output started at /home/progz/www/config.php:12) in
/home/progz/www/belepes.php on line 8
Mi történik?
A HTTP válasz két részből áll: előbb a fejlécek jönnek (például Location, Set-Cookie, Content-Type), utána a tartalom. Amikor a PHP kiírja az első bájt tartalmat, legyen az akár egyetlen szóköz, a fejléceket már el kellett küldenie. Az ezután hívott header(), setcookie() vagy session_start() már nem tud fejlécet hozzáadni, és ezt jelzi a figyelmeztetés.
A legfontosabb: a hibaüzenet megmondja, hol kezdődött a kimenet. A fenti példában a config.php 12. sorában. A belepes.php 8. sora csak az áldozat.
A négy leggyakoribb ok
1. Szóköz vagy üres sor a <?php előtt vagy a ?> után.
<?php
$dbHost = 'localhost';
$dbUser = 'progz';
?>
A záró ?> után ott maradt egy sortörés és egy szóköz. Ez kimenet, és mivel a config.php-t minden oldal betölti, minden oldalon hibát okoz. A legbiztosabb megoldás: csak PHP-t tartalmazó fájl végére ne írj ?>-t. A PHP ezt megengedi.
2. BOM a fájl elején. Ha a fájlt a Windows Jegyzettömbje UTF-8-ban menti, a fájl elejére három láthatatlan bájtot tesz (EF BB BF, ez a bájtsorrend-jelölő). A szerkesztőben semmi sem látszik, a böngészőben esetleg egy furcsa karakter az oldal tetején. Használj olyan szerkesztőt, amelyben BOM nélkül is lehet menteni, vagy maradj ISO-8859-2 kódolásnál.
3. HTML a PHP blokk előtt.
<html>
<?php
session_start(); // túl késő, a <html> már kiment
4. Egy echo vagy hibaüzenet korábban. Egy notice is kimenet. Ha a display_errors be van kapcsolva, egy elírt változónév miatti figyelmeztetés is elindítja a kimenetet.
Átirányítás után: exit
<?php
session_start();
if (!isset($_SESSION['felhasznalo'])) {
header('Location: http://www.progz.hu/belepes.php');
exit;
}
A header('Location: ...') csak egy fejléc, a szkript utána tovább fut. Az exit nélkül a védett tartalom is lefut, és a válaszban el is megy, csak a böngésző nem mutatja meg. Ez biztonsági hiba, nem csak szépséghiba. A Location értéke pedig a szabvány szerint teljes URL legyen.
És az ob_start?
Az ob_start() a szkript elején bekapcsolja a kimenet pufferelését: semmi sem megy ki addig, amíg a szkript véget nem ér, ezért utána még bármikor lehet fejlécet küldeni. Működik, és néha ez a jó megoldás, például ha egy sablonrendszer előbb rendereli az oldalt. Hibajavításként viszont csak elfedi a problémát, a láthatatlan szóköz vagy BOM ott marad a fájlban, és a következő tárhelyen újra előjön.