UTF-8 BOM simboli, PHP un "Headers already sent"
7. May, 2007, 22:04 Pamācībasphp.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.


7. May, 2007, 23:14
Neliela piebilde:
Bet reāli ir tā, ka UTF-8 kodējumam šie simboli nav vajadzīgi.
Tā nav īsti taisnība. UTF-8 kodējumā šie BOM simboli protams nenosaka baitu kārtību (endianness), jo utf-8 ir bait-orientēts kodējums. Taču šajā gadījumā šie simboli norāda teksta redaktoram (vai citam softam faila apstrādei), ka fails ir UTF-8 kodējumā. Jo tīri teorētiski redaktors nevar atšķirt vai tu lieto UTF-8 vai kādu Extended ASCII kodējumu (piemēram kādu no ISO/IEC 8859 standarda kodējumiem). Protams, analizējot tekstu visdrīzāk to var noteikt. Taču tīri teorētiski šāds teksta fails (bez BOM baitiem) ir valīds gan UTF-8 kodējumā, gan jebkurā citā ISO/IEC 8859-xx kodējumā.
7. May, 2007, 23:55
Vienkārši nevajag izmantot notepad priekš php kodēšanas.
8. May, 2007, 0:39
Ja tas BOM ar output buffering tomēr tiks izmocīts cauri, tad XHTML dokuments nebūs diezko valīds.
Byte-Order Mark found in UTF-8 File.
The Unicode Byte-Order Mark (BOM) in UTF-8 encoded files is known to cause problems for some text editors and older browsers. You may want to consider avoiding its use until it is better supported.
http://validator.w3.org/check

8. May, 2007, 19:23
Kā ta XHTML dokuments nebūs valīds? XML dokumentā šie BOM simboli ir atļauti, tātad arī XHTMLā šie simboli ir atļauti, jo XHTML ir XML dokuments.
http://www.w3.org/TR/2002/REC-xhtml1-20020801/#xhtml
XHTML documents are XML conforming.
http://www.w3.org/TR/2006/REC-xml11-20060816/#charencoding
Entities encoded in UTF-16 MUST and entities encoded in UTF-8 MAY begin with the Byte Order Mark described in ISO/IEC 10646
Taču tas, ka browseri un validatori nesaprot šo BOM-marku, tā ir cita problēma.
16. May, 2007, 22:29
XHTML is a stricter and cleaner version of HTML.
Bet tas tā - ne pa tēmu.
27. June, 2007, 15:36
Paldies par lielisko rakstu. Tiešām ļoti noderēja un kļūda bija variants nr 2, kas man pašam nekad nebūtu ienākusi prātā. :)
14. July, 2007, 12:46
eeam, vienmēr esmu izmantojis veco labo edit+, kas ar šādām lietām automātiski tiek galā un saglabājot failus utf-8 nekad nav bijušas problēmas…
tas tikai tā, ja nu kāds meklē alternatīvu notepad++ ;)
26. July, 2007, 6:28
Labs raksts, jāpiemin, ka veselu dienu ar tiem BOM simboliem nočakarējos citā sakarā, nevis ar HEADER. Karoče nolasa txt failu, kur stāv var teikt 2D masīvs ar šādu txt faila saturu
a1|a11|a12
b1|b12|a13
ievieto visu saturu masīvā, kur a1, b1 ir atslēgas. Un sanāk situācija, ka vienbrīd pēc atslēgas “a1″ iegūst korektu saturu, citā gadījumā ne. Pats dīvainākais, kad vērtība netiek atgriesta, tad kad $_GET masīvs ir tukšs (lapa palaista bez parametriem). Mēģinot konvertēt, netieši salidzinā šo $_GET tukšo masīvu man neizdevās.
28. October, 2007, 15:47
Nu , a, ko iesakat darīt tad, ja man piem ir index.php fails, šams saņem $_GET informāciju in inklūdo vajadzīgo failu piem ?kat=admin inklūdo admin.php, kurš savukārt pārbauda, vai ir sesija, ja nav, tad redirekto lietojot hederi.. Ko ieteiktu darīt šādā gadījumā?
28. October, 2007, 15:55
Tajā index.php failā nedrīkst neko izdrukāt. Nedrīkst izdrukāt vispār neko pirms tās pārbaudes un redirekta.
28. October, 2007, 15:59
Nu to es zinu, bet iesaki, ko drīt, jo lapas struktūra ir aptuveni tāda - index.php, kur ir viss layouts un, kur centrā tiek inklūdoti vajadzīgie faili, viens, moš, ir jau pārbaudīt lapas sākumā tieši to, vai nepieprasa admin paneli.
28. October, 2007, 16:54
Nu - tu pats pareizi izdomāji. Vari jau arī ar output buffering izlīdzēties. Kā jau rakstīju - tās ir “vieglās zāles” šādiem gadijumiem.
29. October, 2007, 0:32
Nu labi, tam gadījumam ok, bet ja piem, man atkal ir tas index.php, kurā inklūdojās ‘komentāru sadaļa’ vēlos, lai pēc datu nosūtīšanas nosūtās hederis, bet tas atkal nav iespējams..
29. October, 2007, 8:27
Nu tas tikai nozīmē, ka nav īsti labi ar koda arhitektūru. Vajag skriptus veidot tā, lai HTML izvade notiku pašās beigās.
14. April, 2008, 15:08
Dažreiz ja patiešām vajag nosūtīt headeri pa vidu html kodam, tad var izmantot tā sauktās “Output Control Functions”, sākumā palaižam ob_start(), dokumenta pašā sākumā, tad beigās izsaucam ob_end_flush(). Šajā gadījumā php saģenerētais html tiek sūtīts ar ob_end_flush() funkcijas palīdzību, pirms tam nekas netiek sūtīts pārlūkam, izņemot headerus. Izmantojot šīs funkcijas var strādāt arī ar izslēgtu output_buffering direktīvu. Sīkāk, protams, par šām lietām var palasīt php dokumentācijā.
21. July, 2009, 20:34
Liels paldies palīdzēja man session_start _^^
16. June, 2010, 16:43
Afigetj. Visu laiku čakarējos ar parasto notepad. Nemaz nezināju par tādu notepad++.
Tagad pavisam cita aršana.
Paldies par help :)
17. June, 2010, 10:26
Nja, aršana gan cita, bet vai labāk, tas ir cits jautājums.
Te arī sākas problēmas.
Ar notepad++ izlaboju visu php failu. Saglabāju UTF-8 bez BOM. No sākuma itkā viss ok, lapu refresh fai F5, viss kārtībā. bet kā pārlūkā atveru pa jaunam tā garumzīmju vietā rāda ķeburus. Reāli sanāk tā, ka utf-8 ar BOM, rāda visu ok, tikai ar HEADER… erroru, bet bez BOM HEADER… errors pazūd, un visas garumzīmes arī pazūs…
Pat nezinu ko lai iesāk. Izpalīdziet lūdzu :)
17. June, 2010, 11:42
Arturs, izskatās, ka tu pašā HTML neesi norādījis kodējumu un pārlūks līdz ar to nezin kā attēlot.
Iekš <head> tev ir ielikts šis ?
<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″ />
26. June, 2010, 9:17
Nja. auns es esmu :D
Protams ka nebija. Šajā skriptā vispār heada nebija.
Bija include(”html_header.php”); un visa problēma arī bija tajā.
Nezinu no kurienes biju ieņēmis galvā ka tas charset jāraksta “utf8_unicode_ci”. tagad pateicoties tev visur izlaboju uz “utf-8″ i viss strādā kā pa diedziņu :)
Laiels paldies :)