Dynaamisten WWW-sivujen tuottaminen PHP-kielen avulla

 

Pauli Kujala

 

LuK-tutkielma

23.10.2000

 

Jyväskylän yliopisto

Tietotekniikan laitos


Sisältö


1. Johdanto

Nykyisin useimmat yritysten ja kotien tietokoneet on verkotettu. Tällöin tietokoneista on modeemin, ISDN:n tai verkkokortin avulla pääsy muualla verkossa sijaitseviin resursseihin, kuten tulostimiin, tiedostopalvelimiin ja sähköpostipalvelimiin.

Organisaation sisäistä verkotusta kutsutaan yleensä paikallisverkoksi (LAN eli Local Area Network). Lähes aina paikallisverkot ovat myös yhteydessä isompaan runkoverkkoon, jotka taas ovat yhteydessä toisiin runkoverkkoihin. Tätä tietokoneverkoista koostuvaa valtavaa verkkoa kutsutaan Internetiksi. Se kattaa nykyään noin 60 miljoonaa tietokonetta ympäri maapallon.

Luvussa 2 kuvaillaan lyhyesti Internetin erästä palvelua, WWW:tä, ja sen käyttömahdollisuuksia.

Luvussa 3 esitellään tutkielman aihe, PHP-kieli.

Luvussa 4 tarkastellaan pintapuolisesti PHP:n syntaksia ottamalla esille yleisimmät PHP-kielen kontrollirakenteet sekä tulostuslauseet.

Luvun 5 esimerkkisovellusten avulla pyritään antamaan käytännön kuva siitä, mitä PHP:llä on mahdollista saada aikaan.

Luvun 6 yhteenvetoon kootaan saadut tulokset sekä vertaillaan PHP:tä muihin vastaaviin tekniikoihin.

LuK-tutkielman tavoitteena on tutustua PHP-kielen tarjoamiin mahdollisuuksiin. Tarkoituksena ei ole kuitenkaan syventyä perinpohjaisesti eri ominaisuuksiin, vaan tutkia pintapuolisesti, kuinka saadaan tehtyä helposti ja nopeasti dynaamisia WWW-sivuja. Tarkasteluun otetaan tärkeimmät perusominaisuudet, joita tarvitaan useimmissa PHP-kieltä käyttävissä HTML-sivuissa. Lisäksi tarkastellaan tietokantojen käyttämistä PHP-kielen avulla.

Tarkoituksena ei ole myöskään tutustua PHP-kielen aliohjelmien syntaksiin kovin syvällisesti. Perinpohjaiset kuvaukset olemassaolevista aliohjelmista ja paljon muuta löytyvät PHP:n kotisivuilta. [PHP-kotisivu]

Jotta PHP:stä muodostuisi kokonaisvaltainen kuva, oletetaan tutkielman lukijan hallitsevan jonkin verran ohjelmointia sekä HTML-sivujen tekemistä. Erityisesti esimerkkikoodien tarkastelussa tarvitaan jonkin verran esitietoja. Luvun 5 esimerkkisovellusten koodeista on selitetty tarkemmin vain merkittävimmät komentokokonaisuudet. Yksittäisiä koodirivejä ei ole yleensä selitetty. Lukijan tehtäväksi jää pohtia yksittäisten komentojen merkitys ja tehtävä koodissa.

2. WWW

WWW eli World Wide Web on yksi Internetin suosituimpia palveluita. WWW:n avulla on mahdollista selata dokumentteja graafisesti ajasta ja paikasta riippumattomasti. Koska Internet koostuu useista eri käyttöjärjestelmiä käyttävistä erilaisista tietokoneista, tulee tiedon esittämiseen käyttää jotakin yhteisesti sovittua tapaa. Tämä tapa on W3C:n eli WWW Consortiumin standardoima HTML-kieli (Hypertext Markup Language) eri versioineen. [W3C-kotisivu]

2.1. HTML

HTML-kielellä kuvataan WWW:hen sijoitettavan dokumentin ("sivun" tai usean sivun eli "sivuston") rakenne ja siihen liitettävät asiat. Pelkän tekstin lisäksi dokumentti voi sisältää kuvia, videokuvaa ja ääntä, mutta näiden esittäminen käyttäjän tietokoneella ns. selainohjelman avulla ei välttämättä suoraan onnistu. Mahdollisuus kuvien käyttöön oli eräs syy Internetin räjähdysmäiseen kasvuun 1990-luvun puolivälissä.

2.2. Selaimet

Selaimeksi kutsutaan ohjelmaa, jolla pystytään lukemaan ja selailemaan WWW:ssä sijaitsevia HTML-kielellä kirjoitettuja dokumentteja. Kun käyttäjä haluaa tutkia jotakin tällaista dokumenttia, kirjoittaa hän selaimeen dokumentin osoitteen eli URLin (Uniform Resource Locator) tai painaa jollakin sivulla olevaa linkkiä, joka johtaa ko. dokumenttiin. Tällöin selain lähettää WWW-dokumentin sisältävälle palvelimelle pyynnön palauttaa haluttu dokumentti.

WWW-palvelin tarkistaa, löytyykö dokumentti oikeasta paikasta ja voidaanko se palauttaa kysyvälle käyttäjälle (selaimelle). Jos voidaan, dokumentti lähetetään selaimelle otsikkotiedoin varustettuna (dokumentin koko, tyyppi, viimeisin muutospäivämäärä, jne.). Jos dokumenttia ei löydy tai ilmenee jokin muu este, WWW-palvelin palauttaa selaimelle virheilmoituksen.

2.3. Staattisia ja dynaamisia WWW-sivuja

Perinteisesti HTML-kieli on koostunut tietystä joukosta standardinimisiä elementtejä eli tageja, jotka kuvaavat dokumentin rakenteen. Näillä voidaan tuottaa staattisia eli muuttumattomia HTML-dokumentteja, joiden päivittäminen ja korjaaminen voi olla hankalaa ja aikaavievää.

Koska ensi-ihastuksen jälkeen HTML-dokumenttien laatijat halusivat kuitenkin helpomman tavan tuottaa dokumentteja, jotka sisältävät muuttuvaa tietoa, oli keksittävä tapa toteuttaa tämä. Syntyi tekniikoita, joilla staattisia WWW-dokumentteja voitaisiin täydentää tai jopa korvata dynaamisella vastineella. Dynaamisten sivujen muodostukseen käytettyjä tekniikoita kutsutaan yhteisnimellä CGI eli Common Gateway Interface. CGI:n avulla on mahdollista sijoittaa dokumenttiin muuttuvaa tietoa, joka voidaan hakea esimerkiksi tietokannasta sen mukaan, kuka käyttäjä on dokumentin selaimellaan pyytänyt.

On kuitenkin muistettava, että HTML ei ole sanan varsinaisessa merkityksessä ohjelmointikieli. Siitä puuttuvat muun muassa toistorakenteet, ehtolauseet ja aliohjelmat, joten varsinaista ohjelmakoodia ei HTML:llä voida saada aikaan.

2.4. Tapoja tuottaa dynaamisia WWW-sivuja

Dynaamisten WWW-sivujen tuottamiseen on saatavilla kymmeniä eri vaihtoehtoja. Historiallisesti ensimmäiset toteutukset olivat puhtaasti WWW-palvelimessa suoritettuja komentojonoja, jotka ajettiin käyttöjärjestelmän komentotulkilla. Nämä tuottivat koko HTML-sivun, joka lähetettiin suoraan selaimelle. Vaikka idea on vanha, ei se suinkaan ole huono, joten vielä tänä päivänäkin käytetään tällaista toimintatapaa. Suosituimpia ovat Perl- ja muut UNIX-komentojonot.

Seuraavan kehitysaskeleen tarjosi HTML-sivujen sisään upotettu dynaamisuus. Tämä mahdollistaa sen, että WWW-palvelimessa koodia suorittava tulkki tai kääntäjä voidaan erottaa itse HTML-koodista, joten HTML-koodin korjaileminen on useissa tapauksissa helpompaa. Tulkki tai kääntäjä suorittaa vain sille tarkoitetut komennot ja jättää muun osan sivusta koskemattomaksi. Tällaisen vaihtoehdon tarjoavat muun muassa Microsoftin ASP sekä tässä tutkielmassa esiteltävä PHP.

