Dnes nás čeká poslední díl seriálu, jehož cílem bylo seznámit vás se základy tvorby internetových aplikací na straně serveru. Na závěr se budeme věnovat koláčkům -- v originále cookies -- což je velice zajímavá technologie, která dala název i dnešnímu článku. Zopakujme si nejprve stručně na jakém principu cookies pracují.
Samotný protokol HTTP je bezstavový -- pro stažení každé stránky se musí vytvořit nové spojení, po kterém se obsah stránky přenese. Server tedy neví, zda danou stránku posílá podesáté jednomu uživateli anebo zda stránku desetkrát poskytl různým uživatelům. Přitom na mnoha stránkách by šlo tuto informaci využít. Server by monitoroval pohyb uživatele po stránkách a na základě jeho vyhodnocení by uživateli poskytoval personalizované stránky -- např. se specifickou reklamou či informacemi.
Výše zmíněného nedostatku si byla vědoma firma Netscape a vytvořila proto cookies, které brzy začaly podporovat i všechny další významné firmy ve svých prohlížečích. Cookies jsou rozšířením protokolu HTTP -- přidávají do něj dvě nové hlavičky.
První hlavičku Set-Cookie
používá server při odesílání
odpovědi prohlížeči. V této hlavičce může server prohlížeči poslat
informace. Získané informace si prohlížeč uloží do souboru. Pokud pak
uživatel v budoucnu přistupuje ke stejnému serveru, prohlížeč uložené
informace pošle zpět serveru v hlavičce Cookie
. Server na
základě obdržených informací v cookies zjistí, že uživatel se k serveru
připojil již dříve a může na to patřičně zareagovat.
Každá cookie má své jméno a můžeme do ní uložit hodnotu. Kromě toho lze u každé cookie nastavit dobu její platnosti a další atributy.
Práce s cookies je v PHP velice snadná. K odeslání cookie klientovi
slouží funkce SetCookie()
. Tuto funkci musíme volat ještě
předtím, než náš skript generuje nějaký výstup v HTML, protože cookies
jsou součástí HTTP-hlaviček.
Naopak cookies, které v požadavku na náš skript odeslal prohlížeč máme přístupné v proměnných odpovídajícího jména podobně jako pole formuláře.
Použití cookies si ukážeme na jednoduchém příkladě -- vytvoříme stránku, která každému uživateli zobrazí, kolikrát ji navštívil:
<? if (!IsSet($pocetPristupu)) $pocetPristupu = 0; $pocetPristupu++; SetCookie("pocetPristupu", $pocetPristupu); ?> <HTML> <HEAD> <TITLE>Vítejte na serveru s podporou koláčků</TITLE> </HEAD> <BODY> <? if ($pocetPristupu==1): ?> <H1>Vítejte nový uživateli</H1> <?else:?> <H1>Ahoj starý brachu -- my už se známe</H1> Na našem serveru jste již po <?echo $pocetPristupu?>. <?endif?> </BODY> </HTML>Skript nejprve testuje, zda mu prohlížeč poslal cookie s názvem
pocetPristupu
. Pokud ne, zinicializuje proměnnou
$pocetPristupu
. Následně počet přístupů zaktualizujeme a
odešleme zpět klientovi. Zobrazená stránka se liší podle toho, zda
uživatel ke stránce přistupuje poprvé nebo již poněkolikáté (viz obr. 1
a 2).
Funkce SetCookie
má i několik nepovinných parametrů.
Celkem můžeme použít šest následujících parametrů:
SetCookie(jméno, hodnota, platnost, cesta, doména, zabezpečení)Platnost udává časový okamžik, do kdy je obsah cookie platný. Čas se udává jako počet sekund od začátku 1. ledna 1970. S výhodou můžeme použít funkci
Time()
, který vrací aktuální čas v
tomto formátu. K nastavení cookie s platností jedna hodina (3600 sekund)
můžeme použít volání:
SetCookie("Kategorie", "obchod", Time()+3600)Pomocí parametrů cesta a doména můžeme rozšířit platnost cookie. Normálně je cookie platná -- je zasílána prohlížečem zpět serveru -- pouze pokud se shoduje doména a úvodní část cesty ke skriptu, který cookie odeslal prohlížeči. Pokud chceme rozšířit platnost cookie na celý server, použijeme jako parametr cesta lomítko '
/
'. Pomocí parametru doména můžeme rozšířit
platnost cookie na celou doménu. Pokud jako hodnotu uvedeme
např. 'firma.cz
', bude cookie platná pro všechny
servery v doméně firma.cz.
Pokud jako hodnotu posledního parametru zabezpečení,
uvedeme true
, bude cookie zaslána pouze, pokud je mezi
serverem a klientem vytvořeno bezpečné spojení pomocí SSL (Secure Socket
Layer).
Cookies můžeme z prohlížeče smazat dvěma způsoby -- buď jako hodnotu cookie pošleme prázdný řetězec nebo platnost cookie nastavíme do minulosti. V obou dvou případech je nepotřebná cookie odstraněna z prohlížeče a nezabírá zbytečné místo na disku uživatele.
Použití cookies si nyní ukážeme na složitějším příkladě. Naším úkolem bude vytvořit server, který si od každého uživatele při vstupu na hlavní stránku vyžádá zodpovězení jedné z otázek. Otázka bude pro uživatele vybrána náhodně, ale zároveň bychom jednomu uživateli neměli vícekrát pokládat tutéž otázku.
Budeme dále předpokládat, že jednotlivé otázky máme uloženy v
databázové tabulce Dotaznik
. U každé otázky máme uloženo
její identifikační číslo, znění a počet hlasů pro a proti.
V řešení našeho úkolu nám výborně pomohou cookies, protože právě pomocí nich si budeme u každého uživatele evidovat, na které otázky již odpověděl. Z databáze při přihlášení k serveru vybereme vždy dosud nepokládanou otázku -- to zamezí zbytečnému zkreslení výsledků tím, že někdo vícekrát odpoví na jednu otázku.
Protože pro každého uživatele potřebujeme evidovat větší počet
zodpovězených otázek, využijeme toho, že více cookies může mít stejné
jméno a různou hodnotu -- v PHP s nimi pak pracujeme jako s polem. My si
cookie pojmenujeme zodpovezeneDotazy
. Následující skript z
tabulky náhodně vybere jeden dosud nepoložený dotaz a zobrazí jej:
<HTML> <HEAD> <TITLE>NÁZORY.CZ</TITLE> </HEAD> <BODY> <? $sql = "select * from Dotaznik"; if (IsSet($zodpovezeneDotazy)) $sql .= " where ID not in (". Implode($zodpovezeneDotazy, ","). ")"; do { MySQL_Connect("localhost"); $vysledek = MySQL("test", $sql); $pocet = MySQL_NumRows($vysledek); if ($pocet==0) break; SRand((double)MicroTime()*1e6); $aktualni = Rand() % $pocet; $id = MySQL_Result($vysledek, $aktualni, "ID"); $dotaz = MySQL_Result($vysledek, $aktualni, "Otazka"); } while (false); if (IsSet($id)): ?> <H1>Pro vstup na server odpovězte na následující otázku:</H1> <FORM ACTION="17-03.php3"> <?echo $dotaz?><BR><BR> <INPUT TYPE=Submit NAME=Odpoved VALUE="Ano"> <INPUT TYPE=Submit NAME=Odpoved VALUE="Ne"> <INPUT TYPE=Hidden NAME=id VALUE="<?echo $id?>"> </FORM> <?else:?> <H1>Dnes to bude bez otázky</H1> <A HREF="17-03.php3">Vstupte na náš server</A> <?endif?> </BODY> </HTML>Pochopení skriptu nechám na laskavém čtenáři, zmíním se jen o pár funkcích PHP, se kterými jsme se dosud nesetkali. Funkce
IsSet()
zjišťuje, zda daná proměnná obsahuje nějakou
hodnotu. Funkce Implode()
vezme jednotlivé prvky pole a
navzájem je spojí do jednoho řetězce -- k oddělení prvků pole v řetězci
se používá znak předaný jako druhý parametr. Voláním
Implode($zodpovezeneDotazy, ",")
tedy získáme seznam
identifikačních čísel již zodpovězených dotazů, který s výhodou
použijeme při zadávání SQL-dotazu, který vybírá pouze dosud nepoložené
otázky.
Příkaz SRand((double)MicroTime()*1e6)
zinicializuje
generátor náhodných čísel. Funkce Rand()
vrací náhodné
číslo. Použitím operátoru modulo (zbytek po dělení) upravíme náhodné
číslo na potřebný rozsah.
Nyní nadešel pravý čas pro skript, který zpracuje odpověď uživatele.
Skript 17-03.php3
má na starosti mnoho věcí -- předně musí
klientovi odeslat cookie, která obsahuje číslo zodpovězené otázky. Poté
musí v databázi aktualizovat počet odpovědí pro/proti u dané otázky.
Nakonec skript vypíše přehled odpovědí na všechny otázky, abychom měli
přehled.
<? if(IsSet($id)) SetCookie("zodpovezeneDotazy[$id]", $id, Time()+2592000); ?> <HTML> <HEAD> <TITLE>NÁZORY.CZ</TITLE> </HEAD> <BODY> <H1>Výsledky hlasování pro všechny dotazy</H1> <TABLE CELLSPACING=0 BGCOLOR=BLACK> <TR BGCOLOR=YELLOW><TH>Otázka<TH WIDTH=120>Ano<TH WIDTH=120>Ne</TR> <? MySQL_Connect("localhost"); $sql = "update Dotaznik"; if ($Odpoved=="Ano") $sql .= " set Ano = Ano + 1"; else $sql .= " set Ne = Ne + 1"; $sql .= " where ID = $id"; $vysledek = MySQL("test", $sql); if (!$vysledek) echo "Nepodařilo se zapsat vaši odpověď."; $sql = "select * from Dotaznik order by Otazka"; $vysledek = MySQL("test", $sql); $pocet = MySQL_NumRows($vysledek); define("SirkaGrafu", 100); for ($i=0; $i<$pocet; $i++): $ano = MySQL_Result($vysledek, $i, "Ano"); $ne = MySQL_Result($vysledek, $i, "Ne"); echo "<TR><TD COLSPAN=3></TR>". "<TR VALIGN=TOP BGCOLOR='#FFFF80'><TD>". MySQL_Result($vysledek, $i, "Otazka"). "<TD ALIGN=RIGHT>$ano ". ($ano ? "<IMG SRC=bluedot.gif WIDTH=". Round(SirkaGrafu/($ano+$ne)*$ano). " HEIGHT=10>" : ""). "<TD ALIGN=LEFT>". ($ne ? "<IMG SRC=reddot.gif WIDTH=". Round(SirkaGrafu/($ano+$ne)*$ne). " HEIGHT=10>" : "")." $ne". "</TR>"; endfor; ?> </TABLE> </BODY> </HTML>U odesílané cookie jsme nastavili platnost na 30 dní -- nepředpokládáme, že jeden dotaz bude na serveru delší dobu a je zbytečné, aby byl prohlížeč uživatele zavalen nepotřebnými cookies. V hranatých závorkách za názvem cookie uvádíme jedinečný index -- PHP nám následně umožní s cookie pracovat jako s polem (to využíváme v prvním skriptu).
Při psaní aplikací nesmíme však na cookies spoléhat. U většiny prohlížečů lze podporu cookies vypnout a někteří uživatelé dělají, protože se zcela neoprávněně bojí úniku osobních informací. Profesionální aplikace by si měla poradit i s touto situací a pracovat správně -- třeba jen s omezeným uživatelským komfortem.
Zákusek máme úspěšně za sebou. Končí i seriál Aplikace na Webu, se kterým jste na stránkách Computerworldu setkávali více než půl roku. Doufám, že získané poznatky vám byly k užitku a že se brzy společně setkáme u dalších článků věnovaných webovým technologiím. Nečekejte však s rukama složenýma v klíně a napište nám, se kterými tématy a technologiemi byste se nejraději seznámili.