php.lv/f forumos ik pa laikam parādās tēmas par “Headers already sent”,
kas parasti izsauc no pārējiem lamu vētras un nebeidzamās pasūtīšanas uz google
vai uz paša foruma meklēšanu, jo tēma jau ir “100 reizes pārrunāta”. Un tā tas
tiešām ir. Tāpēc esmu sagatavojis nelielu tekstu, kas varētu palīdzēt cilvēkiem pašiem tikt galā ar šo problēmu.

Ko tad īsti “headers already sent” nozīme?

Tas nozīmē, ka tu klientam (pārlūkam) centies nosūtīt HTTP headerus
pēc tam, kad jau ir notikusi izvade. Piemēram, ir jau nosūtīti kaut kādi
HTML tagi (bet reāli jebkura simbola izvade izsauks šo kļūdu).

Lai būtu skaidrāks, apskatīsim ko un kādā secībā tad īsti web serveris sūta
pārlūkam. Un kas vispār ir HTTP headeri. Kad tu ieraksti pārlūkā kādu adresi
un nospied enter, tad web serverim attiecīgajā adresē tiek nosūtīts kaut kas
līdzīgs nākamajām rindām:

GET / HTTP/1.1
Host: andrisp.blogiem.lv
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3

Ja lapa ir atrasta (šajā gadijumā webroot direktorijas noklusētais fails), tad
web serveris atbild ar kaut ko šādu:

HTTP/1.x 200 OK
Date: Mon, 07 May 2007 16:34:56 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Type: text/html; charset=utf-8

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
    <head>
        <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
		<title>Andra P blogs</title>
		<meta name=”Description” content=”par web kodēšanu & stuff” />
… u.t.t.

Piezīme: Vienkāršības labad, šajos piemēros lielu daļu
headerus esmu atmetis.

Kā jau iespējams var noprast, tad header ir vērtības nosaukuma un pašas
vērtības pāris, piemēram, Host: andrisp.blogiem.lv. Šīs vērtības
apraksta datus, kas tiek pārsūtīti. Šos datus parasts lapas apmeklētājs neredz, bet
pārlūks (vai arī web serveris) pēc tiem zina, ka, piemēram, dokuments ir ticis atsūtīts
tādā un tādā datumā; ka tas ir text/html
dokuments UTF-8 kodējumā; ka to, piemēram, nedrīkst saglabāt kešatmiņā u.t.t.
Šie parastā lietotājā acīm neredzamie dati vienmēr ir jānosūta pārlūkam pirms
paša HTML dokumenta.

Visbiežāk izplatītākā kļūdas situācija #1

Bieži web izstrādātāji-iesācēji lapas veido, haotiski jaucot HTML valodu kopā
ar PHP kodu, kas bieži noved pie nākamā kodā līdzīgas situācijas:

<html>
<head>
    <title>Mana lapa</title>
</head>
<body>
	<?php
	if ($user_logged_in != true) {
		header('Location: login.php');
		die();
	}
	?>
</body>
</html>

Kā redzams, tad HTML izvade jau ir notikusi pirms ar PHP
header() funkcijas palīdzību pārlūkam
tiek nosūtīts Location headeris (Location headeris
pasaka pārlūkam, ka jāatver norādītā lapa).

Risinājums: Šāda veida darbības vajag veikt pašā skripta
sākumā.

Visbiežāk izplatītākā kļūdas situācija #2

Ja skripti ir UTF-8 kodējumā, tad ļoti bieži gadās, ka tie ir saglabāti
ar BOM (Byte Order Mask)
simboliem. Tie ir trīs neredzami simboli pašā faila sākumā, kas
ir vajadzīgi, lai programmas mācētu atpazīt kā apstrādāt konkrēto Unicode failu.
Bet reāli ir tā, ka UTF-8 kodējumam šie simboli nav vajadzīgi. Citiem Unicode kodējumiem,
piemēram, UTF-16 vai UTF-32 tie ir vajadzīgi. Parasti teksta redaktoros šos trīs
BOM simbolus nevar redzēt.

Problēma ir tāda, ka ne PHP interpretātors, ne arī pārluki nemāk apieties ar šiem
trijiem simboliem. PHP šos simbolus uztver kā jebkuru citus simbolus, kas jānosūta
pārlūkam. Tātad, ja faili ir UTF-8 kodējumā ar BOM simboliem, tad pie skripta
atvēršanas tie būs paši pirmie simboli, kas tiks nosūtīti pārlūkam. Rezultātā
headerus vairs nebūs iespējams nosūtīt.

Piezīme: Šī kļūdas situācija visbiežāk parādās kopā ar nākamo
kļūdas situāciju.

Piezīme: Bieži gadās, ka izvade notiek kādā no iekļautajiem skriptiem.
Tādās reizēs nereti to ir grūtāk pamanīt.

Risinājums #1: BOM simbolus var izdzēst ar kādu HEX redaktoru.
Faila sākumā izdzēsiet EF BB BF simbolus.

Risinājums #2: Daudzi koda redaktori piedāvā iespēju saglabāt
UTF-8 tekstu bez BOM. Piemēram, Notepad++.

Visbiežāk izplatītākā kļūdas situācija #3

Nereti jaunais izstrādātājs
mēģina startēt sesiju (izmantojot iebūvēto session_start()) pēc tam,
kad jau ir notikusi izvade. Problēma slēpjas tur, ka session_start() pārlūkam
nosūta speciālu headeri (Set-Cookie). Tātad, ja būs nosūtīts kaut viens HTML tags,
vai arī UTF-8 kodējuma fails būs saglabāts ar visiem BOM simboliem, tad sesija
uzsākta netiks, un tiks izdrukāts kļūdas paziņojums.

Risinājums: Novietojam session_start() pašā skripta
sākumā. Kā arī, ja ir, tad izdzēšam BOM simbolus.

Vieglās zāles - Output buffering.

Reizēm gadās, ka nākas papildināt kādu vecāku vai kāda cita cilvēka radītu sistēmu, bet visa
tās uzbūve ir tāda, ka nav iespējams nosūtīt pašam savus headerus. Tādā gadijumā
var palīdzēt PHP konfigurācijas direktīva output_buffering. Ja norādīsim tai vērtību
On, tad PHP interpretātors apstrādājot skriptu, uzreiz neko nesteigsies sūtīt
pārlūkam, bet vispirms saglabās vienā bufferī visus no skripta sūtītos
headerus, bet citā bufferī HTML tagus un pārējos simbolus. Kad skripts
beigs darbu, tad PHP vispirms nosūtīs visus headerus, bet pēc tam tikai izvadīs
pārējo. Tādejādi headeru nosūtošās funkcijas drīkst būt jauktas kopā ar HTML
tagiem.

Uzmanību! Lai gan šāda iespēja pastāv,
nevajag to izmantot tikai tāpēc, ka tas ir iespējams. Ja rakstat jaunu kodu nevis mēģinat tikt galā
ar kādu vecu sistēmu,
tad iesaku rakstīt kodu, ar
output_buffering direktīvu Off. Tādejādi Jūs piespiedīsiet
sevi būt organizētākam.

Noslēgumā

Šis dokuments ir sagatavots, lai palīdzētu jaunienācējiem PHP izstrādē. Ja
jums ir padomi
kā uzlabot šo dokumentu, vai arī es esmu kautko aizmirsis pieminēt vai pieļāvis
kādu kļūdu, tad rakstiet par to komentāros.