Kolmannen ryhmän muodostavat tekniikat, jotka ovat ensimmäisen tavan muunnelmia. Näissä WWW-palvelimessa komentoja tulkkaava suoritin palauttaa koko sivun, mutta sivun ulkoasu voidaan suunnitella helposti graafisilla työkaluilla. Tulkin sisäinen kieli voi poiketa suurestikin HTML-kielestä. Tällaisen tekniikan tarjoavat muun muassa Borland (nyk. Inprise) IntraBuilder sekä useat Java-kehitystyökalut, jotka tuottavat HTML-koodia WWW-palvelimen yhteyteen asennettujen servlettien avulla.

Vuosien saatossa on selainten yhteyteen liitetty tulkkeja, jotka osaavat rajoitetusti suorittaa HTML-sivussa olevia tulkin suoritettavaksi tarkoitettuja koodirivejä. Yleisin näistä koodikielistä on Javascript, jota käytetään elävöittämään HTML-sivuja lisäämällä sinne (yleensä) turhia kelloja, vieriviä tekstejä, jne. (engl. "bells and whistles").

3. PHP

PHP eli "PHP: Hypertext Preprocessor" on kokonaisuus, jonka avulla staattisten HTML-kielellä kirjoitettujen sivujen sisälle on mahdollista upottaa dynaamisuutta. PHP sisältää oman kielen, jonka avulla HTML-sivuille saadaan dynaamisuutta käyttäen suurta joukkoa valmiita aliohjelmia.

PHP-tulkki asennetaan WWW-palvelimen yhteyteen. Tutkielmassa ei kuitenkaan puututa PHP:n asennukseen, jonka pitäisi onnistua PHP:n mukana tulleiden ohjeiden avulla. PHP on saatavissa useimmille laiteympäristöille ja WWW-palvelimille. [PHP-kotisivu]

3.1. PHP-kielen suhde HTML:ään

PHP-kieli ja HTML-kieli eivät ole toisensa poissulkevia. Kumpaakin voidaan käyttää samassa dokumentissa täydentämään toista. Molempia ei ole kuitenkaan pakko käyttää, joten W3C-suositusten mukainen HTML-sivu voidaan tehdä myös pelkästään PHP-koodilla.

Kun selain pyytää PHP-koodia sisältävän sivun WWW-palvelimelta, se antaa sivun suoritettavaksi palvelimeen asennetulle PHP-tulkille. PHP-tulkki tutkii dokumentin rakenteen, suorittaa sivulla olevat PHP-kieliset komennot ja palauttaa WWW-palvelimelle työn tuloksena syntyneen staattisen dokumentin. WWW-palvelin palauttaa lopuksi dokumentin sitä pyytävälle selaimelle. PHP-kielellä täydennetyt sivut (kuten yleensä kaikki muutkin dynaamiset WWW-sivut) näkyvät siis selaimella kuten mitkä tahansa HTML-sivut.

3.2. Historiaa

PHP-kokonaisuuden kehittäminen alkoi ideasta saada tietää kuka lukee WWW-palvelimella olevia dokumentteja. Kun myös muut käyttäjät ottivat käyttöön kehitetyn ohjelmapaketin pyytäen siihen lisää uusia ominaisuuksia, syntyi alkuperäinen PHP eli Personal Home Page Tools.

Myöhemmin PHP:hen lisättiin ominaisuudet tietokantojen käyttämiseksi HTML-sivuilta. Tätä versiota PHP:stä kutsuttiin nimellä PHP/FI versio 2. FI eli Form Interpreter oli aiemmin toteutettu erillinen HTML-lomakkeiden käsittelyohjelma, jonka ominaisuudet siis yhdistettiin PHP:hen.

PHP:n versiota 3 (PHP3) varten PHP/FI kirjoitettiin ja suunniteltiin uudelleen. Samalla lisättiin uusia ominaisuuksia. PHP versio 4 (PHP4) on uusin sukupolvi PHP-kokonaisuudesta. PHP4 sisältää kaikki PHP3:n ominaisuudet ja lisäksi useita uusia ominaisuuksia. Osa uudistuksista on PHP4:n sisäisiä parannuksia, jotka eivät näy ulospäin. [PHP-uudistuksia]

Lisää historiaa ja kehitystietoa PHP:stä löytyy PHP:n kotisivuilta. [PHP-historia]

Tutkielmassa termi PHP tarkoittaa PHP3:n ja PHP4:n sisältämiä ominaisuuksia.

3.3. Ominaisuuksista

PHP sisältää kaikki nykyaikaisten kielten parhaat ominaisuudet (muun muassa muuttujat, aliohjelmat, toisto- ja ehtorakenteet sekä olio-ohjelmoinnissa tarvittavat luokat, periytymisen ja ylikuormituksen).

Yleisiä aliohjelmia on tarjolla satoja, sekä muiden valmistajien ohjelmistopakettien tukea varten erikoistuneempia aliohjelmakirjastoja, esimerkiksi eri tietokantakomponenttien käyttöä varten. [PHP-käsikirja]

PHP:n lähdekoodi on vapaasti saatavilla, joten kuka tahansa voi lisätä siihen uusia ominaisuuksia. [PHP-lähdekoodi]

3.4. Tulevaisuudesta

PHP:n tulevaisuus näyttää valoisalta. Sillä on vankka käyttäjäkuntansa, joka tuskin luopuu PHP:n käytöstä. Koska PHP on ilmainen, on se vapaasti saatavilla useille eri käyttöympäristöille. Ilmaisuudesta huolimatta PHP:tä kehitetään ja siitä ilmestyy aika ajoin uusia versioita.

PHP on käytössä useissa WWW-sivustoissa eri organisaatioissa. Erään tutkimuksen mukaan PHP olisi käytössä lähes 3.5 miljoonassa WWW-palvelimessa ympäri maapalloa (tieto elokuulta 2000). PHP-perustan päälle on rakennettu useita eri projekteja, joista osa on ilmaisia ja osa kaupallisia. [PHP-käyttö] ja [PHP-projekteja]

4. PHP-kielen syntaksi

Luvussa esitellään pääpiirteissään PHP-kielen syntaksi. Myös esimerkkikoodeja tutkimalla selviää syntaksi.

4.1. Yleistä

PHP-kielen syntaksi perustuu C-, Java- ja Perl-kielten syntaksiin, jonka lisäksi mukana on muutamia pelkästään PHP:lle ominaisia piirteitä. Tarkoituksena on ollut kehittää kieli, jonka avulla HTML-sivuille voitaisiin helposti ja nopeasti saada dynaamisuutta. Tästä johtuen PHP:n syntaksi ei ole yhtä monimutkainen verrattuna esimerkiksi Perl-kielen syntaksiin, mutta kuitenkin yhtä monipuolinen.

4.2. Lauserakenne

PHP-kieliset komennot sijoitetaan elementtien <?php ja ?> väliin. Näitä alku- ja loppuelementtejä voidaan käyttää missä tahansa HTML-sivulla aloittamaan ja lopettamaan PHP-komento-osuus. Jopa aliohjelman sisällä voidaan "vaihtaa" pois PHP-moodista käyttämällä elementtiä ?>, kirjoittaa HTML-elementtejä ja palata takaisin PHP-moodiin elementillä <?php.

PHP-komentoja voidaan kirjoittaa peräkkäin erottelemalla ne puolipistein. Aaltosulkeella "{" voidaan aloittaa lausekokoelma, joka päätetään sulkevalla aaltosululla "}".

Kommentteja voidaan merkitä joko C++-tyylisesti //-merkein, Unix-komentojonotyylisesti risuaitamerkillä # tai C-tyylisesti aloittamalla kommentti merkinnällä /* ja lopettamalla merkinnällä */.

