Datan hakeminen muilta sivuilta, välimuisti, handlerit
- RSS-feedin sisällyttäminen omalle sivulle
- HTML-dokumentin sisällyttäminen omalle sivulle
- Apache handlerit
- Sivupohjan käyttöönotto
- Välimuisti
- Lisätehtävä
Käytetään muiden tekemiä WWW-sivuja tai XML-dokumentteja tiedonlähteenä. Kokeillaan kuinka oikeilla HTTP-otsakkeilla ja välimuistilla vähennetään palvelimen kuormaa ja nopeutetaan palvelua. Tutustutaan Apachen handlereihin ja toteutetaan niiden avulla oma sivupohja.
RSS-feedin sisällyttäminen omalle sivulle
- Aloita uusi PHP-ohjelma. Pyritään hakemaan ja parsimaan XML.comin RSS-feedi ja sisällyttämään siitä tärkeimmät osat omalle sivulle.
- Käsiteltävä RSS-feed löytyy osoitteesta: http://www.oreillynet.com/pub/feed/20?format=rss1. Tutki sivun lähdekoodia niin pääset jyvälle RSS-tiedoston rakenteesta.
-
Haetaan RSS-dokumentti PHP-ohjelmaan käyttäen PHP:n DOM-kirjastoa:
// näytetään kaikki tässä vaiheessa pelkkänä tekstinä header("content-type: text/plain"); $url = "http://www.oreillynet.com/pub/feed/20?format=rss1"; // luodaan uusi dokumentti ja haetaan sen sisältö halutusta osoitteesta $doc = new DomDocument(); $doc->load($url); // tulostetaan koko dokumentti print $doc->saveXML(); - Kaivellaan dokumentista yksittäisiä osia. Haetaan ensimmäiseksi yleistietoja:
// dokumentissa tarvittavat nimiavaruudet $rdf_ns = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; $dc_ns = "http://purl.org/dc/elements/1.1/"; $rss_ns = "http://purl.org/rss/1.0/"; // haetaan dokumentin yleistiedot // täytyy käyttää nimiavaruuksia ymmärtävää versiota // getElementsByTagName-metodista $title = $doc->getElementsByTagNameNS($rss_ns, "title"); $link = $doc->getElementsByTagNameNS($rss_ns, "link"); print $title->item(0)->textContent . "\n"; print $link->item(0)->textContent . "\n"; - Hae edellä tehtyyn tapaan description, rights ja language elementit ja tulosta niiden tekstisisältö. Huomaa ottaa käyttöön oikea nimiavaruus ($rss_ns tai $dc_ns)
- Muuta dokumenttisi mediatyypiksi text/html ja poista edellä tehdyt
tulostuslauseet. Kopioi itsellesi pohja.xml-tiedosto. Luo uusi DomDocument ja avaa
tähän pohjaksi pohja.xml-tiedosto:
$oma = new DomDocument(); $oma->load("pohja.xml"); -
Lisää $oma-dokumenttiin RSS-feedistä kaiveltu pääotsikko ja tulosta koko $oma-dokumentti:
// on syytä huomata, että id pitää määritellä // <body xml:id="foo"> // jos dokumentti olisi ladattu komennolla loadHTMLFile niin tavallinen // id="foo" olisi riittänyt $body = $oma->getElementById("foo"); $h1 = $oma->createElement("h1"); $h1->appendChild( $oma->createTextNode( $title->item(0)->textContent ) ); $body->appendChild($h1); print $oma->saveXML();Testaa myös selaimella
- Lisää edellä tehtyyn tapaan pääotsikosta linkki RSS-feedistä saatuun osoitteeseen (link). Lisää myös muiden rss-feedistä kaivamiesi elementtien sisältö xhtml-dokumenttiisi esim. tekstikappaleina.
- Hae RSS-feedistä kaikki item-elementit. Käy nämä kaikki läpi ja luo xhtml-dokumenttiin linkkilista jossa yksi lista-alkio muodostuu kunkin item-elementin sisältämästä title- ja link-elementistä.
HTML-dokumentin sisällyttäminen omalle sivulle
- HTML-dokumentin parsiminen tapahtuu PHP:ssa aivan samalla tavalla kuin
edellä parsittiin RSS-dokumenttia. Luo uusi php-tiedosto
ja hae siihen tiedot valuuttakursseista:
$url = "http://www.bof.fi/ohi/fin/0_new/0.1_valuuttak/fix-fin-bw.htm"; $doc->load($url);Kokeile minkälaisen virheilmoituksen php antaa... Tutki hieman valuuttasivun lähdekoodia
-
Suomen pankin valuuttalistaussivu ei ole nähnytkään ehjää HTML- saatikka XHTML-dokumenttia joten
ainakaan XML:nä parsiminen ei onnistu. Kokeillaan seuraavaa:
$url = "http://www.bof.fi/ohi/fin/0_new/0.1_valuuttak/fix-fin-bw.htm"; // säädöt mahdollisimman löysälle ja ladataan HTML-moodissa $doc->recover = true; $doc->strictErrorChecking = false; $doc->loadHTMLFile($url);Tarkista virheilmoitukset
- Ongelma ei vieläkään korjaannu. PHP:hen on olemassa HTMLTidy-kirjasto jolla pystytään
korjaamaan rikkinäisiä HTML-dokumentteja. Tätä kirjastoa ei kuitenkaan löydy oletusasennuksesta
joten joudutaan käyttämään kiertotietä. Ajetaan valuuttasivu WWW:ssä sijaitsevan Tidy your HTML -palvelun kautta ja parsitaan korjattua
versiota:
// $url = "http://www.bof.fi/ohi/fin/0_new/0.1_valuuttak/fix-fin-bw.htm"; // tidylla siivottu versio $url = "http://cgi.w3.org/cgi-bin/tidy?docAddr=http%3A%2F%2Fwww.bof.fi%2Fohi%2Ffin%2F0_new%2F0.1_valuuttak%2Ffix-fin-bw.htm&forceXML=on"; $doc->load($url); - Tee valuuttatiedoista xhtml-dokumenttiin taulukko jossa lukee valuutan nimi, lyhenne ja kurssi
- PHP-sovellus hakee nyt jokaisella suorituskerralla valuuttatiedot
riippumatta siitä onko niissä tapahtunut muutoksia. Parannellaan ohjelmaa ja
tallennetaan kerran valmiiksi tehty sivu tiedostoon:
$oma->saveHTMLFile("valuutta.html"); - Seuraavaksi pitäisi päätellä milloin voidaan käyttää aikaisemmin tehtyä html-tiedostoa ja milloin
se täytyy luoda uudelleen. Lataa valuuttasivu Mozilla Firefoxilla ja katso mitä tietoja
löytyy hiiren kontekstivalikon valinnalla View Page Info. HTTP-otsakkeissa
tulee tieto milloin dokumenttia on viimeksi muutettu (Modified) ja mihin asti
dokumentti on voimassa (Expires). Periaatteessa Expires-kentän tieto
voitaisiin tallentaa vaikka tietokantaan ja jokaisella php-sovelluksen suorituskerralla tutkia
joko dokumentti on vanhentunut ts. expiroitumisaika ylitetty jolloin haettaisiin valuuttakurssit
uudelleen.
Yritetään tässä vaiheessa selvitä ilman tietokantaa ja muutetaan sovellusta sen verran että kysytään valuuttatietoja jakavalta WWW-palvelimelta onko dokumentti muuttunut edellisen käynnin jälkeen. Jos on muutoksia niin ladataan uudet tiedot ja muussa tapauksessa käytetään vanhoja. Käytetään apuna Client URL (CURL)-kirjastoa.
// filemtime palauttaa tiedon milloin tiedostoa on muokattu // gmdate muokkaa kellonajan http-otsakkeeseen sopivaksi // pitäisi myös tarkistaa onko valuutta.html-tiedostoa olemassa. nyt tulee // turha varoitus $modtime = gmdate('D, d M Y H:i:s', filemtime("valuutta.html")) . " GMT"; // alustetaan curl-kirjasto viittaamaan tutkittavaan osoitteeseen $ch = curl_init($url); // asetetaan http-otsake If-Modified-Since, joka kysyy palvelimelta // onko tämän ajan jälkeen tullut muutoksia curl_setopt ($ch, CURLOPT_HTTPHEADER, Array("If-Modified-Since: $modtime")); // säädetään curl palauttamaan http-otsikot sekä mahdollinen sivun // sisältö curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt ($ch, CURLOPT_RETURNTRANSFER, true); // ajetaan curl. $tmp = curl_exec ($ch); // parsitaan curlin palaute helpommin käsiteltävään muotoon // palauttaa taulukon jonka ensimmäisessä alkiossa on // http-koodi (normaalisti 200 tai 304) // toisessa alkiossa http-otsakkeet // ja kolmannessa alkiossa mahdollinen sisältö $apu = parse_response($tmp); // jos koodi on 200 niin sisältö on muuttunut // ja saatiin uusi sisältö if ( $apu[0] == "200" ) { $doc = new DomDocument(); // data voidaan ladata suoraan curlin antamana $doc->loadXML($apu[2]); // Lisäillään tässä välissä valuuttatiedot omaan dokumenttiin // ... // ... // ... // tallennetaan oma dokumentti tiedostoon $oma->saveHTMLFile("valuutta.html"); print $oma->saveXML(); } // muussa tapauksessa http-koodi on joko 304 (not modified) // tai virhe jolloin aina voidaan palauttaa aiemmin tehty versio else { readfile("valuutta.html"); }Koodissa käytetyn parse_response-funktion joudut kopioimaan.
Apache handlerit
Apachen handlereilla saadaan helposti otettua käyttöön oma sivupohja tai lisättyä esim. autentikointi kokonaisen sivuston päälle ilman, että tarvitsee tehdä mitään muutoksia yksittäisiin sivuihin.
- Sijoita edellä tekemäsi php-tiedostot samaan kansioon ja lisää tähän kansioon
.htaccess-tiedosto johon kirjoitat:
# määritellään oman käsittelijän sijainti # korjaa osoite toimivaksi Action oma /~tunnus/php/demo10/template.tmpl # määrätään .php-päätteisille tiedostoille oma käsittelijä AddHandler oma .php #määrätään .tmpl-päätteiset tiedostot php5-käsittelijälle AddHandler php5-script .tmpl
Action-rivin osoite on puoliabsoluutttinen viite käsittelijäohjelmaan (samaan tapaan kuin uudelleenohjauksessa). Action-riville EI laiteta absoluuttista unix-järjestelmän polkua pwd-komennolla. - Kirjoitetaan seuraavaksi oman käsittelijän (template.tmpl) runko:
<?php // www-palvelimelta pyydettävän tiedoston polku $file = $_SERVER["PATH_TRANSLATED"]; // käynnistää puskuroinnin eli ottaa talteen kaiken // sen mitä yritetään tulostaa ja antaa template-nimiselle funktiolle käsiteltäväksi ob_start("template"); // käsittelee varsinaisen tiedoston eli suorittaa sen sisältämän mahdollisen // php-koodin yms. include($file); // puskurointi loppuu ob_end_flush(); // funktio, joka käsittelee puskuriin kerätyn datan // yksinkertaisimmassa tapauksessa ei tee mitään vaan palauttaa suoraan // puskurin sisällön function template($buffer) { return $buffer; } ?> - Kokeile vieläkö aiemmin tekemäsi php-ohjelmat toimivat. Mitään eroa aiempaan ei pitäisi olla havaittavissa vaikka ohjelmasi kulkevat nyt oman käsittelijän kautta
- Muuta template-funktiota sen verran, että se lisää jonkin
merkkijonon puskurissa tulleeseen dataan niin pääset toteamaan, että käsittelijä
todella toimii:
return $buffer . " Kyllä toimii";Tarkista selaimella näkyykö lisätty teksti sivulla
- Omalla käsittelijällä voidaan nyt rakentaa kaikkien php-tiedostojen päälle esim. autentikointi tai lisätä yhtenäiset navigointilinkit (sivupohja)
Sivupohjan käyttöönotto
- DOM-rajapinnan ja oman käsittelijän avulla on helppoa liittää puskuriin tullut dokumentti omaan sivupohjaan. Kopioi käyttöösi valmis sivupohja
- Lisää template-funktioon DOM-rajapinnan avulla seuraavat
toiminnot:
- Luo uusi DOMDocument johon lataat pohjaksi sivupohjan
- Luo toinen DOMDocument johon lataat puskurissa tulleen dokumentin (loadXML)
- Hae puskurissa tulleesta dokumentista title-elementin tekstisisältö ja kopioi se sivupohjadokumenttiisi title-elementin sisällöksi
- Hae puskuroidusta dokumentista kaikki body-elementti ja importoi se sivupohjadokumenttiin ($doc->importNode($node, true))
- Lisää jokainen puskuroidun dokumentin body-elementin lapsisolmuista sivupohjadokumentin body-elementin sisälle
- Palauta lopuksi sivupohjadokumentti johon on nyt sisällytetty kaikki sisältö mitä puskuroidusta dokumentista löytyi
- Kokeile selaimella miten valuuttakurssiohjelmassasi on nyt navigointilinkistö.
Välimuisti
Sivuston yhteisten piirteiden (sivupohjan) käyttöönotta DOM-rajapinnan avulla ei ole kaikista tehokkain tapa. Useimmiten sivujen sisältö ei muutu yhtenään joten kannattaa tallentaa kerran tehty sivu tiedostoon ja luoda se uudestaan vain tarvittaessa tai tietyn ajan kuluttua.
- Lisää template-function loppuun sivun tallentaminen. Tallenna sivu tiedostoon, joka on samanniminen kuin palvelimelta pyydetty tiedosto mutta lisäät tiedoston perään merkkijonon ".cache".
- Lisää pääohjelmaan testi onko tallennustiedosto vanhempi kuin 120 sekuntia ja jos on niin silloin tehdään puskurointi ja luodaan uusi tiedosto, muussa tapauksessa palautetaan suoraan aikaisemmin tallennettu tiedosto. Kellonajan saat time()-funktiolla ja tiedoston päivitysajan filemtime-funktiolla.
- Dokumentti voi olla tallessa myös selaimen välimuistissa tai proxyssa selaimen
ja WWW-palvelimen välissä. Varmistetaan, että selain ei turhaan pyydä sivua
uudestaan ennen kuin haluttu aika (120 sekuntia) on kulunut. Lisää ohjelmaasi
seuraavat HTTP-otsakkeet:
// sallitaan välimuistiin tallentaminen. Oletuksena PHP:ssä // sivu ladattaisiin aina uudelleen Header("Cache-Control: Public"); Header("Pragma: Public"); // viimeksi muokattu silloin kun cachetiedosto on luotu header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($file.".cache")).' GMT'); // expiroitumisaika on cachetiedoston luomisaika + $expiry_time (esim. 120) header("Expires: " . gmdate('D, d M Y H:i:s', filemtime($file.".cache")+$expiry_time) . ' GMT'); - Mikään ei kuitenkaan estä WWW-selainta tiedustelemasta onko sivua muutettu tietyn ajankohdan
jälkeen. Jos uutta tiedostoa ei tarvi luoda ja selain kysyy If-Modified-Since-otsakkeella
onko tiedosto muuttunut niin voidaan vastata suoraan Not Modified - 304
eikä tarvitse siirtää edes cache-versiota. Lisää seuraava tarkistus
ennen valmiin tiedoston palauttamista:
// verrataan onko MODIFIED_SINCE sama kuin cachen luontiaika jos on palautetaan not modified // vielä parempi olisi verrata joskos modified_since on pienempi kuin cachetiedoston expiroitumisaika // mutta vaatii HTTP-otsakkeen muuntamisen vertailukelpoiseksi päivämääräksi if ( isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) <= $modtime ) { header("HTTP/1.0 304 Not Modified"); exit; } - Asenna Firefox-selaimeen Tamperdata-laajennos. Aktivoi laajennos ja kokeile latailla tekemiäsi php-ohjelmia. Jos teit tarkistukset ja HTTP-otsikot oikein niin selaimen pitäisi osata tarvittaessa hakea sivu suoraan omasta välimuististaan (ei mitään response-otsakkeita) tai saada palvelimelta vastauksena 304 (Not Modified) jos sivua ei tarvinnut hakea uudelleen.
- Jos sivusto koostuu pelkästään staattisista sivuista joiden päälle lisätään yhtenäinen navigointi niin sivujen vanhenemisajan voi perustaa suoraan staattisten sivujen muutosaikoihin.
Lisätehtävä
Lisää tekemillesi php-dokumenteilla käsittelijän avulla autentikointi luennon mallin mukaan.

