Jako zßkusek si dßme kolßΦky

Ji°φ Kosek ml.

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).

Obr. 1: Prvnφ p°φstup na strßnku s cookies
Prvnφ p°φstup na strßnku s cookies

Obr. 2: DalÜφ p°φstupy na strßnku s cookies
DalÜφ p°φstupy na strßnku s cookies

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.

Obr. 3: Nßhodn∞ vybranß otßzka
Nßhodn∞ vybranß otßzka

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&nbsp;".
      ($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>" : "")."&nbsp;$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).

Obr. 4: V²sledky hlasovßnφ
V²sledky hlasovßnφ

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.

© Ji°φ Kosek 1999