4.3. Muuttujat ja vakiot

Muuttujat aloitetaan $-merkillä, jonka jälkeen ilmoitetaan muuttujan nimi. Nimen pitää alkaa kirjaimella tai alaviivalla "_", jonka jälkeen voi esiintyä kirjaimia, numeroita tai alaviivoja "_". Erikoismerkit (ASCII-merkistössä merkit 127-255) ja skandinaaviset kirjaimet å, ä ja ö ovat sallittuja, mutta selvyyden vuoksi niitä ei kannata käyttää. Kirjainten koolla on merkitystä, joten $foo ja $Foo ovat eri muuttujia.

Toisin kuin useimmissa muissa ohjelmointikielissä, PHP-muuttujilla ei ole tyyppiä. Siten sama muuttuja voi toimia ensin merkkijonona ja sitten numeroarvoisena. Tämä aiheuttaa myös ohjelmoijalle tiettyä vastuuta: muuttujien käytössä on oltava erityisen tarkkana, koska tyyppitarkistusta ei suoriteta.

Muuttuja määritellään sijoittamalla siihen arvo =-merkillä. Muuttujien arvojen vertailu tapahtuu käyttäen ==-merkintää.

Muuttujan arvo voidaan sijoittaa toiseen muuttujaan. Jos muuttujien tyypit ovat erilaisia, esimerkiksi merkkijono ja numero, varoitusta erityyppisten muuttujien arvojen sijoittamisesta ei tule. Erityyppisiä muuttujia voidaan myös vertailla keskenään, mutta tämä ei välttämättä tuota haluttua tulosta.

Muuttujan näkyvyysalue riippuu siitä, missä muuttuja on määritelty. Aliohjelman sisällä määritelty muuttuja näkyy vain aliohjelmassa. Aliohjelmien ulkopuolella määritelty muuttuja ei näy aliohjelmassa, ellei sitä aliohjelmassa erikseen määritellä näkyväksi global-merkinnällä.

Seuraava PHP-koodi ei tuota varoituksia tai virheilmoituksia, vaikka se ei ole kovin selkeää kieltä. Käytetty print-aliohjelma esitellään seuraavassa luvussa. PHP-tulkille tarkoitetut komennot on kursivoitu kuten muissakin koodiesimerkeissä.

<?php
$testi = 'foo';   print($testi . "\n");
$testi = bar;     print($testi . "\n");
$testi = 007;     print($testi+'foobar' . "\n");
?>

Koodin tuloksena saadaan seuraavat rivit.

foo
bar
7

Välttämättä tulostus ei siis ole sitä mitä halutaan. Muuttujien ja lainausmerkkien käytössä täytyy olla todella tarkkana.

Vakioita voidaan PHP-koodissa määritellä define()-aliohjelman avulla esimerkiksi seuraavasti:

define("VAKION_NIMI", "Vakion arvo");

Vakio on kuin muuttuja, mutta sillä on tiettyjä eroavaisuuksia muuttujiin. Vakiot eivät ala $-merkillä, niitä voidaan käyttää missä tahansa PHP-koodissa riippumatta näkyvyyksistä eikä vakion arvoa voida muuttaa tai poistaa sen jälkeen kun se on kerran määritelty.

4.4. Tulostusaliohjelmista

PHP-kielen print-, printf- ja echo-aliohjelmilla voidaan HTML-sivulle tulostaa merkkejä. Käytössä on lisäksi sprintf-aliohjelma, jolla voidaan muuttujaan sijoittaa muotoiltu merkkijono kuten tulostettaisiin HTML-sivulle printf-aliohjelmalla. Kaikilla näillä on joitakin eroavaisuuksia, joten tutkitaan niitä vähän tarkemmin.

Print-komennolla voidaan tulostaa merkkijonoja, jotka tulostuvat HTML-sivulle sellaisenaan. Echo-komennolla tulostettavasta merkkijonosta korvataan merkkijonossa olevat muuttujien nimet näiden arvolla, jos merkkijono on lainausmerkeissä. Heittomerkkien sisällä oleva merkkijono tulostetaan sellaisenaan. Echo-komennolla voidaan tulostaa myös rivinvaihtoja sisältäviä pidempiä merkkijonoja, joiden rivinvaihdot säilytetään.

Echo-komento ei varsinaisesti ole aliohjelma, joten sitä käytettäessä ei tarvita sulkumerkkejä parametrien ympärillä. Lisäksi parametreina voi olla useampia merkkijonoja pilkuilla eroteltuina. Normaalisti PHP-merkkijonojen yhdistäminen tapahtuu pistemerkillä (ei +-merkillä!). Rivinvaihto saadaan aikaan tulostamalla "\n".

Printf-komennolla voidaan tulostaa muotoiltuja merkkijonoja kuten C-kielen printf-komennolla.

Tutkitaan esimerkkinä alla olevia tulostuskomentoja sisältäviä koodirivejä.

<?php
$foo = 'bar';
print('Muuttujan $foo arvo on ' . $foo . '.\n');
echo "Muuttujan $foo

arvo on " , $foo , ".\n";
printf('Muuttujan $foo arvo on %s.\n',$foo);
?>

PHP-tulkki muokkaa edellä olleet komennot seuraavaan muotoon.

Muuttujan $foo arvo on bar.
Muuttujan bar

arvo on bar.
Muuttujan $foo arvo on bar.

Ensin sijoitetaan muuttujaan $foo merkkijonoarvo. Print-komento tulostaa pistemerkillä yhdistetyn merkkijonon, jonka lopussa on muuttujan $foo arvo. Seuraavalle riville echo-komento tulostaa rivinvaihtoja sisältävän merkkijonon. Tulostetun merkkijonon sisällä muuttujamerkintä $foo on korvattu muuttujan arvolla, koska merkkijono on lainausmerkkien sisällä. Pilkulla erotellun muuttujan $foo arvo tulostetaan rivin loppuun. Printf-komento tulostaa muotoillun merkkijonon, joka sisältää komennolle toisena parametrina annetun merkkijonon arvon sijoitettuna ensimmäisen parametrin kohtaan %s.

4.5. Ehtolauseet

PHP-kieli sisältää if-ehtolauseen, jolla voidaan tutkia annetun ehdon totuusarvo ja sen mukaan tehdä toimintoja. Luvussa 4.7 on esimerkkejä ehtolauseen käytöstä.

4.6. Toistolauseet

PHP-kieli sisältää useita toistolauseita. For-, while- ja do-while-rakenteet ovat näistä yleisimmät. Luvussa 4.7 on esimerkkejä toistolauseiden käytöstä.

4.6.1. For-lause

For-lauseella voidaan lause tai lausekokoelma toistaa niin kauan kuin annettu ehto on voimassa. Yleensä toistojen lukumäärä on ennalta tiedossa.

For-lauseen syntaksi on seuraava:

for (alkuehto; lopetusehto; seuraava_askel) suoritettavat_lauseet

Alkuehto on for-lauseen suoritusta aloitettaessa kerran suoritettava lause. Tällä voidaan esimerkiksi nollata lauseessa käytetty silmukkamuuttuja tai tehdä muita alustustoimenpiteitä.

Lopetusehto kuvaa ehtolausetta, jonka perusteella for-lauseen suoritus joko lopetetaan tai sitä jatketaan. Jos lopetusehdon totuusarvo on tosi (TRUE), suoritus jatkuu, muussa tapauksessa (FALSE) se lopetetaan.

Seuraava_askel kuvaa lausetta, joka suoritetaan, kun yksi suorituskerta on saatu tehtyä ja lopetusehto tutkittua.

Suoritettavat_lauseet kuvaavat toistettavaa lausetta tai lausekokoelmaa.

4.6.2. While-lause

While-lauseella voidaan lause tai lausekokoelma toistaa niin kauan kuin annettu ehto on voimassa. Yleensä toistojen lukumäärä ei ole ennalta tiedossa.

While-lauseen syntaksi on seuraava:

while ( ehto ) suoritettavat_lauseet

