↳ Funkce
Už v kapitole o [odkaz]základech syntaxe[/odkaz] jsme si řekli o funkcích, argumentech (resp. parametrech) a jak zavolat
vestavěnou funkci PHP. Později jsme se seznámili i s dalšími funkcemi, ale stále šlo o vestavěné funkce PHP. V této
kapitole si ukážeme, jak vytvářet své vlastní funkce.
Poznámka: Funkce se týkají zejména procedurálního programování v PHP. Pokud chcete raději programovat objektově, místo
funkcí budete vytvářet třídy a metody. Z této a následujících kapitol ale budete potřebovat jednu důležitou znalost, a sice
kontext, který se uplatní i v objektově-orientovaném programování.
↳ Zápis funkce
Funkce se zapisuje klíčovým slovem function, následovaným závorkami, ve kterých mohou být definovány argumenty, a kódem
funkce ve složených závorkách.
Příklad:
function pozdrav() {
echo "Ahoj!";
}
Volání funkce pak vypadá stejně jako u vestavěných funkcí, tedy např.:
Poznámka: V názvech funkcí se, stejně jako u proměnných, doporučuje používat jen písmena bez diakritiky, čísla, pomlčku
a podtržítko. PHP dovoluje i některé další znaky, ale je lepší si vystačit jen s těmi uvedenými, například některá
vývojová prostředí mohou s jinými znaky v názvu mít problémy.
Velikost písmen se u funkcí nerozlišuje, takže u funkce pozdrav() definované výše budou fungovat i volání
Pozdrav();
PozdraV()
či
POZDRAV()
. Že to funguje ale ještě neznamená, že je to dobrý
nápad. Kvůli přehlednosti byste vždy měli používat jen jednu formu zápisu, čili volání stejné jako definici.
↳ Argumenty
Funkce také může mít argumenty a může vracet hodnotu. Hodnota se vrací pomocí klíčového slova return a argumenty se píší
do závorek za název funkce.
Příklad:
function sude($cislo) {
// zkontroluje, zda je číslo sudé
// Číslo je sudé, pokud číslo modulo 2 je 0
return (($cislo % 2) == 0) ? true : false;
}
Poznámka: Výsledkem samotného
(($cislo % 2) == 0)
je true nebo false, takže zápis by mohl být ještě kratší:
return (($cislo % 2) == 0)
. Zápis s podmíněným operátorem byl použit jen proto, aby bylo na první pohled zřejmé,
co funkce vrací.
Volání funkce by pak vypadalo třeba sude(5);
a jeho výsledkem bude hodnota, kterou vrací funkce. Celý kód by
tedy mohl vypadat třeba takto:
<?php
// vložení hlavičky stránky, kterou jsme vytvořili ve [odkaz]4. kapitole[/odkaz]
include "spolecna_hlavicka.php";
function sude($cislo) {
// zkontroluje, zda je číslo sudé
return (($cislo % 2) == 0) ? true : false;
}
if (isset($_GET["cislo"])) {
// Konverze na číslo, zároveň případné nežádoucí vstupy změní na 0
$vstup = intval($_GET["cislo"]);
$vyrok = "není";
if (sude($vstup)) {
$vyrok = "je";
}
echo "Číslo $cislo $vyrok sudé";
}
// níže HTML formulář pro odeslání
?>
<form action="" method="get">
<label for="cislo">Zadejte číslo:</label>
<input type="text" name="cislo" id="cislo">
<input type="submit" name="odeslat" value="Odeslat">
</form>
Funkce může mít argumentů i více, pak se oddělují čárkami. Také lze argumentům zadat výchozí hodnoty. Argumenty se zadanou
výchozí hodnotou by měly být v seznamu argumentů funkce na konci (jinak to nemá smysl) a stávají se nepovinnými. Argumenty
bez výchozí hodnoty jsou povinné a při volání funkce je nelze vynechat. To si můžete vyzkoušet, když funkci sude
z příkladu výše zkusíte zavolat bez argumentu:
sude();
Výsledkem bude varování „Missing argument 1 for sude()“.
Výchozí hodnota u nepovinných argumentů se zadává takto:
function funkce($prvni, $druhy=0, $treti=1) { …
Příklady volání takové funkce:
funkce(1, 2, 3); // hodnoty argumentů jsou 1, 2, 3
funkce(1, 2); // hodnoty argumentů jsou 1, 2, 1
funkce(1); // hodnoty argumentů jsou 1, 0, 1
funkce(); // skončí chybou, první argument musí být uveden
↳ Předání argumentu referencí
Když se funkci předá proměnná jak jsme to dělali výše, zkopíruje se do funkce jen její hodnota a pracuje se s ní
nezávisle na původní proměnné.
Proto například toto nefunguje dle očekávání:
<?php
function dvakrat($hodnota) {
$hodnota = $hodnota * 2;
}
$cislo = 5;
dvakrat($cislo);
echo $cislo; // vypíše 5 a ne 10
?>
Protože uvnitř funkce se pracuje jen s kopií hodnoty, případné změny se neprojeví vně funkce. Toho můžeme docílit tak, že
do funkce předáme tzv.
referenci na proměnnou. Reference se označuje při definici funkci uvedením ampersandu (&)
před daný argument. Když se pak funkci předá nějaká proměnná, předá se jí odkaz přímo na tu proměnnou a změny její
hodnoty uvnitř funkce se projeví i vně funkce.
Upravený příklad tedy již bude fungovat dle očekávání:
<?php
function dvakrat(&$hodnota) {
$hodnota = $hodnota * 2;
}
$cislo = 5;
dvakrat($cislo);
echo $cislo; // vypíše 10
?>
Reference se občas hodí, ale neměli byste je používat místo návratové hodnoty (což dělá i příklad výše). Použití
návratové hodnoty bývá přehlednější.
Příklad výše by tedy šlo upravit takto:
<?php
function dvakrat($hodnota) {
return $hodnota * 2;
}
$cislo = 5;
$cislo = dvakrat($cislo);
echo $cislo; // vypíše 10
?>
[h2#n5]Rekurze[/preh2]
Funkce může volat i sama sebe (tzv. rekurze). Není to ale příliš časté a patří to mezi pokročilejší techniky
programování. Stejně jako u cyklů i u rekurze je třeba dát pozor, aby řetěz volání někdy skončil a nenastal nekonečný
cyklus.
Klasickým příkladem využití rekurze je výpočet faktoriálu čísla:
function faktorial($cislo) {
if ($cislo < 0) return 0; // chyba
if($cislo < 2) return 1; // 0! a 1! = 1
else {
// Jinak je výsledek číslo krát faktoriál o 1 nižšího čísla
return $cislo * faktorial($cislo - 1);
}
}
Rekurze je ovšem oproti jiným technikám náročnější na systémové zdroje, zejména při větším počtu vnoření, takže bývá
často výhodnější rekurzivní volání funkce nahradit cyklem. To lze udělat i v případě faktoriálu:
function faktorial($cislo) {
if ($cislo < 0) return 0; // chyba
$vysledek = 1;
for($i = 2; $i <= $cislo ; $i++) {
$vysledek = $vysledek * $i;
}
return $vysledek;
}
↳ Návrh funkcí
Několik užitečných rad, jakým způsobem organizovat kód do funkcí:
- Smyslem funkcí je mít kód řešící určitý problém na jednom místě a přitom opakovaně použitelný.
- Pamatujte, že na spoustu problémů existují vestavěné funkce v PHP. Jak se říká, je zbytečné znovu vynalézat kolo.
Čím víc máte pocit „Na tohle už přece musel narazit někdo přede mnou“, tím usilovněji hledejte již existující řešení.
- Už dvě různá místa ve skriptu kde řešíte stejný problém jsou důvod k zamyšlení, zda na řešení toho problému neudělat
funkci.
- Funkce by měla být maximálně nezávislá na okolním kódu. Přebírá-li nějaké hodnoty z okolního kódu, mělo by se to dít
skrze argumenty. Předává-li hodnoty okolnímu kódu, mělo by se to dít skrze návratovou hodnotu. Jiný postup je možný jen
když k němu je vážný důvod.
- Jedna funkce by měla řešit jeden problém. Složitá funkce řešící více různých problémů současně je těžko použitelná,
když pak jinde chcete řešit jen jeden z nich. Typickým příkladem dvou problémů je realizace nějakého výpočtu
a prezentace jeho výsledků. Funkce by měla buď ukládat či vypisovat nějaká data, nebo realizovat nějaký výpočet, ale
neměla by dělat obojí najednou. Například funkce faktorial výše provádí výpočet. Častá chyba začátečníků je, že podobnou
funkci navrhnou asi takto:
function faktorial($cislo) {
if ($cislo < 0) echo "Chybný vstup!"; // Chyba
$vysledek = 1;
for($i = 2; $i <= $cislo ; $i++) {
$vysledek = $vysledek * $i;
}
echo "Faktoriál $cislo je $vysledek";
}
Tato funkce řeší jak výpočet, tak prezentaci jeho výsledků. Ovšem pokud jinde sice chci spočítat faktoriál, ale potom
chci s výsledkem udělat něco jiného, než vypsat text "Faktoriál $cislo je $vysledek", nelze takovou funkci použít.
Proto je lepší navrhnout funkci řešící jen jeden problém (výpočet) a druhý problém (prezentaci výsledků) řešit zvlášť.