Funktiot, luokat ja oliot - Luento 7
Luennolla käsitellään PHP:n funktioita, luokkia ja olioita. Tutkitaan miten parametrien välitys toimii. Katsotaan, miten voidaan tehdä luokkia, luoda oliota ja käyttää niitä.
Luentotaltiointi
- sovellukset07.mp3 16M
- sovellukset07.wmv 62M
- sovellukset07.avi 214M
Tämän luennon lähteenä on käytetty osittain Sami Kollanuksen tekemää PHP-opasta. Kiitokset!
Funktiot
Yleistä funktioista
Funktiot määritellään PHP:ssä seuraavasti:
function nimi(parametri 1, parametri 2, ...) { toteutus }
Funktioille ei siis määritellä palautusarvolle tai parametreille tietotyyppiä, kuten Javan metodeissa ja C-kielen funktioissa tehdään. Funktiot voidaan kirjoittaa mihin kohtaan koodia tahansa. Niitä ei ole siis välttämätöntä kirjoittaa koodiin ennen käyttöä.
Esimerkkifunktio:
<?php
function tulosta_kertotaulu($riveja, $sarakkeita) {
echo "<table>\n<tr><th> </th>";
// ensimmäinen rivi
for($j=1; $j<=$sarakkeita; $j++) {
echo "<th>$j</th>";
}
echo "</tr>\n";
for($i=1; $i<=$riveja; $i++) {
// ensimmäinen sarake
echo "<tr><th>$i</th>";
for($j=1; $j<=$sarakkeita; $j++) {
// normaalit solut
echo "<td>".($i*$j)."</td>";
}
echo "</tr>\n";
}
echo "</table>\n";
}
tulosta_kertotaulu(3,5);
tulosta_kertotaulu(9,9);
tulosta_kertotaulu(20,10);
?>
Parametrien välitys
Funktion parametrien nimet määrätään suluissa. Funktiota voi kutsua ylimääräisillä
parametreilla, mutta nämä jätetään huomiotta. Funktio voi palauttaa jonkin
arvon return-lausekkeella. Tällöin funktion suoritus loppuu ja annettu
arvo palautuu siihen kohtaan, jossa funktiota kutsuttiin.
<?php
function tulosta_teksti($teksti) {
echo $teksti;
}
$test = array("Tekstiä"," ","testiin.");
tulosta_teksti(1,"Muuli",$test); // tulostaa: 1
?>
Kaikki saadut parametrit voidaan käydä läpi func_-alkuisilla funktioilla:
<?php
function summaa_luvut() {
$param_lkm = func_num_args();
$param_arvot = func_get_args();
$summa = 0;
for($i=0; $i<$param_lkm; $i++) {
$parametri = $param_arvot[$i];
if(is_numeric($parametri)) {
$summa = $summa + $parametri;
}
}
return $summa;
}
echo "<p>".summaa_luvut(1, 4, 5)."</p>"; // tulostaa 10
echo "<p>".summaa_luvut(2, 2, 2, 5)."</p>"; // tulostaa 11
?>
Funktion parametrina voi olla myös taulukko:
<?php
function tulosta_tekstit($teksti, $taulukko, $teksti2) {
echo $teksti;
foreach($taulukko as $muuttuja) {
echo $muuttuja;
}
echo $teksti2;
}
$test = array("Tekstiä"," ","testiin.");
tulosta_tekstit("<p>",$test,"</p>");
?>
Parametri referenssinä
Parametrit välitetään PHP:n funktioissa arvoperusteisesti, eli parametrina annetun muuttujan arvo kopioidaan funktiossa määriteltyyn lokaaliin muuttujaan.
Normaali parametrin välitys väärin käytettynä:
<?php
function lisaa_kaksi($summattava) {
// $summattava saa arvon 6, mutta näkyvyys on vain funktion sisällä
$summattava = $summattava + 2;
// $summattava on 8, mutta mitään ei palauteta funktiosta
}
$luku = 6;
lisaa_kaksi($luku);
echo $luku; // tulostaa: 6
?>
Normaali parametrin välitys oikein käytettynä:
<?php
function lisaa_kaksi($summattava) {
// $summattava saa aluksi arvon 6, ei muuta alkuperäistä arvoa $luku
$summattava = $summattava + 2;
// $summattava on 8 ja se palautetaan funktion arvona
return $summattava;
}
$luku = 6;
// funktion arvo sijoitetaan muuttujaan $luku
// muuttaa entisen arvon 6:sta 8:aan
$luku = lisaa_kaksi($luku);
echo $luku; // tulostaa: 8
?>
Esimerkeistä nähdään, että funktiossa muutettu muuttujan arvo vaikuttaa ainoastaan funktion sisällä. Muutos ei vaikuta alkuperäiseen parametrina annettuun muuttujaan ($luku). Alapuolella vielä yksi esimerkki selventämässä asiaa.
<?php
function tulosta_lkm() {
echo "<p>Lukumäärä on $lkm</p>";
}
function tulosta_omalkm() {
$lkm = 3;
echo "<p>Oma lukumäärä on $lkm</p>";
}
function tulosta_paramlkm($lukumaara) {
echo "<p>Funktioon tuotu lukumäärä on $lukumaara</p>";
}
// ei tulosta lukumäärää, koska $lkm ei ole asetettu
print "<p>Funktion ulkopuolella lukumäärä on $lkm</p>";
// ei tulosta määrää, koska funktiossa ei määritelty $lkm
tulosta_lkm();
// tulostaa 3, koska funktion sisällä on määritelty $lkm-muuttuja
tulosta_omalkm();
// ei tulosta mitään, koska parametria ei olla välitetty funktioon
// antaa myös varoituksen koska parametria ei ole annettu!
tulosta_paramlkm();
// määrätään globaalilla näkyvyysalueella muuttuja $lkm
$lkm = 8;
// tulostaa 8
echo "<p>Funktion ulkopuolella lukuja on $lkm</p>";
// ei tulosta mitään edelleenkään
tulosta_lkm();
// tulostaa 3, koska funktion sisällä on määritelty muuttuja $lkm
tulosta_omalkm();
// tulostaa 8, koska $lkm oli määritelty ja se vietiin parametrina funktiolle
tulosta_paramlkm($lkm);
?>
Jos funktion halutaan oikeasti muuttavan parametrina annetun muuttujan arvoa, voidaan parametri määrittää viitemuuttujaksi alla olevan esimerkin mukaisesti.
Parametri referenssinä:
<?php
function lisaa_kaksi(&$summattava) {
// $summattava viittaa nyt annettuun parametriin, tässä tapauksessa muuttujaan $luku
$summattava = $summattava + 2;
// $luku-muuttujan arvo kasvaa kahdella
}
$luku = 6;
lisaa_kaksi($luku);
echo $luku; // tulostaa: 8
?>
Esimerkki on samanlainen kuin edelliset sillä poikkeuksella, että funktion parametri on määritelty viitemuuttujaksi. Viitemuuttuja ilmaistaan &-merkillä esimerkin mukaisesti. Viitemuuttujan käytöllä saadaan nyt funktio muuttamaan alkuperäisen muuttujan ($luku) arvoa.
Referenssi välitettynä toiseen funktioon:
<?php
function lisaa_kaksi(&$summattava) {
// $summattava viittaa nyt muuttujaan $luku
$summattava = $summattava + 2; // $luku-muuttujan arvo kasvaa kahdella
}
function test(&$testi) {
// $testi viittaa nyt muuttujaan $luku
lisaa_kaksi($testi);
}
$luku = 6;
test($luku);
echo $luku; // tulostaa: 8
?>
Parametrien oletusarvon käyttö
Funktion parametreille voidaan määrittää oletusarvot, joita käytetään, mikäli käyttäjä ei niitä anna. Alla on esimerkki tästä:
<?php
function kuution_tilavuus($a, $b=10, $c=10) {
return $a*$b*$c;
}
echo kuution_tilavuus(10)."<br />\n"; // tulostaa: 1000
echo kuution_tilavuus(10, 5)."<br />\n"; // tulostaa: 500
echo kuution_tilavuus(10, 5, 3)."<br />\n"; // tulostaa: 150
?>
Esimerkissä on annettu parametreille $b ja $c oletusarvo 10. Sitä käytetään, mikäli funktion kutsussa ei parametreja anneta. Tästä on hyvä huomata, että on mahdotonta antaa funktiolle parametreina esimerkiksi $a ja $c. Parametrit luetaan aina määrittelyjärjestyksessä. Jos annetaan yksi parametri, se on aina $a, ja kaksi parametria ovat aina $a ja $b.
Funktio-osoittimet
Jos muuttujassa on funktion nimi, niin sen nimistä funktiota voidaan kutsua suoraan antamalla muuttujan perään suluissa parametrit.
<?php
function tulosta_kissa() {
echo "Kissa";
}
function tulosta_luku($luku) {
echo $luku;
}
$funktio = "tulosta_kissa";
$funktio(); // tulostaa: kissa
$funktio = "tulosta_luku";
$funktio(3); // tulostaa: 3
?>
Lisätietoa
- PHP.net: Functions
- PHP.net: Function Arguments
- PHP.net: func_get_args
- PHP.net: func_num_args
- PHP.net: Variable Functions
Oliot ja luokat
Olio on luokan ilmentymä. Luokka puolestaan on kokoelma hyvin pitkälti funktion tapaisia toimintoja, eli metodeja, sekä muuttujia, joita luokkien yhteydessä nimitetään attribuuteiksi. Yhdestä luokasta voi luoda mielivaltaisen määrän olioita. Jokaisella oliolla attribuuttien arvot voivat vaihdella, mutta attribuuttien nimet ja metodit ovat kaikilla saman luokan olioilla yhtenäisiä. Näin luokka on kuin kaava, jonka avulla luodaan olioita.
Esimerkkiluokka ja sen käyttö lyhyesti:
<?php
/*
* Esimerkkiluokka WWW-sovellukset-kurssin luennolle 7.
* Alapuolella lisäksi lyhyt esimerkki luokan käytöstä.
* Tekijä: Antti Ekonoja, viimeksi päivitetty: 6.3.2008
*
* Metodeja:
* get_all_pages() hakee kaikki sivut tietokannasta
* get_pages($where) hakee sivuja tietokannasta where-ehdon perusteella
* get_page($id) hakee sivun tietokannasta id:n perusteella
* get_pagecount() hakee sivujen lukumäärän tietokannassa
*/
class pagehandler {
// attribuutit
private $database;
// konstruktori
// oletetaan, että on kuvitteellinen tietokantaluokka database */
function __construct($database) {
$this->database = $database;
}
// destruktori
function __destruct() {
$this->database = NULL;
}
// metodit
public function get_all_pages(){
return $this->database->get('page','*');
}
public function get_pages($where) {
return $this->database->get('page','WHERE '.$where);
}
public function get_page($id) {
return $this->database->get('page','id=\''.$id.'\'');
}
public function get_pagecount() {
return $this->database->get_count('page','*');
}
} // luokka loppuu
// muuttaa tietokannasta löytyneet rivit taulukoksi (parametrina osoitin)
function fetch_array($result) {
return mysql_fetch_array($result); // vain mysql-tietokannalle
}
// luokaan uudet oliot
// todellisuudessa databasen arvot kannattaisi tietenkin hakea joltain
// config-luokalta
$database = new database('server', 'database', 'username', 'password');
$pagehandler = new pagehandler($database);
// haetaan kaikki sivut tietokannasta
$pagepointer = $pagehandler->get_all_pages();
// tehdään löytyneille sivuille jotain
while($pagearray = fetch_array($pagepointer)) {
echo '<p>'.$pagearray['name'].'</p>\n';
}
?>
Alla olevat linkit osoittavat Mureakuha.com-ohjelmointisivuston hyvään PHP-oppaaseen. Kannattaa kuitenkin huomata, että jotkut esimerkit oppaassa käyttävät vielä PHP4:sta, jonka olio-ominaisuudet ovat todella suppeat PHP5:seen verrattuna.
Esimerkiksi luokan attribuuttien määrittelyssä tulee huomata se, että käyttämällä attribuutin nimen edessä PHP4:sen varattua sanaa var attribuutista tulee aina julkinen (PHP4:ssa ei ollut vielä näkyvyysasteita). Julkisia attribuutteja ei kuitenkaan PHP5:ssa kannata käyttää kuin poikkeustapauksissa. Tämän vuoksi on tärkeää, että attribuutin nimen eteen laittaa var-sanan sijaan joko private (yksityinen) tai protected (suojattu, olion itsensä lisäksi vain periytyneet luokat näkevät sen). PHP5:ssa julkisen attribuutin määre on public, mutta sen käyttöä tulee siis välttää.
Yleistä luokista ja olioista
Perintä
Näkyvyysasteet
Staattiset metodit ja attribuutit
Olioiden järjestäminen
Olioiden kloonaaminen (kopioiminen)
Haluttaessa kloonata olio (tehdä oliosta kopio) ei riitä pelkästään sijoittaa oliota uuteen muuttujaan. Tällöin uusi muuttuja olisi vain osoitin alkuperäiseen olioon, eli kaikki uuteen olioon tehdyt muutokset välittyisivät myös alkuperäiselle oliolle. Oikea ratkaisu on luoda uusi olio alkuperäisen olion arvojen pohjalta. Tämän voi tehdä itse eri kontrollirakenteiden ja operaatioiden avulla tai siihen voi tehdä vaikka oman luokan ja olion hoitamaan kloonausta. Katso olioilla tehty esimerkki PHP:netin sivuilta. Alla on esimerkkiratkaisu ilman olioita.
<?php
class elain {
private $nimi;
function __construct($arvo) {
$this->nimi = $arvo;
}
function __destruct() {
$this->nimi = NULL;
}
public function anna_arvo() {
return $this->nimi;
}
public function aseta_arvo($arvo) {
$this->nimi = $arvo;
}
}
$arvo = "kissa";
$olio1 = new elain($arvo);
$olio2 = $olio1; // Nyt $olio2 on vasta osoitin $olio1:een
$olio2->aseta_arvo("koira");
echo $olio2->anna_arvo(); // Tulostaisi "koira"
echo $olio1->anna_arvo(); // Tulostaisi "koira"
$vanha_arvo = $olio1->anna_arvo();
// Nyt luodaan klooni $olio1:stä, joka ei kuitenkaan ole pelkkä osoitin
$olio3 = new elain($vanha_arvo);
echo $olio3->anna_arvo(); // Tulostaisi "koira"
echo $olio1->anna_arvo(); // Tulostaisi "koira"
$olio3->aseta_arvo("jänis");
echo $olio3->anna_arvo(); // Tulostaisi "jänis"
echo $olio1->anna_arvo(); // Tulostaisi "koira"
?>
__set ja __get-metodit
PHP:ssä kaikki ei-vakioiset attribuutit kannattaa pitää yksityisinä (private) ja arvon asetus sekä lukeminen tapahtuu erikseen itse määriteltyjen settereiden (set_attribuutin_nimi) ja gettereiden (get_attribuutin_nimi) avulla. PHP5:ssa on kuitenkin tähän tarkoitukseen erikseen erikoismerkityksen omaavat __set($attribuutin_nimi,$arvo) ja __get($attribuutin_nimi) -metodit, jotka voidaan halutessa määrittää luokalle. Niiden avulla on mahdollista rajoittaa melko helposti olion attribuuttien luku- ja kirjoitusoikeuksia. Toinen etu on se, ettei tarvitse tehdä erikseen settereitä ja gettereitä kaikille attribuuteille. Alla on yksinkertainen esimerkki kyseisten metodien luomisesta ja käyttämisestä.
<?php
class Luokka {
private $arvo;
private $arvo2;
public function __set($attribuutti,$value) {
$this->$attribuutti = $value;
}
public function __get($attribuutti) {
return $this->$attribuutti;
}
}
$olio = new Luokka();
$olio->arvo = "Matti Meikäläinen";
$olio->arvo2 = "Maija Meikäläinen";
echo $olio->arvo."\n"; // Tulostaa: Matti Meikäläinen
echo $olio->arvo2."\n"; // Tulostaa: Maija Meikäläinen
?>

Käyttäjien kommentit