Ehto kuvaa lopetusehtoa, jonka saatua totuusarvon FALSE lopetetaan while-lauseen suoritus. Suoritettavat_lauseet kuvaavat kullakin lauseen suorituskerralla suoritettavia lauseita.

4.6.3. Do-while -rakenne

Do-while-rakenne toimii lähes samoin kuin edellisen luvun while-lause, mutta lopetusehdon tutkinta tapahtuu vasta suoritettavien lauseiden jälkeen. Tästä johtuen lause tai lausekokoelma suoritetaan aina vähintään kerran.

Do-while-rakenteen syntaksi on seuraava:

do suoritettavat_lauseet while ( ehto )

Rakenteen osat ovat samat kuin edellisen luvun while-lauseella.

4.7. Esimerkkejä ehto- ja toistolauseista

Luvussa käsitellään esimerkkejä toistolauseista. Sama silmukka suoritetaan käyttäen ensin for-lausetta, sitten while-lausetta ja lopuksi do-while-rakennetta. Kussakin toistolauseessa on mukana lisäksi esimerkki if-lauseesta. Koodit tulostavat luvut yhdestä kahteenkymmeneen ja tiedon siitä, onko tulostettu luku parillinen vai pariton.

For-lausetta käyttäen silmukka näyttää seuraavalta:

<?php
$i = 1;
for ($i = 1; $i <= 20; $i++) {
  if ( $i % 2 == 0 )
    print("Numero " . $i . " on parillinen.\n");
  else
    print("Numero " . $i . " on pariton.\n");
}
?>

While-lausetta käyttäen sama silmukka näyttää seuraavalta:

<?php
$i = 1;
while ( $i <= 20 ) {
  if ( $i % 2 == 0 )
    print("Numero " . $i . " on parillinen.\n");
  else
    print("Numero " . $i . " on pariton.\n");
  $i++;
}
?>

Do-while -rakennetta käyttäen silmukka näyttää seuraavalta:

<?php
$i = 1;
do {
  if ( $i % 2 == 0 )
    print("Numero " . $i . " on parillinen.\n");
  else
    print("Numero " . $i . " on pariton.\n");
  $i++;
} while ( $i <= 20 );
?>

Saman tekevä silmukka saadaan siis aikaan useammalla eri tavalla. Käyttökohteesta kuitenkin aina riippuu, mikä silmukkatyyppi kannattaa valita.

4.8. Olio-ohjelmointi

PHP tarjoaa ominaisuudet olio-ohjelmointia varten. Luokkien määrittely onnistuu kuten missä tahansa yleisesti käytetyssä olio-ohjelmointikielessä. Luokkien määrittelyn jälkeen niitä voidaan käyttää PHP-koodissa. Seuraavassa esimerkissä määritellään Auto-luokka, jonka jälkeen luodaan kaksi luokan ilmentymää.

<?php
class Auto
{
  var $nimi;
  var $teho;

// konstruktori oletusparametrein
  function Auto($nimi = "nimetön", $teho = 0) {
    $this->nimi = $nimi; $this->teho = $teho;
  }

  function aseta_nimi($n)    {  $this->nimi = $n;     }
  function aseta_teho($teho) {  $this->teho = $teho;  }
  function kerro_nimi()      {  return $this->nimi;   }
  function kerro_teho()      {  return $this->teho;   }

  function kerro_tiedot() {
    printf("Olen %s. Tehoa minulla on %d yksikköä.\n",
      $this->kerro_nimi(),$this->kerro_teho());
  }
}  // luokan määrittely päättyy

$lada = new Auto("Lada",60);
$mersu = new Auto;
$mersu->aseta_nimi("Mersu");
$mersu->aseta_teho(220);
$mersu->kerro_tiedot();
$lada->kerro_tiedot();
?>

Koodi asettaa arvoja Auto-luokan ilmentymille (olioille) ja tulostaa olioiden attribuutit (muuttujat). PHP-tulkin muokkaama koodi näyttää seuraavalta:

Olen Mersu. Tehoa minulla on 220 yksikköä.
Olen Lada. Tehoa minulla on 60 yksikköä.

4.9. Tietokantojen käyttö PHP:n avulla

PHP-kokoonpano tarjoaa rajapinnat useiden eri tietokantajärjestelmien käyttöön. Koska PHP-kieli on laitteistoriippumaton, tulee jokaiseen käyttöympäristöön olla oma liittymä kuhunkin tuettuun tietokantaan. Liittymiä käytetään samannimisillä aliohjelmilla ja samoin aliohjelmalle vietävin parametrein, jotta laitteistoriippumattomuus saadaan aikaan.

PHP-kokoonpanossa on tuki yleisimmille tietokannoille, joihin kuuluvat muun muassa dBase, Fiservin filePro, InterBase, Microsoftin SQL Server, mSQL, MySQL, Oracle, PostgreSQL ja Sybase. Lisäksi PHP:stä löytyy tuki geneeriselle ODBC-ajurille, joten PHP:n avulla voidaan käyttää periaatteessa mitä tahansa tietokantaa, jolle löytyy ODBC-ajuri. ODBC toimii sekä Windows- että Unix-versioissa.

Kutakin tietokantaa käyttävät aliohjelmat on nimetty yksilöllisesti. Tästä johtuen tietokantoja käyttäviä aliohjelmia on suuri määrä, joten tässä ei voida niitä kaikkia esittää. Tarkastellaan kuitenkin esimerkkinä ODBC:tä käyttäviä aliohjelmia. Kaikki tietokanta-aliohjelmat löytyvät PHP:n käsikirjoista. [PHP-käsikirja]

PHP:ssä on lähes 40 ODBC-aliohjelmaa, joten kaikkia ei käydä tässä läpi. Aliohjelmista vain tärkeimpiä käytetään alla olevassa esimerkkikoodissa. Koodia on kommentoitu //-merkein alkavilla riveillä. Tietokannasta tulee löytyä Taulu-niminen taulu, jossa on ainakin Tallenne-niminen merkkijonotyyppinen kenttä.

<?php
// Määritellään vakioita virheenkäsittelyä varten.
define("VIRHE", "Tapahtui virhe käsiteltäessä tietokantaa.");
define("TALLENNUS_EI_ONNISTUNUT", "Tallennus ei onnistunut.");

// Määritellään aliohjelmia tietokannan käsittelyyn.

function hae_taulusta($arvo) {

// Aliohjelmalla voidaan taulusta hakea haluttu arvo. Arvossa voidaan
// käyttää SQL-kyselykielen %-jokerimerkkiä.

// Avataan tietokantayhteys käyttäen PHP:n ODBC-ajuria.
// "lukki" on tässä järjestelmän ODBC-asetuksiin määritelty
// liittymä tietokantaan. odbc_connect()-aliohjelmalle annetaan parametreina
// myös käyttäjätunnus ja salasana, mutta tässä tietokannassa
// niitä ei tarvita. Palautetaan VIRHE-vakio, jos tietokantaan ei saada
// yhteyttä.

  $tietokantayhteys = odbc_connect("lukki", "", "");
  if ( !$tietokantayhteys ) return VIRHE;

// Muodostetaan kysely halutusta arvosta. Suoritetaan kysely käyttäen
// odbc_exec()-aliohjelmaa. Jos kysely ei onnistunut, palautetaan
// VIRHE-vakio.

  $query = "select * from Taulu where Tallenne like '" . $arvo . "'";
  $result_id = odbc_exec($tietokantayhteys, $query);
  if ( !$result_id ) {
    if ( $tietokantayhteys ) odbc_close($tietokantayhteys);
    return VIRHE;
  }

// Tutkitaan löytyykö haluttu arvo tietokannasta.
// Jos löytyy, otetaan kenttään Tallenne sijoitettu arvo muuttujaan.

  $onko_tallennetta = odbc_fetch_row($result_id);
  $tallenne = "";
  if ( $onko_tallennetta )
    $tallenne = odbc_result($result_id, "Tallenne");

// Suljetaan avattu tietokantayhteys ja palautetaan vastaus.
 
  odbc_free_result($result_id);
  if ( $tietokantayhteys )  odbc_close($tietokantayhteys);
  return $tallenne;
}

