Kirjoittaja: Antti Laaksonen (2011).
PHP:n tavalliset muuttujatyypit (lukuarvot, merkkijonot ja totuusarvot) sekä taulukot riittävät periaatteessa kaikkiin tarkoituksiin. Usein kuitenkin koodin rakenteen saa selkeämmäksi määrittelemällä omia erikoistuneita muuttujatyyppejä. Tämä onnistuu olio-ohjelmoinnin avulla.
Olio-ohjelmoinnin peruskäsitteet ovat luokka ja olio. Luokka määrittelee, millaista tietoa olio sisältää (luokan muuttujat) ja miten sitä voi käsitellä (luokan funktiot). Käytännössä luokkaa voi ajatella muuttujan tyyppinä ja oliota voi ajatella muuttujan arvona.
Tässä oppaassa tutustumme olio-ohjelmoinnin perusasioihin. Olio-ohjelmoinnin vaikeutena on, että se pääsee oikeuksiinsa vasta silloin, kun koodin määrä on suuri. Pienissä esimerkeissä oliot saattavat tämän vuoksi tuntua vain ylimääräiseltä lisäkuormalta. Vasta kokemus opettaa, mitä todellista hyötyä olio-ohjelmoinnista voi olla.
Kattava selostus PHP:n olio-ominaisuuksista on PHP:n manuaalissa.
Seuraava koodi määrittelee luokan Henkilo
ja muodostaa siitä kaksi oliota:
<?php class Henkilo { private $nimi; public function __construct($nimi) { $this->nimi = $nimi; } public function puhu() { echo "Hei, nimeni on {$this->nimi}! <br>"; } } $aapeli = new Henkilo("Aapeli"); $maija = new Henkilo("Maija"); $aapeli->puhu(); $maija->puhu(); ?>
Koodin tulostus on seuraava:
Hei, nimeni on Aapeli!
Hei, nimeni on Maija!
Luokan määrittelyn aloittaa sana class
, jonka jälkeen tulee luokan nimi. Luokka muodostuu muuttujista ja funktioista, jotka määritellään luokan sisällä. Esimerkiksi luokassa Henkilo
on muuttuja $nimi
sekä funktiot __construct
ja puhu
. Luokan muuttujat ovat käytettävissä kaikissa funktioissa, ja niihin viitataan sanalla $this
.
Muuttujan ja funktion määrittelyn edessä voi ilmoittaa sen näkyvyysalueen. Jos näkyvyysalue on private
(yksityinen), muuttujaa tai funktiota voi käyttää vain luokan sisällä. Jos taas näkyvyysalue on public
(julkinen), käyttäminen onnistuu myös luokan ulkopuolelta. Tavallisesti luokan muuttujat määritellään yksityisiksi, jolloin niitä pystyy käsittelemään luokan ulkopuolelta vain julkisten funktioiden kautta.
Funktio __construct
on luokan konstruktori, jota kutsutaan aina, kun luokasta muodostetaan olio. Tavallisesti konstruktori asettaa alkuarvot luokan muuttujille. Esimerkiksi luokassa Henkilo
konstruktorille annetaan henkilön nimi, joka sijoitetaan luokan muuttujaan $nimi
.
Komento new
luo luokkaa vastaavan olion. Luokan nimen jälkeen annetaan parametrit luokan konstruktorin mukaisesti. Yllä olevassa koodi luo luokasta Henkilo
kaksi oliota ja tallentaa ne muuttujiin $aapeli
ja $maija
.
Tässä esimerkissä teemme ostoskorin, johon voi lisätä tuotteita. Esimerkki muodostuu kahdesta luokasta: Tuote
vastaa yhtä tuotetta, ja Ostoskori
sisältää joukon tuotteita.
Luokan Tuote
sisältönä on tuotteen nimi ja hinta (Tuote.php
):
<?php class Tuote { private $nimi; private $hinta; public function __construct($nimi, $hinta) { $this->nimi = $nimi; $this->hinta = $hinta; } public function nimi() { return $this->nimi; } public function hinta() { return $this->hinta; } } ?>
Olio-ohjelmoinnin käytännön mukaisesti muuttujat $nimi
ja $hinta
ovat yksityisiä. Tämän vuoksi luokassa on erilliset funktiot nimi
ja hinta
, joilla muuttujien arvot pystyy hakemaan luokan ulkopuolelta.
Luokka Ostoskori
sisältää kaikki ostoskorin tuotteet (Ostoskori.php
):
<?php class Ostoskori { private $tuotteet = array(); public function lisaa($tuote) { $this->tuotteet[] = $tuote; } public function maara() { return count($this->tuotteet); } public function lista() { $nimet = array(); foreach($this->tuotteet as $tuote) { $nimet[] = $tuote->nimi(); } return $nimet; } } ?>
Luokan sisältönä on taulukko, jossa on ostoskorin tuotteet. Funktio lisaa
lisää uuden tuotteen ostoskoriin, funktio maara
palauttaa tuotteiden määrän ja funktio lista
palauttaa kaikkien tuotteiden nimet taulukossa.
Seuraava koodi testaa luokkia:
<?php include("Tuote.php"); include("Ostoskori.php"); $ostoskori = new Ostoskori(); $ostoskori->lisaa(new Tuote("selleri", 3)); $ostoskori->lisaa(new Tuote("retiisi", 2)); $ostoskori->lisaa(new Tuote("nauris", 5)); echo "Määrä: " . $ostoskori->maara() . "<br>"; echo "Tuotteet: " . implode(", ", $ostoskori->lista()); ?>
Koodi tuottaa seuraavan sivun:
Määrä: 3
Tuotteet: selleri, retiisi, nauris
Perintä on tavallinen olio-ohjelmoinnin tekniikka, jossa uuden luokan lähtökohtana on aiemman luokan sisältö. PHP:ssä perinnän ilmaisee sana extends
luokan määrittelyn alussa.
Tässä on uudestaan aiempi Henkilo
-luokka:
<?php class Henkilo { private $nimi; public function __construct($nimi) { $this->nimi = $nimi; } public function puhu() { echo "Hei, nimeni on {$this->nimi}! <br>"; } } ?>
Seuraava luokka Ohjelmoija
perii Henkilo
-luokan:
<?php include("Henkilo.php"); class Ohjelmoija extends Henkilo { private $kieli; public function __construct($nimi, $kieli) { parent::__construct($nimi); $this->kieli = $kieli; } public function puhu() { parent::puhu(); echo "Ohjelmointikieleni on {$this->kieli}. <br>"; } } ?>
Luokka Ohjelmoija
sisältää henkilön nimen lisäksi ohjelmoijan ohjelmointikielen. Ideana on, että luokan funktiot kutsuvat ensin ylemmän luokan vastaavia funktioita. Tämä onnistuu merkinnän parent
avulla.
Seuraava koodi testaa luokkaa Ohjelmoija
:
<?php include("Ohjelmoija.php"); $ohjelmoija = new Ohjelmoija("Aapeli", "PHP"); $ohjelmoija->puhu(); ?>
Koodin tulostus on seuraava:
Hei, nimeni on Aapeli!
Ohjelmointikieleni on PHP.
MVC-malli on yleinen tapa nettisivuston rakenteen suunnitteluun. MVC-mallissa sivusto jakaantuu kolmeen osaan:
Tarkoituksena on, että malli huolehtii sivuston tietosisällöstä, näkymä luo käyttöliittymän ja ohjaimen vastuulla on mallin ja näkymän päivittäminen käyttäjän toimien perusteella. Malli ja näkymä ovat mahdollisimman erillään toisistaan, mikä selkeyttää järjestelmän rakennetta.
MVC-malli ei ole tiukka sääntökokoelma, vaan sen soveltaminen riippuu tilanteesta. Monet PHP:n sovelluskehykset, kuten Symfony ja Zend, hyödyntävät MVC-mallia. Seuraavaksi teemme itse yksinkertaisen MVC-malliin perustuvan sovelluksen.
MVC-mallin esimerkkimme on chat, jossa käyttäjät voivat lähettää anonyymeja yksirivisiä viestejä nettisivulle. Voit kokeilla valmista chat-sovellusta tästä.
Luokka Malli
vastaa chatin tietosisällöstä. Tässä tapauksessa tietosisältönä ovat chatin viestit, jotka tallennetaan tiedostoon viestit.txt
. Luokka tarjoaa funktion viestit
, joka palauttaa viestit taulukossa, sekä funktion lisaa
, joka lisää uuden viestin.
<?php class Malli { private $tiedosto = "viestit.txt"; public function viestit() { if (file_exists($this->tiedosto)) { return file($this->tiedosto); } else { return array(); } } public function lisaa($viesti) { $viesti = date("H:i:s: ") . $viesti; file_put_contents($this->tiedosto, "{$viesti}\n", FILE_APPEND); } } ?>
Seuraava tiedosto nakyma.php
sisältää chatin käyttöliittymän. Näkymä olettaa, että sitä kutsutaan luokassa, jonka taulukko $viestit
sisältää chatissa olevat viestit. Käytännössä tämä luokka tulee olemaan ohjain, jonka teemme seuraavaksi.
<!DOCTYPE html> <html> <head> <title>Chat</title> </head> <body> <h1>Chat</h1> <?php if (empty($this->viestit)) { echo "Chatissa ei ole viestejä."; } else { foreach ($this->viestit as $viesti) { echo htmlspecialchars($viesti) . "<br>"; } } ?> <h2>Uusi viesti</h2> <form action="?toiminto=lahetys" method="post"> <input type="text" name="viesti"> <input type="submit" value="Lähetä"> </form> </body> </html>
Luokka Ohjain
sisältää kaksi funktiota: lista
hakee chatin viestit mallista ja näyttää ne näkymässä, ja lahetys
lähettää uuden viestin malliin ja ohjaa käyttäjän takaisin listaan.
<?php class Ohjain { private $malli; public function __construct() { $this->malli = new Malli(); } public function lista() { $this->viestit = $this->malli->viestit(); include("nakyma.php"); } public function lahetys() { $this->malli->lisaa($_POST["viesti"]); header("Location: chat.php?toiminto=lista"); } } ?>
Kaikki edellä luodut osat kytkee yhteen sivu chat.php
:
<?php include("Malli.php"); include("Ohjain.php"); $ohjain = new Ohjain(); $toiminto = $_GET["toiminto"] ?? null; if (!in_array($toiminto, ["lista", "lahetys"])) { header("Location: chat.php?toiminto=lista"); } else { $ohjain->$toiminto(); } ?>
Tässä sivulle annettu parametri toiminto
kertoo, mistä sovelluksen osasta on kysymys. Chatissa toiminto on käytännössä joko "lista" tai "lahetys". Huomaa muuttujan $toiminto
käyttäminen olion $ohjain
funktion valinnassa.
Tässä tapauksessa MVC-mallin käyttäminen on siinä mielessä turhaa, että vastaavan sovelluksen voisi toteuttaa ongelmitta yksinkertaisemmin ilman MVC-mallia. Esimerkin lähestymistapaa voisi kuitenkin soveltaa laajemmassa järjestelmässä, jossa MVC-mallin todelliset edut tulisivat esille. Tyypillisesti malli, näkymä ja ohjain muodostuvat suuresta määrästä tiedostoja.
Jotain tällaista kaipasinkin! Pitkälle olen päässytkin ilman luokkia, mutta nyt ois jo varmaan aika opiskella niitäkin :P kertalaakista ei vielä tämä homma uponnut, mutta eiköhän tuo harjoittelemalla ja kokeilemalla ala luonnistua!
Erittäin vaikeaa. Pidän olioista, mutta ne saattavat myös sekoittaa pakkaa.. eli pitää selvittää milloin niitä kannattaa hyödyntää, kiitos oppaasta.
Hyvä opas. Itselle oli uutta tuo MVC. Ihmettelin vain sitä, kun Ohjain::lahetys() kutsuu headeria, vaikka sivulla on jo sisältöä...?
vesikuusi, eihän sivulla ole sisältöä silloin. Kun lomake lähetetään, tapahtuu uusi sivunlataus, jolla ei tulosteta minkäänlaista sivua vaan suoritetaan ainoastaan tuo lahetys-metodi. Seuraa koodia rivi kerrallaan, niin ymmärrät.
Moi olinkin unohtanut tämän kokonaan :D Joo kiitti kun avasit asiaa, Metabolix. Jotenkin oli mennyt ohi tuo formin action varmaan, tai koko formi jos on nukkunut huonosti :p
Onneksi on tämä ohjelmointiputka. Täällä on oikein hyvää informaatiota. Tällainen sivistävä asiakokonaisuus pitäisi kaikkien oppia, ainakin perusteet. Kiitos
Huomio! Kommentoi tässä ainoastaan tämän oppaan hyviä ja huonoja puolia. Älä kirjoita muita kysymyksiä tähän. Jos koodisi ei toimi tai tarvitset muuten vain apua ohjelmoinnissa, lähetä viesti keskusteluun.