19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla 337 OPPITUNTI 19 Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla HTTP on tilaton protokolla. Se merkitsee sitä, että jokainen käyttäjän lataama sivu edustaa erillistä yhteyttä. Toisaalta Web-sivustoa pidetään käyttäjien ja julkaisijoiden toimesta ikäänkuin ympäristöinä, avaruuksina, joiden sisällä jokainen sivu on osa laajempaa kokonaisuutta. Siksi ei ole lainkaan yllättävää, että strategiat, joilla tietoa viedään sivulta toiselle, ovat yhtä vanhoja kuin itse Webikin. Tällä tunnilla tutkimme kahta menettelyä tallentaa tietoa sivulta toiselle, jotta tieto olisi peräkkäisten sivujen käytettävissä. Tämän tunnin aiheita ovat seuraavat: Mitä evästeet ovat Kuinka eväste luetaan Kuinka eväste asetetaan Kuinka evästeitä käytetään tallentamaan sivuston käyttöön liittyvää informaatiota tietokantaan Kyselymerkkijonot Kuinka muodostetaan funktio, joka muuntaa assosiatiivisen taulukon kyselymerkkijonoksi
338 19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla Evästeet Netscape loi maagisen evästeen Netscapen ykkösversion aikaan. Nimen voisi olettaa perustuvan onnen kekseihin. Noista ajoista lähtien standardi on otettu huomioon myös muiden selainvalmistajien toimesta. Eväste on tiedonpalanen, joka tallennetaan selaimen toimesta palvelin- tai skriptipyynnön yhteydessä. Palvelin voi pyytää tallentamaan enintään 20 evästettä käyttäjän selaimen toimesta. Yksittäisen evästeen koko on enintään 4 kilotavua. Kun eväste on asetettu, vain sen synnyttänyt palvelin voi lukea tietoa, jolloin taataan käyttäjän yksityisyys. Käyttäjä voi lisäksi laittaa selaimensa asetukset sellaisiksi, että evästeiden laittamisesta ilmoitetaan, tai hän voi jopa estää evästeiden käytön. Tästä syystä evästeitä tulisi käyttää varoen eikä niistä tulisi tehdä sivuston olennaisia elementtejä varoittamatta käyttäjää. Evästeet voivat kuitenkin olla erinomainen keino tallentaa pieniä määriä tietoa käyttäjästä ja viedä tuo tieto sivulta toiselle ja jopa käyntikerralta toiselle. Evästeen anatomia Evästeet asetetaan yleensä HTTP-otsikossa (vaikkakin JavaScript voi asettaa evästeen suoraan selaimelta käsin). PHP-skripti, joka asettaa evästeen, saattaa lähettää otsikoita, jotka näyttävät jokseenkin seuraavanlaisilta: HTTP/1.1 200 OK Date: Fri, 04 Feb 2000 21:03:38 GMT Server: Apache/1.3.9 (UNIX) PHP/4.0b3 Set-Cookie: vegetable=artichoke; expires=friday, A 04-Feb-00 22:03:38 GMT; path=/; domain=zink.demon.co.uk Connection: close Content-Type: text/html Kuten näet, Set-Cookie-otsikko sisältää nimi/arvo-parin, GMT-ajan, polun ja domainin. Nimi ja arvo ovat URL-koodattuja. Kenttä nimeltä expires on ohje selaimelle: se kertoo, milloin selain voi unohtaa evästeen. Kenttä nimeltä path määrittää Web-sivuston, jonka alta eväste lähetetään takaisin palvelimelle. Domainkenttä määrittää Internet-domainit, joihin eväste lähetetään. Domain ei voi olla eri domain kuin se, mistä eväste lähetettiin, mutta siinä voi kuitenkin olla hieman joustavuutta. Edellisessä esimerkissä selain lähettää evästeen palvelimelle zink.demon.co.uk ja palvelimelle www.zink.demon.co.uk. Löydät lisätietoa HTTP-otsikoista luvusta 13, "Pintaa syvemmältä". Jos selain asetetaan tallentamaan evästeet, se pitää tiedon, kunnes päättymispäivämäärä kohdataan. Jos käyttäjä siirtyy mille tahansa sivulle, joka vastaa evästeen polkua ja domainia, se lähettää evästeen uudelleen palvelimelle. Selaimen otsikot saattavat näyttää jokseenkin tältä: GET / HTTP/1.0 Connection: Keep-Alive User-Agent: Mozilla/4.6 (X11; I; Linux 2.2.6-15apmac ppc)
19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla 339 Host: zink.demon.co.uk:1126 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */* Accept-Encoding: gzip Accept-Language: en Accept-Charset: iso-8859-1,*,utf-8 Cookie: vegetable=artichoke PHP-skriptillä on tämän jälkeen pääsy evästeeseen ympäristömuuttujan HTTP-COOKIE (joka tallentaa kaikkien evästeiden tunnisteet ja arvot), globaalin muuttujan $vegetable tai globaalin taulukkomuuttujan HTTP_COOKIE_VARS["vegetable"] kautta: print "$HTTP_COOKIE<BR>"; // tulostaa "vegetable=artichoke" print getenv("http_cookie")."<br>"; // tulostaa "vegetable=artichoke" print "$vegetable<br>"; // tulostaa "artichoke" print "$HTTP_COOKIE_VARS[vegetable]<BR>"; // tulostaa "artichoke" Evästeen asettaminen PHP:llä PHP-skriptissä voidaan asettaa eväste kahdella eri tavalla. Voit käyttää header()-funktiota asettamaan Set- Cookie-otsikko. Tutustuit tähän header()-funktioon jo luvussa 9, "Lomakkeiden käsittely". Funktio ottaa argumentikseen merkkijonon, joka sitten sijoitetaan palvelimen vastauksen otsikko-osaan. Koska otsikot lähetetään automaattisesti puolestasi, header()-kutsun on oltava ennen kaikkia muita selaintulostuksia. header ("vegetable=artichoke; expires=friday, 04-Feb-00 22:03:38 GMT; path=/; domain=zink.demon.co.uk"); Vaikka tämä menettely ei olekaan kovin vaikea, evästeen asettaminen tällä tavoin vaatii muodostamaan funktion, joka kehittää otsikon merkkijonon. Päivämäärän muotoilu (kuten tässä esimerkissä) ja nimi/arvoparin URL-koodaus eivät olisi kovinkaan hauskoja tehtäviä. Kyseessä olisi joka tapauksessa pyörän uudelleen keksiminen, koska PHP tarjoaa käyttöön funktion, joka tekee juuri nuo toiminnot. Funktio nimeltä setcookie() tekee nimensä mukaisesti sen mitä pitääkin: se tulostaa Set-Cookie-otsikon. Siitä syystä sitä tulee kutsua ennen kuin selaimelle lähetetään mitään muuta tietoa. Funktio ottaa argumenteikseen evästeen nimen, evästeen arvon, päättymispäivämäärän UNIXin muodossa, polun, domainin ja kokonaisluvun, jonka arvo on 1, jos eväste lähetetään turvallisen yhteyden yli. Kaikki muut argumentit paitsi ensimmäinen (evästeen nimi) ovat valinnaisia. Listaus 19.1 käyttää setcookie()-funktiota evästeen asettamiseen.
340 19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla Listaus 19.1 Evästeen arvon asettaminen ja tulostaminen 1: <?php 2: setcookie( "vegetable", "artichoke", time()+3600, "/", 3: "zink.demon.co.uk", 0 ); 4:?> 5: <html> 6: <head> 7: <title>listing 19.1 Setting and printing a cookie value</title> 8: </head> 9: <body> 10: <?php 11: if ( isset( $vegetable ) ) 12: print "<p>hello again, your chosen vegetable is $vegetable</p>"; 13: else 14: print "<p>hello you. This may be your first visit</p>"; 15:?> 16: </body> 17: </html> Vaikka asetamme evästeen, kun skripti ajetaan ensimmäistä kertaa, ei $vegetable-muuttujaa aseteta tällä kohtaa. Eväste vain luetaan, kun selain lähettää sen palvelimelle. Sitä ei tapahdu ennen kuin käyttäjä vierailee domainin sivulla uudelleen. Asetamme evästeen nimeltä "vegetable" ja evästearvon "artichoke". Käytämme time()-funktiota sieppaamaan nykyisen aikaleiman ja lisäämään siihen 3600 (tunnissa on 3600 sekuntia). Tuo kokonaissumma kuvaa päättymisajankohtaa. Määrittelemme poluksi "/", mikä tarkoittaa sitä, että eväste tulee lähettää alueen jokaisen sivun kohdalla. Asetamme domainiksi "zink.demon.co.uk", mikä tarkoittaa sitä, että eväste lähetetään jokaiselle tuon ryhmän palvelimelle (esimerkiksi palvelimille www.zink.demon.co.uk sekä dev.zink.demon.co.uk). Jos haluat evästeen palautettavan vain palvelimelta, joka toimii skriptisi alustana, voit käyttää ympäristömuuttujaa nimeltä $SERVER_NAME sen sijaan, että kirjoittaisit palvelimen nimen suoraan. Tämän menettelyn lisäetuna on se, että koodi toimii odotetusti, vaikka siirtyisit uuden palvelimen käyttöön. Lopuksi viemme arvon 0 setcookie()-funktiolle, jolla ilmoitamme, että evästeet voidaan lähettää ei-turvallista yhteyttä käyttäen. Vaikka voitkin ohittaa kaikki muut paitsi ensimmäiset argumentit, on hyvä antaa kaikki muut argumentit paitsi domain ja turvallisuus. Tämä siksi, että polkuargumentti vaaditaan joidenkin selainten toimesta, jotta evästeet toimisivat odotetusti. Ilman polkuargumenttia eväste voidaan lähettää vain nykyisen kansion tai sen alikansioiden asiakirjoille. Viemällä setcookie()-funktiolle tyhjän merkkijonon ("") merkkijonoargumenttien paikalla ja nollan (0) kokonaislukuargumenttien sijaan, nuo argumentit ohitetaan.
19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla 341 Evästeen tuhoaminen Eväste voidaan tuhota kutsumalla setcookie()-funktiota ja antamalla vain evästeen nimi argumenttina: setcookie( "vegetable" ); Se ei kuitenkaan toimi aina oikein ja siihen ei pitäisi luottaa. On turvallisinta asettaa päättymisajankohta: setcookie( "vegetable", "", time()-60, "/", "zink.demon.co.uk", 0); Sinun tulisi myös varmistaa, että viet setcookie()-funktiolle saman polun, domainin ja turvallisuusparametrin kuin evästeen luonnin yhteydessä. Istuntoevästeiden luominen Voimme luoda evästeen, joka kestää vain istunnon ajan, viemällä setcookie()-funktiolle päättymisajankohdaksi arvon 0. Kun selain on päällä, evästeet palautetaan palvelimelle. Selain ei muista kuitenkaan evästettä sen jälkeen kun selain suljetaan ja avataan uudelleen. Tällainen menettely voi olla hyödyllistä skripteissä, jotka todentavat käyttäjän evästeen avulla ja sallivat jatkuvan pääsyn henkilökohtaisiin tietoihin useilta eri sivuilta, kun salasana on lähetetty. Yleensä et halua, että selain pääsee noille sivuille sen jälkeen kun se on käynnistetty uudelleen, koska et voi olla lainkaan varma siitä, että käyttäjä on sama kuin edellisellä kerralla. setcookie( "session_id", "55435", 0 ); Esimerkki sivuston käytön seuranta Kuvittele, että meille on annettu tehtäväksi kerätä tietoa käyttäjistä evästeiden ja MySQL-tietokantojen avulla. Asiakas haluaa meidän keräävän tietoa yksittäisistä käyttäjistä, keskimääräisen latausten määrän ja keskimääräisen vierailuajan. Ensimmäisenä tehtävänämme on selittää evästeiden rajallisuus asiakkaalle. Ensiksikin kaikki kävijät eivät salli evästeiden käyttöä selaimellaan. Jos evästettä ei hoideta selaimen toimesta, evästeskripti olettaa todennäköisesti, että kyseessä on aina asiakkaan ensimmäinen vierailu. Niinpä tilastotiedot vääristyvät, kun mukana on selaimia, jotka eivät tue evästeitä. Edelleen on sanottava, että emme voi olla varmoja siitä, että sama käyttäjä käyttää jatkuvasti tiettyä selainta; usein selain on pikemminkin usean käyttäjän käytössä. Kun rajallisuus on asiakkaan tiedossa, voimme mennä eteenpäin. Voimme itse asiassa muodostaa toimivan esimerkin alle sadalla koodirivillä! Ensin meidän on luotava tietokannan taulukko, jossa ovat taulukon 19.1 sisältämät kentät. Taulukko 19.1 Tietokannan kentät Nimi Tyyppi Kuvaus id Kokonaisluku Automaattisesti kasvatettava kenttä, joka tuottaa ja tallentaa uniikin ID:n kullekin käyttäjälle first_visit Kokonaisluku Aikaleima, joka edustaa ensimmäisen
342 19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla sivupyynnön ajankohtaa last_visit Kokonaisluku Aikaleima, joka edustaa viimeisimmän sivupyynnön ajankohtaa num_visits Kokonaisluku Käyttäjälle jaettujen istuntojen lukumäärä total_duration Kokonaisluku Arvioitu kokonaisaika, joka on vietetty sivustossa (sekunneissa) total_clicks Kokonaisluku Käyttäjän tekemien pyyntöjen kokonaismäärä Voimme luoda MySQL-taulukon nimeltä track_visit seuraavalla CREATE-lauseella: create table track_visit ( id INT NOT NULL AUTO_INCREMENT, PRIMARY KEY( id ), first_visit INT, last_visit INT, num_visits INT, total_duration INT, total_clicks INT ); Nyt, kun meillä on käytössämme taulukko, meidän on kirjoitettava koodia, joka avaa tietokantayhteyden ja tarkistaa evästeen olemassaolon. Jos eväste ei ole olemassa, luomme uuden rivin taulukkoomme ja asetamme alkuarvot kenttiin. Se tehdään listauksessa 19.2. Listaus 19.2 Skripti, joka lisää uuden käyttäjän tiedot MySQL-tietokantaan 1: <?php 2: $link = mysql_connect( "localhost", "harry", "elbomonkey" ); 3: if (! $link ) 4: die( "Couldn't connect to mysqld" ); 5: mysql_select_db( "sample", $link ) or die ( mysql_error() ); 6: if (! isset ( $visit_id ) ) 7: $user_stats = newuser( $link ); 8: else 9: print ``Welcome back $visit_id<p>"; 10: function newuser( $link ) 11: {
19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla 343 12: // uusi käyttäjä! 13: $visit_data = array ( 14: first_visit => time(), 15: last_visit => time(), 16: num_visits => 1, 17: total_duration => 0, 18: total_clicks => 1 19: ); 20: $query = "INSERT INTO track_visit ( first_visit, 21: last_visit, 22: num_visits, 23: total_duration, 24: total_clicks ) "; 25: $query.= "values( $visit_data[first_visit], 26: $visit_data[last_visit], 27: $visit_data[num_visits], 28: $visit_data[total_duration], 29: $visit_data[total_clicks] )"; 30: $result = mysql_query( $query ); 31: $visit_data[id] = mysql_insert_id(); 32: setcookie( "visit_id", $visit_data[id], 33: time()+(60*60*24*365*10), "/" ); 34: return $visit_data; 35: } 36:?> Kytkeydymme MySQL-palvelimelle normaaliin tapaan ja valitsemme tietokannan, joka sisältää taulukkomme (saat lisätietoa MySQL-kannoista luvusta 12, "Tietokantojen käyttö"). Testaamme nyt muuttujan $visit_di läsnäolon; se on evästeen nimi, joka identifioi yksittäisen käyttäjän. Jos muuttujaa ei ole olemassa, oletamme, että olemme tekemisissä uuden käyttäjän kanssa ja kutsumme funktiota nimeltä newuser(). Funktio ottaa argumentikseen linkkitunnisteen ja palauttaa arvotaulukon, jonka lisäämme omaan taulukkoomme. Funktion sisällä luodaan taulukko nimeltä $visit_data. Asetamme sen first_visit- ja last_visitalkioiden arvoiksi nykyisen ajan sekunneissa. Koska kyseessä on ensimmäinen vierailu, asetamme alkioiden num_visits ja total_clicks arvoiksi ykkösen. Tällä vierailulla ei ole kulunut yhtään aikaa, joten alkion total_duration arvoksi asetetaan 0. Käytämme tämän taulukon alkioita luodaksemme uuden rivin taulukkoomme ja asetamme kuhunkin kenttään samannimisen alkion arvon. Koska id-kenttä kasvattaa itse itseään, sitä ei tarvitse asettaa. Voimme saada
344 19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla selville id-kentän arvon funktiolla mysql_insert_id(). Nyt kun meillä on uuden käyttäjän ID, voimme lisätä sen $visit_data-taulukkoon, joka sitten viittaa vierailijan riviin MySQL-taulussa. Lopuksi käytämme setcookie()-funktiota asettamaan visit_id-evästeen ja palauttamaan $visit_datataulukon kutsuvalle koodille. Kun vierailija lataa tämän skriptin seuraavan kerran, $visit_id-muuttujan arvona on visit_id-evästeen arvo. Koska tämä muuttuja on asetettu, käyttäjä yksinkertaisesti toivotetaan tervetulleeksi eikä mtään muita toimintoja tarvitse enää tehdä. Itse asiassa meidän tulee päivittää track_visit-taulukon informaatio, jos havaitsemme tunnetun vierailijan palaamisen sivuille. Meidän on testattava, onko nykyinen pyyntö osa meneillään olevaa vierailua vai uuden vierailun alku. Jos viimeisen pyynnön aika lisättynä tähän ajanjaksoon on suurempi kuin nykyinen, oletamme, että nykyinen pyyntö on osa meneillään olevaa istuntoa. Muutoin toivotamme vanhan kävijän tervetulleeksi. Listaus 19.3 lisää aiempaan koodiimme uuden funktion nimeltä olduser(). Listaus 19.3 Skripti jäljittää käyttäjät evästeiden ja MySQL-tietokannan avulla 1: <?php 2: $slength = 300; // 5 minuuttia sekunneissa 3: $link = mysql_connect( "localhost", "harry", "elbomonkey" ); 4: if (! $link ) 5: die( "Couldn't connect to mysqld" ); 6: mysql_select_db( "sample", $link ) or die ( mysql_error() ); 7: if (! isset ( $visit_id ) ) 8: $user_stats = newuser( $link ); 9: else 10: { 11: $user_stats = olduser( $link, $visit_id, $slength ); 12: print "Welcome back $visit_id<p>"; 13: } 14: function newuser( $link ) 15: { 16: // uusi käyttäjä! 17: $visit_data = array ( 18: first_visit => time(), 19: last_visit => time(), 20: num_visits => 1, 21: total_duration => 0,
19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla 345 22: total_clicks => 1 23: ); 24: $query = "INSERT INTO track_visit ( first_visit, 25: last_visit, 26: num_visits, 27: total_duration, 28: total_clicks ) "; 29: $query.= "values( $visit_data[first_visit], 30: $visit_data[last_visit], 31: $visit_data[num_visits], 32: $visit_data[total_duration], 33: $visit_data[total_clicks] )"; 34: $result = mysql_query( $query ); 35: $visit_data[id] = mysql_insert_id(); 36: setcookie( "visit_id", $visit_data[id], 37: time()+(60*60*24*365*10), "/" ); 38: return $visit_data; 39: } 40: function olduser( $link, $visit_id, $slength ) 41: { 42: // hän on ollut täällä ennenkin! 43: $query = "SELECT * FROM track_visit WHERE id=$visit_id"; 44: $result = mysql_query( $query ); 45: if (! mysql_num_rows( $result ) ) 46: // eväste löydetty, mutta id ei ole tietokannassa: uusi käyttäjä 47: return newuser( $link ); 48: $visit_data = mysql_fetch_array( $result, $link ); 49: // ei käyntejä, joten kasvatus 50: $visit_data[total_clicks]++; 51: if ( ( $visit_data[last_visit] + $slength ) > time() ) 52: // edelleen istunnossa, joten lisää kuluneeseen kokonaisaikaan 53: $visit_data[total_duration] += 54: ( time() - $visit_data[last_visit] );
346 19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla 55: else 56: // uusi vierailu 57: $visit_data[num_visits]++; 58: // päivitä tietokanta 59: $query = "UPDATE track_visit SET last_visit=".time().", 60: num_visits=$visit_data[num_visits], "; 61: $query.= "total_duration=$visit_data[total_duration], 62: total_clicks=$visit_data[total_clicks] "; 63: $query.= "WHERE id=$visit_id"; 64: $result = mysql_query( $query ); 65: return $visit_data; 66: } 67:?> Lisäsimme koodiin uuden globaalin muuttujan nimeltä $slength. Se määrittää ajanjakson, jonka kuluttua oletamme, että uusi vierailu on aloitettu. Jos $visit_id-muuttuja löytyy, tiedämme, että eväste oli käytössä. Kutsumme uutta olduser()-funktiota ja viemme sille tietokantalinkin tunnisteen, $visit_id-muuttujan ja $slength-muuttujan, jonka arvona on 300 sekuntia. Teemme ensin kyselyn tietokantaan olduser()-funktiossa saadaksemme tietoa kävijästä, jonka tiedot on jo tallennettu. Tutkimme track_visit-taulukkoa löytääksemme rivin, jonka id-kentän arvo on sama $visit_idmuuttujan arvo. Voimme sitten testata mysql_query()-funktion palauttamien arvojen määrän funktiolla mysql_num_rows() viemällä sille tulostunnisteen. Jos funktio palauttaa arvon nolla, tiedämme, että kyseessä ei ollut vanha kävijä, joten kutsumme nyt newuser()-funktiota ja päätämme funktion suorituksen. Olettakaamme, että olemme paikantaneet taulukosta rivin, joka vastaa visit_id-evästettä; käytämme sitten mysql_fetch_array()-funktiota täyttämään taulukkomuuttujan ($visit_data) tiedot rivillä olevien kenttien nimillä ja arvoilla. Nykyinen pyyntö edustaa uutta viittausta sivuille, joten kasvatamme alkion $visit_data[total_clicks] arvoa yhdellä. Tarkistamme sitten, onko $visit_data[last_visit]-alkion arvo, johon on lisätty $slength-muuttujassa oleva aikajakso, suurempi kuin nykyinen aika. Jos niin on, viimeisestä viittauksesta on kulunut aikajaksoa vähemmän aikaa, joten voimme olettaa, että tämä pyyntö kuuluu nykyiseen istuntoon. Siksi lisäämme viimeisestä viittauksesta kuluneen ajan $visit_data[total_duration]- alkioon. Jos pyyntö edustaa uutta vierailua, kasvatamme $visit_data[num_visits]-arvoa. Lopuksi käytämme $visit_data-taulukon muuttuneita arvoja päivittämään käyttäjän rivin track_visit-taulukossa ja $visit_datataulukon kutsuvalle koodille. Huomaa, että asetamme last_visit-kentän arvoksi nykyisen ajan. Nyt kun olemme luoneet koodin, meidän tulee luoda nopea funktio demonstroimaan toimintaa. Funktio nimeltä outputstats() yksinkertaisesti laskee käyttäjän keskimääräisarvot ja tulostaa ne selaimelle. Todellisuudessa meidän tulisi luultavasti luoda joitakin analysoituja näyttöjä asiakkaallemme, jotta asiakas saisi kokonaiskuvan tilanteesta. Listaus 19.4 luo outputstats()-funktion. Edellisten esimerkkien koodi on yhdistetty tähän skriptiin include()-lauseella.
19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla 347 Listaus 19.4 Skripti, joka tulostaa käyttöön liittyvää tilastotietoa, jota on kerätty listauksessa 19.3 1: <?php 2: include("listing19.3.php"{); 3: outputstats(); 4: function outputstats() 5: { 6: global $user_stats; 7: $clicks = sprintf( "%.2f", 8: ($user_stats[total_clicks]/$user_stats[num_visits]) ); 9: $duration = sprintf( "%.2f", 10: ($user_stats[total_duration]/$user_stats[num_visits]) ); 11: print "<p>hello! Your id is $user_stats[id]</p>\n\n"; 12: print "<p>you have visited 13: $user_stats[num_visits] time(s)</p>\n\n"; 14: print "<p>av clicks per visit: $clicks</p>\n\n"; 15: print "<p>av duration of visit: $duration seconds</p>\n\n"; 16: } 17:?> Kuva 19.1 esittää listauksen 19.4 tulostuksen. Käytämme include()-lausetta kutsumaan kirjoittamamme jäljityskoodin. Lisäämme samanlaisen rivin jokaiselle asiakkaan sivuston sivulle. Koodin outputstats()-funktio toimii yhdessä globaalin $user_stats-muuttujan kanssa. Joko funktio newuser() tai olduser() palauttaa muuttujan ja se sisältää saman tiedon kuin käyttäjän rivi track_visit-taulukossa. KUVA 19.1 Käyttöön liittyvän tilastotiedon raportointi.
348 19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla Laskeaksemme käyttäjän keskimääräisen viittausten lukumäärän jaamme $user_stats[total_clicks]-alkion arvon vierailujen määrällä. Samalla lailla jaamme $user_stats[total_duration]-alkion arvon samalla luvulla. Käytämme sprint()-funktiota pyöristämään tuloksen kahteen desimaaliin. Jäljellä on vain raportin kirjoittaminen selaimelle. Voisimme tietenkin laajentaa tätä esimerkkiä jäljittämään käyttäjän asetukset sekä selaintyypin ja IP-osoitteet. Kuvittele sivustoa, joka analysoi käyttäjän liikkeet ja muokkaa sisältöä valittujen linkkien perusteella. Kyselymerkkijonojen käyttö Evästeiden heikkoutena on se, että niiden käyttäminen riippuu asiakkaasta. Kyse ei ole pelkästään siitä, salliiko käyttäjä evästeiden käytön vaan myös siitä, että selaimetkin toteuttavat evästeitä eri tavoin. Jotkin selaimet ovat dokumentoineet virheitä, jotka liittyvät siihen, kuinka ne käsittelevät evästeitä. Jos haluat pelkästään säilyttää tilan yksittäisen istunnon ajan, voisi olla järkevää pohtia perinteisempiä keinoja. Kun lähetät lomakkeen GET-metodilla, sen kentät ja arvot URL-koodataan ja lisätään URL-osoitteeseen, johon lomake lähetetään. Tällöin tiedot tulevat palvelimen ja skriptiesi saataville. Olettaen, että lomakkeella on kaksi kenttää, user_id ja name, näyttäisi kyselymerkkijono seuraavalta: http://www.corrosive.co.uk/test5.php?name=344343&user_id=matt+zandstra Kukin nimi ja arvo erotetaan toisistaan yhtäsuuruusmerkillä (=) ja kukin nimi/arvo-pari erotetaan toisistaan etmerkillä (&). PHP koodaa tämän merkkijonon ja asettaa jokaisen parin assosiatiiviseen $HTTP_GET_VARStaulukkoon. Se luo myös globaalin muuttujan kullakin nimellä ja täyttää sen vastaavalla arvolla. Saadaksesi siis esille user_id-arvon, voit käyttää jompaa kumpaa seuraavista muuttujista: $HTTP_GET_VARS[user_id]; $user_id; Kyselymerkkijonojen käyttäminen ei rajoitu pelkästään lomakkeisiin. Voit rakentaa oman kyselymerkkijonosi suhteellisen helposti ja viedä sillä tavoin tietoa sivulta toiselle. Kyselymerkkijonon luominen Luodaksesi kyselymerkkijonon sinun tulee kyetä URL-koodaamaan tarvittavia avaimia ja arvoja. Olettakaamme, että haluat viedä URL:n sivulta toiselle osana kyselymerkkijonoa. Kauttaviivat ja kaksoispiste voivat aiheuttaa ongelmia jäsentelijälle. Siksi meidän on muunnettava URL heksadesimaalimerkeiksi. Voimme tehdä sen PHP:n urlencode()-funktiolla. Se ottaa argumentikseen merkkijonon ja palauttaa koodatun kopion: print urlencode("http://www.corrosive.co.uk"); // tulostaa http%3a%2f%2fwww.corrosive.co.uk Nyt kun osaat URL-koodata tekstisi, sinun tulee muodostaa oma kyselymerkkijonosi. Seuraava koodi muodostaa kyselymerkkijonon kahdesta muuttujasta:
19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla 349 <?php $interest = "arts"; $homepage = "http://www.corrosive.co.uk"; $query = "homepage=".urlencode( $homepage ); $query.= "&interest=".urlencode( $interest );?> <A HREF="newpage.php?<?print $query?>">go</a> Selain näkee linkin URL tällaisena: newpage.php?homepage=http%3a%2f%2fwww.corrosive.co.uk&interest=arts Parametrit homepage ja interest ovat nyt newpage.php-sivun käytössä globaaleina muuttujina. Tämä lähestymistapa on kuitenkin kömpelö. Koska olemme kirjoittaneet muuttujien nimet suoraan kyselymerkkijonoon, koodin uudelleenkäyttö ei onnistu helposti. Viedäksemme tietoa tehokkaasti sivulta toiselle meidän tulee tehdä nimien ja arvojen lisääminen linkkiin helpoksi ja generoitava kyselymerkkijono automaattisesti. Se on erityisen tärkeää silloin, kun haluamme tarjota muille kuin ohjelmoijille hyvän työskentelyalustan PHP:n avulla. Listaus 19.5 luo funktion nimeltä qlink(), joka ottaa argumentikseen assosiatiivisen taulukon ja palauttaa kyselymerkkijonon. Listaus 19.5 Funktio, joka muodostaa kyselymerkkijonoja 1: <html> 2: <head> 3: <title>listing 19.5 A function to build query strings</title> 4: </head> 5: <body> 6: <?php 7: function qlink( $q ) 8: { 9: GLOBAL $QUERY_STRING; 10: if (! $q ) return $QUERY_STRING; 11: $ret = ""; 12: foreach( $q as $key => $val )
350 19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla 13: { 14: if ( strlen( $ret ) ) $ret.= "&"; 15: $ret.= urlencode( $key ). "=". urlencode( $val ); 16: } 17: return $ret; 18: } 19: $q = array ( name => "Arthur Harold Smith", 20: interest => "Cinema (mainly art house)", 21: homepage => ``http://www.corrosive.co.uk/harold/" 22: ); 23: print qlink( $q ); 24: // tulostaa name=arthur+harold+smith&interest=cinema+%28mainly+art+house 25: // %29&homepage=http%3A%2F%2Fwww.corrosive.co.uk%2Fharold%2F 26:?> 27: <p> 28: <a href="anotherpage.php?<? print qlink($q)?>">go!</a> 29: </p> 30: </body> 31: </html> Koodin qlink()-funktio ottaa argumentikseen assosiatiivisen taulukon, jonka se tallentaa muuttujaan $q. Jos $q ei ole asetettu, palautamme yksinkertaisesti nykyisen skriptin kyselymerkkijonon, joka on tallennettu $QUERY_STRING-muuttujaan. Tällä tavoin qlink()-funktiota voidaan käyttää viemään muuttumaton GETpyynnön tieto. Olettaen, että $q on asetettu, alustamme muuttujan nimeltä $ret sijoittamalla siihen tyhjän merkkijonon. Siinä tulee olemaan kyselymerkkijonomme. Funktiossa käytetään foreach-lausetta käymään läpi $q-taulukko ja sijoittamaan kukin avain muuttujaan $key ja kukin arvo muuttujaan $val. Avain/arvo-parit erotetaan toisistaan et-merkillä (&), joten jos emme ole ensimmäisellä silmukkakierroksella, tulostamme tämän merkin. Tiedämme, että $ret-muuttujan merkkijonon pituus on ensimmäisellä kierroksella nolla, joten voimme käyttää sitä seikkaa estämään et-merkin sijoittamisen merkkijonoon. Käytämme urlencode()-funktiota koodaamaan sekä $key- että $val-muuttujat ja lisäämään ne =-merkillä erotettuina $ret-muuttujaan. Lopuksi palautamme koodatun kyselymerkkijonon. Tällä funktiolla voimme viedä tietoa sivulta toiselle käyttäen samalla mahdollisimman vähän php-koodia HTML-elementtien sisällä.
19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla 351 Yhteenveto Tällä tunnilla tutkimme tietojen siirtämistä pyynnöstä toiselle. Menettelyjen avulla voit luoda moninäyttöisiä sovelluksia ja kehittyneitä ympäristöjä, jotka vastaavat käyttäjien asetuksia ja toiveita. Opit käyttämään setcookie()-funktiota, jolla asetetaan evästeitä käyttäjän selaimelle. Kehitimme menettelyä eteenpäin ja loimme tietokannan, jota voidaan käyttää evästeiden kanssa tallentamaan tietoa käyttäjistä istunnosta toiselle. Opit tekemään kyselymerkkijonoja ja koodaamaan niitä sekä kehittämään funktion, joka automatisoi niiden luomisen. K&V K Liittyykö evästeisiin mitään negatiivisia ja vakavia turvallisuus- tai yksityisyysnäkökohtia? V Palvelin voi päästä käsiksi vain evästeisiin, jotka on asetettu sen oman domainin kautta. Vaikka eväste voidaan tallentaa käyttäjän kiintolevylle, ei muualle käyttäjän tiedostojärjestelmään ole pääsyä. On kuitenkin mahdollista asettaa eväste kuvan pyytämisen yhteyteen. Niinpä jos usealla sivustolla on kolmannen osapuolen tarjoamia kuvia, voi tuo kolmas osapuoli jäljittää käyttäjän useiden domainien läpi. K Kyselymerkkijono näyttää rumalta selainikkunassa. Voiko sanoa, että evästeet ovat siistimpi tapa tallentaa tilatietoa? V Valitettavasti asia ei ole noin yksinkertainen. Parhaimmillaan evästeet ovat läpinäkyvä tapa tallentaa tilatietoa. Jotkut käyttäjät asettavat kuitenkin selaimensa varoittamaan joka kerta, kun eväste asetetaan. Nämä käyttäjät pitävät todennäköisesti sellaisia sivustoja, jotka tallentavat tilatietoja säännöllisesti, hieman turhauttavina. Työpaja Työpaja tarjoaa joukon kertauskysymyksiä, joiden avulla voit tarkistaa, oletko ymmärtänyt materiaalin sisältöä. Yritä ymmärtää vastaukset ennen kuin jatkat seuraaviin lukuihin. Vastaukset ovat liitteessä A. Kysymyksiä 1. Millä funktiolla voit asettaa evästeen vierailijan selaimelle? 2. Kuinka voit tuhota evästeen? 3. Millä funktiolla voit kiertää merkkijonon kyselymerkkijonossa? 4. Mikä sisäinen muuttuja sisältää raa'an kyselymerkkijonon? 5. Kyselymerkkijonon mukana viedyt nimi/arvo-parit ovat saatavilla globaaleina muuttujina. Ne voidaan myös sisällyttää sisäiseen assosiatiiviseen taulukkoon. Mikä sen nimi on?
352 19. Tilan tallentaminen evästeiden ja kyselymerkkijonojen avulla Toiminta 1. Luo käyttäjän asetuksia varten lomake, josta käyttäjä voi valita sivun värin ja antaa nimensä. Käytä evästettä varmistamaan, että käyttäjää tervehditään nimellä seuraavilla sivuilla ja että sivuille asetetaan käyttäjän valitsema väri. 2. Muokkaa edellisen tehtävän skriptiä niin, että käyttäjän kirjoittama tieto tallennetaan kyselymerkkijonoon eikä evästeeseen.