function aseta_tauluun($arvo) {
// Aliohjelmalla voidaan taulun kenttään asettaa haluttu arvo.

// Avataan tietokantayhteys.
  $tietokantayhteys = odbc_connect("lukki", "", "");
  if ( !$tietokantayhteys ) return VIRHE;

// Muodostetaan kysely.

  $query = "select * from Taulu where Tallenne like '" . $arvo . "'";
  $result_id = odbc_exec($tietokantayhteys, $query);
  if ( !$result_id ) return VIRHE;
  $palautettava = "";

// Jos aikaisemmin ei ole vielä tallennettu arvoa, tehdään se
// nyt. Jos aikaisemmin on tallennettu, ei tehdä mitään.

  $onko_tallennetta = odbc_fetch_row($result_id);
  if ( !$onko_tallennetta ) {
    $query = "insert into Taulu (Tallenne) values ('" . $arvo . "')";
    $result_id = odbc_exec($tietokantayhteys, $query);
    if ( !$result_id ) $palautettava = TALLENNUS_EI_ONNISTUNUT;
  }

// Suljetaan tietokantayhteys ja palautetaan tieto toiminnon
// onnistumisesta.
 
  odbc_free_result($result_id);
 
  odbc_close($tietokantayhteys);
  return $palautettava;
}

function hae($arvo) {
// Aliohjelma käyttää edellä määriteltyä hae_taulusta()-aliohjelmaa.

// Tutkitaan onko tauluun jo tallennettu haluttu arvo.

  print("Tutkitaan onko jo tallennettu arvoa '" . $arvo . "': ");

  $haettu = hae_taulusta($arvo);
  if ( $haettu == VIRHE ) {
    print("\n" . $haettu);
    exit;
  }

  if ( $haettu == "" )
    print("ei\n");
  else {
    print("kyllä\n");
    print("Taulusta haettu arvo '" . $haettu . "'.\n");
  }
}

function aseta($arvo) {
// Aliohjelma käyttää edellä määriteltyä aseta_tauluun()-aliohjelmaa.
// Tallennetaan tauluun haluttu arvo.

  $tallenna = aseta_tauluun($arvo);
  if ( $tallenna != "" ) {
    print($tallenna);
    exit;
  }

  print("Tauluun tallennettu arvo '" . $arvo . "'.\n");
}

// "Pääfunktio"
// Suoritetaan hae()- ja aseta()-aliohjelmat.

hae("foo%");
aseta("foobar");
hae("foo%");
?>

PHP-tulkin muokkaama koodi näyttää seuraavalta, kun käytettyyn tauluun ei ole alunperin tallennettu foo-merkkijonolla alkavaa arvoa.

Tutkitaan onko jo tallennettu arvoa 'foo%': ei
Tauluun tallennettu arvo 'foobar'.
Tutkitaan onko jo tallennettu arvoa 'foo%': kyllä
Taulusta haettu arvo 'foobar'.

Jos tietokannan käytössä ilmenee jokin virhe, tulee tästä ilmoitus.

4.10. Muita PHP-ominaisuuksia

PHP-kokoonpano sisältää runsaasti muitakin ominaisuuksia, joita ei ole edellä käsitelty. PHP:n Windows-versiolla voidaan käynnistää ohjelmia ja suorittaa niillä toimintoja Microsoftin COM- eli Component Object Model -arkkitehtuurin avulla. PHP:llä voidaan muodostaa Adobe Acrobat Readerilla luettavia PDF- eli Portable Document Format -tiedostoja. PHP:n aspell- ja pspell-moduleilla voidaan tarkistaa tekstin oikeinkirjoitusta. XML:n eli eXtensible Markup Language -määrittelyn mukaisten dokumenttien käsittely onnistuu PHP:n avulla.

Tietokoneverkkojen ja niihin liittyvien palveluiden käyttö onnistuu PHP:n avulla. SNMP:n eli Simple Network Management Protocol:n avulla voi suuriakin verkkoja käsitellä helposti. Sähköpostin lukeminen ja kirjoittaminen sekä keskusteluryhmiin osallistuminen onnistuvat PHP:n tarjoamien POP3-, IMAP- ja NNTP-protokollien avulla.

PHP:llä onnistuvat myös paikallisessa koneessa olevien tiedostojen ja hakemistojen käsittely, jos PHP:tä ajavan käyttäjän oikeudet siihen riittävät. JPEG-, GIF-, PNG- ja SWF-kuvatiedostojen käsittely onnistuu myös PHP:n avulla.

Kaiken kaikkiaan PHP sisältää suuren määrän erilaisia toimintoja. Aliohjelmia PHP sisältää yhteensä lähes 1500. On vaikea kuvitella ominaisuuksia, jotka PHP:stä vielä puuttuisivat, mutta jotka helposti olisivat toteutettavissa tällaiseen lausekieleen.

5. Esimerkkisovelluksia

Luvussa tarkastellaan esimerkkisovelluksia, joissa on käytetty PHP-kieltä apuna. Esimerkeissä normaalein kirjasimin on esitetty HTML-elementit ja kursivoiduin kirjasimin PHP-kielellä kirjoitetut komennot (ts. PHP-tulkin suoritettavaksi tarkoitetut komennot).

5.1. Hei maailma!

Ensimmäisenä esimerkkiohjelmana esitellään yksinkertainen Hei maailma -tekstin tuottava HTML-sivu. Alla olevassa esimerkkiohjelmassa on käytetty myös kehittyneempiä ominaisuuksia:

<html><head><title>Hei maailma!</title></head>
<body>
<p>
<?php
  print("Hei maailma!\n");
  $aika = time();
  $pvm = date("j.n.Y",$aika);
  $kello = date("H:i:s",$aika);
  print("On " . $pvm . " ja kello on " . $kello . ".\n");
?>
</p>
</body></html>

Tiedoston alussa määritellään HTML-elementtien avulla dokumentin rakenne ja otsikko. PHP-tulkille tarkoitetut komennot on sijoitettu elementtien <?php ja ?> väliin. WWW-palvelimeen asennettu PHP-tulkki suorittaa näiden elementtien välissä sijaitsevat komennot ja palauttaa takaisin WWW-palvelimelle tuloksen, joka siirretään käyttäjän selaimelle.

Edellä olevassa esimerkkikoodissa sijoitetaan muuttujaan aika nykyhetken aika (sekunteja vuoden 1970 alusta). Tämän jälkeen muuttujaan pvm sijoitetaan aika-muuttujasta laskettu muotoiltu päivämäärä ja muuttujaan kello muotoiltu kellonaika käyttäen hyväksi date-funktion tarjoamia muotoilusääntöjä. Kaikki muotoilusäännöt löytyvät PHP:n käsikirjoista. [PHP-käsikirja]

Print-funktiolla tulostetaan päivämäärä ja kellonaika varustettuna selittävillä teksteillä. Merkkijonojen yhdistämiseen PHP-kielessä käytetään pistettä. Lopuksi päätetään HTML-dokumentti.

PHP-tulkin muokkaama WWW-sivu näyttää seuraavalta:

<html><head><title>Hei maailma!</title></head>
<body>
<p>
Hei maailma!
On 21.9.2000 ja kello on 11:49:19.
</p>
</body></html>

Mukana ei enää ole minkäänlaista viittausta PHP-koodiin.

5.2. Päivyrisovellus

Luvussa esitellään PHP-kieltä hyödyntävä käytännön sovellus. Päivyrisovellus näyttää sivulla halutun kuukauden kalenterin sekä valintalistan ja painikkeen, joilla näytettävää kuukautta ja vuotta voidaan vaihtaa. Kalenterin vieressä on tilaa muistiinpanoille. Sovellus käyttää hyväkseen tietokantaa päivyrin tietojen säilömiseen.

Kullekin päivälle voidaan tehdä muistiinpanoja, jotka tallentuvat tietokantaan. Muistiinpanoja voidaan korjailla myöhemmin tai vaikka poistaa kokonaan.

Sovelluksen koodi on esitetty liitteessä. Koodia on kommentoitu risuaitamerkillä # ja //-merkinnällä alkavilla riveillä. PHP-tulkille tarkoitetut komennot on kursivoitu. Jos koodista poistetaan lihavoidut rivit, saadaan aikaan sovellus, joka toimii ilman tietokantaa pelkkänä kalenterina.

Toimiakseen sovellus vaatii normaalien PHP-ominaisuuksien lisäksi myös tietokannan, johon voidaan ottaa WWW-palvelimelta yhteys. Tässä sovelluksessa on käytetty Microsoft Access -kantaa, jonka relaatiokuva ja taulujen rakenne on esitelty kuvassa 1.

Päivyri-kanta
Kuva 1. Päivyrisovelluksen tietokannan taulurakenne.

Käytettävän tietokannan ei tarvitse olla monimutkainen, ja tämänkin kannan Henkilöt-taulu on tässä sovelluksessa tarpeeton. Kalenteri-taulussa riittävät kentät Pvm ja Tallenne, jos kaikki käyttäjät saavat muokata kaikkien muistiinpanoja.

Henkilöt-taulun HenkilöID-kenttä on numerotyyppinen (ja lisäksi autonumber-tyyppinen avainkenttä). Muut taulun kentät ovat merkkijonotyyppisiä. Kalenteri-taulun KalenteriID- ja HenkilöID-kentät ovat numerotyyppisiä. KalenteriID on lisäksi autonumber-tyyppinen avainkenttä. Muut taulun kentät ovat merkkijonotyyppisiä.

Kuvassa 2 on esitetty päivyrisovelluksen ulkoasu selaimella.

Päivyrisovelluksen ulkoasu selaimella
Kuva 2. Päivyrisovelluksen ulkoasu selaimella.

6. Yhteenveto

WWW-sivujen dynaamista tuottamista varten on olemassa useita eri tekniikoita. Kaikki vaihtoehdot eivät sovellu kaikkiin tarpeisiin, ja kaikki tekniikat eivät edes toimi kaikissa käyttöympäristöissä. Tutkielmassa on esitelty yksi käytännössäkin hyväksi koettu vaihtoehto, PHP. Se toimii niin Windows- kuin Unix-ympäristöissäkin.

Unix-ympäristöissä usein käytettyjä ovat Perl-kieliset CGI-ohjelmat. Perl-kieli on monipuolinen, mutta se tuntuu vaikeahkolta aloittelijasta. Tästä syystä se vain harvoin opetellaan ensimmäisenä ohjelmointikielenä. Perl sopii kuitenkin loistavasti merkkijonojen käsittelyyn, jota WWW-lomakkeiden käsittely suurimmaksi osaksi on.

PHP:tä vastaava Microsoftin tarjoama tekniikka on ASP eli Active Server Pages. Pääperiaate ASP:ssa on sama kuin PHP:ssäkin, eli sivujen sisään upotetaan komentoja, jotka WWW-palvelimeen asennettu tulkki suorittaa. ASP-komennoissa voidaan käyttää Microsoftin VBScript- tai JScript-kieliä, mutta muitakin mahdollisuuksia on, esimerkiksi Perl. [ASP-tietoutta]

ASP toimii vain Microsoftin omassa WWW-palvelinohjelmistossa IIS:ssä eli Internet Information Serverissä. ASP on suoraan käytettävissä, kunhan käyttöjärjestelmänä on Windows NT Server tai Windows 2000 Server sekä vapaasti Microsoftilta saatavilla oleva IIS-palvelinohjelmisto. Koska ASP on Microsoftin tuote, on se Windows-käyttöjärjestelmän mukana levinnyt laajalle. [ASP-tietoutta]

ASP:sta ei ole saatavilla lähdekoodia, toisin kuin PHP:stä. [PHP-lähdekoodi]

Mikään ohjelma ei ole täysin virheetön. PHP:n uusimmassa versiossa (tutkielman kirjoitushetkellä 4.0.3 patch level 1) on lähdekoodia yhteensä yli 10 megatavua, joten koodissa on varmasti useita virheitä. PHP:tä kehitetään ja korjaillaan kuitenkin koko ajan, joten ohjelmistossa olevien virheiden vuoksi sitä ei kannata hylätä. Käytännössä virheitä tulee esiin suhteellisen harvoin. [PHP-lähdekoodi]

Jos verrataan PHP:tä ja muita luvussa 2.4 esiteltyjä tapoja tuottaa dynaamisia WWW-sivuja, voidaan sanoa, että PHP on monessa suhteessa paras vaihtoehto. Tämän todistaa sekin, että PHP:tä käytetään WWW-sivustoissa yhä enemmän.

Lähteet

PHP-kotisivu, saatavilla WWW-muodossa <URL: http://www.php.net/>, viitattu 23.10.2000

PHP-historia, saatavilla WWW-muodossa <URL: http://www.php.net/manual/phpfi2.html>, viitattu 23.10.2000

PHP-uudistuksia, saatavilla WWW-muodossa <URL: http://www.zend.com/zend/whats-new.php>, viitattu 23.10.2000

PHP-käsikirja, saatavilla WWW-muodossa <URL: http://www.php.net/manual/>, viitattu 23.10.2000

PHP-projekteja, saatavilla WWW-muodossa <URL: http://www.php.net/sites.php> ja <URL: http://www.php.net/projects.php>, viitattu 23.10.2000

PHP-käyttö, saatavilla WWW-muodossa <URL: http://www.php.net/usage.php>, viitattu 23.10.2000

PHP-lähdekoodi, saatavilla WWW-muodossa <URL: http://www.php.net/downloads.php>, viitattu 23.10.2000

ASP-tietoutta, saatavilla WWW-muodossa <URL: http://msdn.microsoft.com/workshop/server/asp/aspfeat.asp>, viitattu 23.10.2000

W3C-kotisivu, saatavilla WWW-muodossa <URL: http://www.w3.org/>, viitattu 23.10.2000

Liite. Päivyrisovelluksen koodi

Liitteessä on esitelty päivyrisovelluksen koodi. Koodia on kommentoitu risuaitamerkillä # ja //-merkinnällä alkavilla riveillä. HTML-elementit on esitetty normaalein kirjasimin ja PHP-tulkille tarkoitetut komennot kursivoiduin kirjasimin. Lihavoiduin kirjasimin on esitetty kohdat, jotka poistamalla saadaan aikaan sovelluksen versio, joka ei käytä tietokantaa ja jonka avulla ei voida tehdä päivyrimerkintöjä.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">

<html lang=fi>
<head>
  <title>Päivyri</title>
  <meta name="rating" content="safe for kids">
  <meta name="robots" content="index,nofollow">
  <meta name="Description" content="Sivu sisältää PHP:llä tehdyn
  nettipäivyrin, joka on tehty LuK-tutkielmaa varten.">
  <meta name="KeyWords" content="Päivyri, Nettipäivyri, LuK-tutkielma, PHP">
  <meta name="author" content="Pauli Kujala">
  <link rev="Made" href="mailto:pjkujala@mit.jyu.fi">
</head>

<body>
<h1>Päivyri</h1>
<h2>Nettikäyttöinen päivyrisovellus</h2>
<h3>&copy; 2000 Pauli Kujala</h3>
<h3>23.10.2000</h3>

<p>
<strong>Päivyri</strong>n avulla voit käyttää nettipäivyriä ja
tehdä muistiinpanoja päiville. Päivyri vaatii toimiakseen tietokannan
sekä mahdollisuuden käyttää <a href="http://www.php.net/">PHP-komentoja</a>.
</p>

<?php

######################## globaalit muuttujat ########################

# Alustetaan taulukoita ja muuttujia. Tässä globaalien taulukoiden
# käyttö on perusteltua. Yksinkertaisuuden vuoksi myös muita
# muuttujia on määritelty globaaleiksi.


$kuukaudet = array("ei kuu",  // aloitetaan kuukaudet ykkösestä
                   "Tammikuu", "Helmikuu", "Maaliskuu", "Huhtikuu",
                   "Toukokuu", "Kesäkuu", "Heinäkuu", "Elokuu",
                   "Syyskuu", "Lokakuu", "Marraskuu", "Joulukuu" );
$viikonpaivat = array("ma","ti","ke","to","pe","la","su");

$nyk_aika = time();
$nyk_paiva = date("j",$nyk_aika);
$nyk_kuukausi = date("n",$nyk_aika);
$nyk_vuosi = date("Y",$nyk_aika);

# Käyttäjänä yksi henkilö, jonka tiedot on tallennettu tietokannan
# Henkilöt-tauluun ja jonka HenkilöID-kentässä on arvo 1.
# Kaikki päivyrisovellusta käyttävät henkilöt voivat siis muuttaa
# toisten käyttäjien tekemiä muistiinpanoja (julkinen päivyri).
# Jos halutaan lisätä ominaisuus, jonka avulla kukin käyttäjä voi
# muokata vain omia muistiinpanojaan, vaatisi se koodiin
# käyttäjätunnusten hallinnan, salasanojen hallinnan sekä
# sisäänkirjautumisrutiinin.

$tietokantayhteys = 0;
$henkiloid = 1;

# Määritellään VIRHE-vakio tietokannan käsittelyssä tapahtuvia
# virhetilanteita varten.

define(VIRHE, "Virhe tietokannan käsittelyssä.");

######################## funktiot ################################

# Aliohjelmia ei suoriteta heti sivua ladattaessa, vaan vasta niitä
# kutsuttaessa.

function avaa_tietokanta() {
  global $tietokantayhteys;
  $tietokantayhteys = odbc_connect("lukki", "", "");
  if ( !$tietokantayhteys ) return VIRHE;
  return "";
}

#########

function sulje_tietokanta() {
  global $tietokantayhteys;
  if ( $tietokantayhteys ) odbc_close($tietokantayhteys);
  return "";
}

#########

function hae_tallenne($henkiloid,$p,$k,$v) {
  global $tietokantayhteys;
  if ( !$tietokantayhteys ) avaa_tietokanta();

  $query = "select Tallenne from Kalenteri where HenkilöID="
           . $henkiloid . " and Pvm='" .$p. "." .$k. "." .$v. "'";
  $result_id = odbc_exec($tietokantayhteys, $query);
  if ( !$result_id ) return VIRHE;
  $onko_tall = odbc_fetch_row($result_id);
  $tallenne = "";
  if ( $onko_tall )
    $tallenne = odbc_result($result_id, "Tallenne");
  odbc_free_result($result_id);
  sulje_tietokanta();
  return $tallenne;
}

#########

function tallenna_kirjoitus($henkiloid,$p,$k,$v,$teksti,$poista) {
  global $tietokantayhteys;
  if ( !$tietokantayhteys ) avaa_tietokanta();
  if ( $poista ) {
    # Ollaan poistamassa muistiinpanoa.
    $query = "delete from Kalenteri where HenkilöID=" . $henkiloid .
             " and Pvm='" .$p. "." .$k. "." .$v."'";
  }
  else {
    # Ollaan tallentamassa muistiinpanoa.
    # Tutkitaan ensin onko ko. päivälle jo tehty muistiinpanoa.
    # Jos on, korvataan se. Muussa tapauksessa luodaan uusi
    # muistiinpano.
    $query = "select KalenteriID from Kalenteri where HenkilöID="
             . $henkiloid . " and Pvm='" .$p. "." .$k. "." .$v. "'";
    $result_id = odbc_exec($tietokantayhteys, $query);
    if ( !$result_id ) return VIRHE;
    $onko_tall = odbc_fetch_row($result_id);

    # Jos aikaisemmin oli tehty muistiinpano, muuttujan $kalenteriID
    # arvoksi tulee positiivinen luku, jota käytetään muistiinpanon
    # korvaamisessa.
    $kalenteriID = -1;
    if ( $onko_tall )
      $kalenteriID = odbc_result($result_id, "KalenteriID");
    odbc_free_result($result_id);
    if ( $kalenteriID < 0 )
      $query = "insert into Kalenteri (HenkilöID,Pvm,Tallenne) "
               . "values (" .$henkiloid. ",'" .$p. "." .$k. "." .$v. "','" .$teksti. "')";
    else
      $query = "update Kalenteri set Tallenne='" .$teksti.
               "' where KalenteriID=" .$kalenteriID;
  }
  $result_id = odbc_exec($tietokantayhteys,$query);
  sulje_tietokanta();
  if ( !$result_id ) return VIRHE;
  return "";
}

#########

function poista_kirjoitus($henkiloid,$p,$k,$v) {
# Käytetään edellä määriteltyä tallenna_kirjoitus()-aliohjelmaa
# viemällä sille viimeisenä parametrina true-arvo.
  tallenna_kirjoitus($henkiloid,$p,$k,$v,"",true);
}

#########

function tulosta_kalenteri($vuosi,$kuukausi) {

# Määritellään aiemmin määritellyt muuttujat näkymään myös tässä
# aliohjelmassa. Muuttujien arvot voitaisiin myös tuoda parametrina
# tälle aliohjelmalle.

global $kuukaudet, $viikonpaivat,
       $nyk_aika, $nyk_kuukausi, $nyk_vuosi, $nyk_paiva;
global $textarea_paiva, $textarea_kuukausi, $textarea_vuosi;
global $henkiloid;

# Tulostetaan HTML-sivulle form-elementti, joka kutsuu itseään
# (action="paivyri.php3").

?>

<form method="post" action="paivyri.php3">

<?php

# Muodostetaan tiedot kuukauden ensimmäisestä päivästä (viikonpäivä).
# date()-aliohjelmalle ensimmäisenä vietävä parametri "w" kertoo
# viikonpäivän (su=0, ma=1, ..., la=6). Jos kyseessä on sunnuntai,
# muutetaan se suomalaiseen asuun numeroksi 7.

$eka_pv = mktime(0,0,0,$kuukausi,1,$vuosi);
$wday = date("w", $eka_pv);
if ( $wday == 0 ) $wday = 7;

# Tulostetaan HTML-sivulle form-elementtiä varten piilotetut tiedot
# näytettävästä kuukaudesta ja vuodesta, sekä tekstialueessa
# näytettävän muistiinpanon päivämäärästä.

?>

<input type="hidden" name="kuukausi" value="<?php echo $kuukausi; ?>">
<input type="hidden" name="vuosi" value="<?php echo $vuosi; ?>">

<input type="hidden" name="textarea_paiva" value="<?php echo $textarea_paiva; ?>">
<input type="hidden" name="textarea_kuukausi" value="<?php echo $textarea_kuukausi; ?>">
<input type="hidden" name="textarea_vuosi" value="<?php echo $textarea_vuosi; ?>">

<?php

# Tulostetaan otsikkoon kuukausi ja vuosi.
# Tehdään sivulle HTML-taulukko kuukauden päivistä.
# Tulostetaan taulukon ensimmäiselle riville viikonpäivien nimet.

?>

<h2><?php echo $kuukaudet[$kuukausi] . " " . $vuosi; ?></h2>

<table border=3>
<tr>
<td>

<table border=1>
<tr>
<?php
  $leveys = floor(100/count($viikonpaivat));
  for ($i = 0; $i < count($viikonpaivat); $i++)
    print("<th width=\"" . $leveys . "%\">" . $viikonpaivat[$i] . "</th>\n");
?>

</tr>

<tr>
<?php

# date()-aliohjelman ensimmäinen parametri "t" antaa kuukaudessa
# olevien päivien lukumäärän.
# Muuttuja $vkp sisältää tulostettavan päivän viikonpäivän.

$viim = date("t",$eka_pv);
$vkp = $wday;

$kuluva_kuu = false;
$nykypv = false;

# Tulostetaan kuukauden ensimmäisen viikon alkuun tyhjiä soluja kunnes
# vuorossa on kuukauden ensimmäinen päivä.

for ($i=1; $i<$wday; $i++)
  print("<td>&nbsp;</td>\n");

# Tulostetaan kukin kuukauden päivä omaan soluunsa painikkeena.
# Siirrytään taulukon seuraavalle riville kun on tulostettu sunnuntai.
# Jos ollaan tulostamassa kuluvaa päivämäärää, asetetaan solun
# taustaväriksi punainen.
# Jos päivälle on tehty muistiinpanoja, asetetaan solun taustaväriksi
# keltainen.
# Jos kuluvalle päivämäärälle on tehty muistiinpanoja, asetetaan solun
# taustaväriksi sininen.

for ($i=1; $i<=$viim; $i++) {
  $nykypv = false;
  if ( $nyk_vuosi == $vuosi && $nyk_kuukausi == $kuukausi && $nyk_paiva == $i ) {
    $nykypv = true;
    $kuluva_kuu = true;
  }
  $tallenne = hae_tallenne($henkiloid,$i,$kuukausi,$vuosi);
  $bgcolor = "";
  if ( $nykypv == true ) $bgcolor = "red";
  if ( $tallenne != "" ) $bgcolor = "yellow";
  if ( $nykypv == true && $tallenne != "" ) $bgcolor = "blue";

  print("<td align=right");
  if ( $bgcolor != "" ) print(" bgcolor=\"" . $bgcolor . "\"");
  print("><input type=\"submit\" name=\"paivaButton\" value=\"" .substr("0".$i,-2) . "\"></td>\n");
  $vkp++;
  if ( $vkp > 7 && $i < $viim) {
    print("</tr>\n<tr>\n");
    $vkp = 1;
  }
}

# Tulostetaan lopuksi viimeinen rivi viimeisen päivän jälkeen täyteen
# tyhjiä soluja.

for ($i=$vkp; $i<=7; $i++)
  print("<td>&nbsp;</td>\n");
?>

</tr>
<tr><td colspan=7>&nbsp;</td></tr>

<?php
# Tulostetaan valintalista, jonka avulla voidaan siirtyä muihin
# kuukausiin edellisen, kuluvan ja seuraavan vuoden aikana.
?>

<tr>
<td colspan=7 align=left>&nbsp;
<select name="kuu_ja_vuosi" size="1">
<?php
      for ($v=$vuosi-1; $v<=$vuosi+1; $v++)
        for ($i=1; $i<=12; $i++) {
          print("<option");
          if ( $i == $kuukausi && $v == $vuosi ) print(" selected");
          print(">" . $kuukaudet[$i] . " " . $v . "</option>\n");
        }
?>
</select>
<input type="submit" name="siirryButton" value="Siirry">
</td>
</tr>

<?php
  if ( $kuluva_kuu == false ) :

# Jos näkyvillä ei ole kuluvan kuukauden kalenteri, näytetään painike,
# jolla siihen voidaan siirtyä nopeasti.
# Tässä PHP-koodissa käytetään PHP:n erinomaista if..endif-rakennetta.
?>

<tr>
  <td colspan=7 align=center>
    <input type="submit" name="kuluvaKuuButton"
      value="<?php
                print($kuukaudet[$nyk_kuukausi] . " " . $nyk_vuosi);
             ?>">
 
</td>
</tr>

<?php endif; ?>

</table>
</td>

<?php

# Tulostetaan tekstialue, johon voidaan tehdä muistiinpanoja. Haetaan
# tekstialueeseen halutun päivämäärän muistiinpano, jos sellainen on
# tehty. Tutkitaan ja näytetään myös muistiinpanotekstin viikonpäivä.

$ta_pvm = mktime(0,0,0,$textarea_kuukausi,$textarea_paiva,$textarea_vuosi);

$wday = date("w", $ta_pvm);
if ( $wday == 0 ) $wday = 7;
$wday--;

?>

<td>&nbsp;</td>
<td align=center>
<p>
<strong><?php print($viikonpaivat[$wday] . " "
                 . $textarea_paiva . "."
                 . $textarea_kuukausi . "."
                 . $textarea_vuosi);
        ?></strong><br>
 
    <textarea name="taTallenne" rows=10 cols=40><?php
      print(hae_tallenne($henkiloid,$textarea_paiva,
                     $textarea_kuukausi,$textarea_vuosi));
      ?></textarea>
</p>
 
<p>
<input type="submit" name="tallennus" value="Tallenna">
<input type="submit" name="tallennus" value="Poista">
</p>
</td>
</tr>
</table>
</form>

<?php
}

######################## "pääfunktio" ############################

# Varsinainen PHP-komentojen suoritus alkaa tästä.

# Tutkitaan minkä kuukauden kalenteri halutaan näyttää.
# Jollei vielä asetettu muuttujia $kuukausi ja $vuosi (päivyriä
# kutsutaan ensimmäistä kertaa), asetetaan kuluva kuukausi ja vuosi.

if ( !isset($kuukausi) )  $kuukausi = $nyk_kuukausi;
if ( !isset($vuosi) )     $vuosi = $nyk_vuosi;

# Jos on valittu jokin alkio kuukausi ja vuosi -valintalistasta,
# näytetään valitun kuukauden kalenteri.

if ( isset($kuu_ja_vuosi) ) {
  $osat = explode(" ",$kuu_ja_vuosi);
  if ( count($osat) == 2 ) {
    $k = 0;
    for ($i=1; $i<count($kuukaudet); $i++)
      if ( $osat[0] == $kuukaudet[$i] ) $k = $i;
    $osat[1] = intval($osat[1]);
    if ( ( 0 < $k ) && ( 0 <= $osat[1] ) ) {
      $kuukausi = $k;
      $vuosi = $osat[1];
    }
  }
}

# Jos halutaan näytettäväksi kuluvan kuukauden kalenteri, tehdään se.

if ( isset($kuluvaKuuButton) ) {
  $kuukausi = $nyk_kuukausi;
  $vuosi = $nyk_vuosi;
}

# Jos on painettu joko Tallenna- tai Poista-painiketta, tehdään haluttu
# toiminto tekstialueen muistiinpanolle.

if ( isset($tallennus) ) {
  switch ( $tallennus ) {
    case "Tallenna" :
        tallenna_kirjoitus($henkiloid, $textarea_paiva,
          $textarea_kuukausi, $textarea_vuosi, $taTallenne, $false);
        break;
    case "Poista"   :
        poista_kirjoitus($henkiloid, $textarea_paiva,
          $textarea_kuukausi, $textarea_vuosi);
        break;
    default         : break;
  }
  $tallennus = "";
}

# Jos ei ole vielä alustettu tekstialueessa näkyvää päivämäärää,
# tehdään se nyt. Tämä tapahtuu vain skriptiä ensimmäistä kertaa
# kutsuttaessa. Tekstialueessa näkyvän muistinpanon päivämäärä säilyy
# tallessa, vaikka näytettävän kuukauden kalenteri vaihtuisikin.

if ( !isset($textarea_paiva) || $textarea_paiva == "" )
  textarea_paiva = $nyk_paiva;
if ( !isset($textarea_kuukausi) || $textarea_kuukausi == "" )
  $textarea_kuukausi = $nyk_kuukausi;
if ( !isset($textarea_vuosi) || $textarea_vuosi == "" )
  $textarea_vuosi = $nyk_vuosi;

# Jos on painettu kuukauden jotakin päivämääräpainiketta, näytetään
# muistiinpano kyseiseltä päivältä.

if ( isset($paivaButton) ) {
  $textarea_paiva = intval($paivaButton);
  $textarea_kuukausi = intval($kuukausi);
  $textarea_vuosi = intval($vuosi);
}

# Tulostetaan kalenteri halutulta kuukaudelta ja vuodelta.

  tulosta_kalenteri($vuosi,$kuukausi);
?>

<p>
  <a href="http://validator.w3.org/check/referer">
    <img src="/images/vh40.gif" alt="Valid HTML 4.0!" height="31"
     width="88">
  </a>
</p>

</body>
</html>

Valid XHTML 1.0!