Lekce 17 - Tvorba vlastních funkcí v PHP
V minulé lekci, Datové typy v PHP, jsme si vysvětlili, jak fungují datové typy v PHP, co je to dynamické typování a jak funguje práce s různými datovými typy.
V dnešním PHP tutoriálu si ukážeme, jak vytvářet vlastní funkce.
Tvorba vlastních funkcí
Zatím jsme pouze používali cizí funkce. Není ovšem nic těžkého vytvořit si funkci vlastní. Rozdělování skriptů do funkcí má následující výhody:
- Přehlednost – Pokud se nějaký algoritmus skládá z více kroků, vytvoříme funkci pro každý tento krok a funkce potom voláme po sobě. Vezměme si např. náš e-mailový formulář z minulých lekcí. Ten zvalidoval, zda bylo vše vyplněné, a poté odeslal e-mail. Vše bylo napsáno na jednom místě a zamotané do sebe. Správně bychom na daném místě měli volat jak funkci pro validaci, tak funkci pro odeslání. Každá funkce potom obsahuje jen to, co do ní patří. Kód se stává přehlednější.
- DRY (neopakujme se) – Zkratka
DRY
označuje Do not Repeat Yourself, tedy neopakujte se. Jedná se o uznávaný programátorský princip, který odsuzuje duplikovaný kód. Chceme-li nějakou část kódu použít vícekrát, nemusíme ji opisovat, ale oddělíme ji do funkce. Poté pouze zavoláme jednu funkci na více místech programu. Když potom chceme v kódu něco opravit, nehledáme chybu na několika místech, ale opravíme ji jen v dané funkci. Více o DRY v samostatné lekci. - Možnost použití knihoven – Funkce, které často používáme, můžeme vkládat do samostatných souborů, kterým se někdy říká knihovny (anglicky library). Pokud si vytvoříme např. knihovnu pro přihlašování uživatelů, můžeme ji potom jen vložit do jiného projektu, načíst a začít používat dané funkce, aniž bychom něco znovu programovali.
V dalších seriálech si ukážeme i to, jak členit funkce do objektů. Dosáhneme tím opravdu velmi čitelného kódu, který lze navíc rozšiřovat.
Správný program by měl být poskládaný z několika kratších funkcí,
neměl by vypadat jako dlouhá nudle kódu
Jednoduchá funkce
Vytvořme si úplně jednoduchou funkci, která jen něco vypíše. Funkci
deklarujeme pomocí klíčového slova function
. Za ním následuje
název funkce (ten by měl být psaný v camelCase
, tedy velbloudí
notaci) a závorky. Tělo funkce je potom vloženo do bloku ze složených
závorek.
Funkce by se měla jmenovat podle toho, co dělá. Měla by také dělat vždy jen jednu věc – pokud potřebujeme udělat dvě věci, vytvoříme si na to dvě funkce. Jako název funkce se nám osvědčilo používat sloveso v rozkazovacím způsobu (imperativu). Jakmile funkci někde deklarujeme, můžeme ji níže volat. Uděláme to jednoduše napsáním jejího názvu a závorek.
Následující kód vložíme kamkoli do PHP sekvence:
{PHP}
function pozdrav()
{
echo('Vítejte na mém webu');
}
pozdrav();
{/PHP}
Výsledek:
Nahoře funkci deklarujeme, níže hotovou funkci voláme.
Funkci musíme pochopitelně vždy deklarovat nad místem, odkud ji voláme. PHP parsuje zdrojový kód od začátku do konce, a pokud bychom to udělali naopak, PHP by narazilo na volání funkce, o které si ještě „nepřečetlo“.
Funkce s parametry
Funkci samozřejmě můžeme vložit i nějaké parametry. Na jednoduchém příkladu si ukažme funkci, která vypíše součet dvou čísel. Takto triviální funkci bychom v praxi sice nedeklarovali, ale jako příklad poslouží dobře. Reálnou funkci si ukážeme na konci. Parametry vkládáme jako proměnné do závorky v hlavičce funkce a oddělujeme je čárkou:
function secti($a, $b) { $soucet = $a + $b; echo("Součet: $soucet"); }
Funkci následně voláme takto:
{PHP}
function secti($a, $b)
{
$soucet = $a + $b;
echo("Součet: $soucet");
}
secti(10, 20);
{/PHP}
Při volání funkce můžeme do parametrů samozřejmě vložit i proměnné:
$a = $_POST['cislo1']; $b = 20; secti($a, $b);
Z funkcí nelze přistupovat k nějaké proměnné, která je definována
mimo funkci. Ve funkci můžeme přistupovat jen k proměnným, které přišly
v parametru, a maximálně k superglobálním polím, jako jsou
$_GET
a $_POST
. Sice to lze obejít pomocí
klíčového slova global
, ale to nedělejte, jelikož taková
funkce ztrácí smysl. Když něco nejde, má to většinou nějaký smysl.
Neměli bychom proto hledat způsob, jak zákaz obejít, ale jak to udělat
správně Vše, co funkce
potřebuje, jí jednoduše předáme ve vstupních parametrech.
Funkce s návratovou hodnotou
Funkce může také vracet nějakou hodnotu. V ukázkách výše jsme ve
funkcích pouze vypisovali. V praxi se takovéto funkce příliš nepíšou,
jelikož nejsou univerzální. Představme si, že nechceme součet vypsat, ale
použít ho jako mezivýsledek při dalším výpočtu. Proto funkce
výslednou hodnotu většinou vrací. Tam, kde byla funkce volána, si
s ní potom můžeme udělat, co chceme. Funkce může vždy vrátit jen
jednu hodnotu, a jakmile ji vrátí, tak se na tom místě
ukončí a další kód v ní se již neprovede. Hodnotu
vracíme pomocí klíčového slova return
.
Pokud potřebujeme vrátit více hodnot, můžeme vrátit pole nebo si pro každou hodnotu vytvořit samostatnou funkci. Záleží na konkrétním případě.
Upravme naši sčítací funkci tak, aby výsledek místo vypisování vracela:
function secti($a, $b) { $soucet = $a + $b; return $soucet; }
Funkci bychom poté volali takto:
{PHP}
function secti($a, $b)
{
$soucet = $a + $b;
return $soucet;
}
$soucet = secti(10, 20);
echo("Součet: $soucet <br />");
echo("Dvojnásobek součtu: " . ($soucet * 2));
{/PHP}
Vidíte, že když naše funkce hodnoty vrací, je naprosto univerzální a můžeme ji použít jak k výpisu, tak k další práci. Výsledek můžeme později uložit třeba do databáze, souboru, kamkoli.
Od verze PHP 7 byly přidané datové typy. Můžeme tak v parametru metody specifikovat, jaký má mít parametr datový typ. Lze také specifikovat, jaký datový typ metoda vrací.
Ukažme si to na stejném příkladu. Jelikož chceme, ať lze sečíst jak
celé, tak i desetinné číslo, u proměnných $a
a
$b
uvedeme datový typ float
. Funkce bude taktéž
vracet float
(ono : float
na konci řádku):
function secti(float $a, float $b): float { $soucet = $a + $b; return $soucet; }
Funkci bychom volali stejně, jen s desetinným číslem:
function secti(float $a, float $b): float { $soucet = $a + $b; return $soucet; } $soucet = secti(5.333, 4.111); echo("Součet: $soucet <br />"); echo("Dvojnásobek součtu: " . ($soucet * 2));
Výstup:
Můžeme klidně změnit návratový typ metody na typ int
(celé číslo):
function secti(float $a, float $b): int { $soucet = $a + $b; return $soucet; } $soucet = secti(5.333, 4.333); echo("Součet: $soucet <br />"); echo("Dvojnásobek součtu: " . ($soucet * 2));
Výsledek však poté bude možná znepokojující:
Součet čísel 5.333
a 4.333
je
9.666
. Výsledek by tedy měl být správně zaokrouhlený nahoru
na číslo 10
, jenže v tomto případě tomu tak není! Metoda
vrátí pouze celé číslo, klidně tedy číslo 9
při výsledku
9.99999999
!
Funkce, které žádnou hodnotu nevracejí, například naše funkce
pozdrav()
, mají návratový typ void
. Datový typ
void
není totéž co typ null
, jelikož zatímco
null
označuje konkrétní hodnotu a datový typ, void
prezentuje absenci hodnoty. U funkcí s návratovým typem void
lze
použít klíčové slovo return
, a to pro dřívější
opuštění těla funkce. Za klíčovým slovem return
však nesmí
být uvedena žádná hodnota:
function pozdrav(string $jmeno): void { if (empty($jmeno)) { // Proměnná $jmeno je prázdný řetězec return; // Nebudeme vypisovat pozdrav a funkci opustíme } echo('Vítej, ' . $jmeno); }
Ve verzi PHP 8 byl uveden další návratový typ never
, který
značí, že funkce nebude nikdy opuštěna. V praxi to znamená, že funkce
obsahuje nekonečný cyklus, rekurzi bez ukončovací podmínky nebo že běh
skriptu končí uvnitř dané funkce. Běh skriptu může být ukončen před
dosažením konce souboru předčasně, a to vyhozením výjimky pomocí
klíčového slova throw
či klíčovými slovy die
a
exit
:
function ukonci(int $stavovyKod = 0): never { exit('Skript byl ukončen s kódem ' . $stavovyKod); }
Od verze PHP 8 jsou definované datové typy pro parametry funkce vždy vynucovány. Dříve bylo možné povolit jejich ignorování, avšak nyní způsobí předání nesprávného datového typu vždy fatální chybu a zastavení běhu skriptu.
Předání nesprávného datového typu si ukážeme na příkladu:
function secti(float $a, float $b): float { $soucet = $a + $b; return $soucet; } $soucet = secti(5.333, "5 textů"); echo("Součet: $soucet <br />"); echo("Dvojnásobek součtu: " . ($soucet * 2));
Vyvoláme fatal error a skript pokračovat nebude:
Totéž musí platit i o návratové hodnotě. Pokud má funkce vracet
například datový typ int
, avšak vrátí typ null
,
dojde také k chybě:
function vydel(int $delenec, int $delitel): int { if ($delitel === 0) return null; $podil = $delenec / $delitel; return $podil; } $podil = vydel(48, 0); echo("Podíl: $podil <br />");
Rovněž vyvoláme fatal error a skript nebude dále pokračovat:
Složené datové typy
Od verze PHP 8 máme možnost povolit navrácení nebo předání
proměnných o několika různých datových typech. Funkce vydel()
může být návratového typu int
nebo null
,
například v případě dělení nulou, ale musíme jako návratový typ uvést
?int
. Přidáním otazníku před název datového typu tedy
povolíme použití hodnoty null
.
To platí u jakéhokoli datového typu, a to i v seznamu parametrů:
function pozdrav(string $jmeno, ?string $dodatek = null): void { echo('Vítej, ' . $jmeno . '<br>'); if (!is_null($dodatek)) echo($dodatek); } pozdrav('Jiří', null); // Lze nahradit i za pozdrav('Jiří'); echo('<hr />'); pozdrav('Jiří', 'Dnes budeš mít šťastný den!');
Povolení hodnoty null
bylo možné již ve verzi
PHP 7, avšak v PHP 8 je možné kombinovat i jiné datové typy.
Na příkladu si ukážeme kombinaci datového typu int
a
string
:
function jePrvniVetsi(string|int $a, string|int $b) : ?bool { if (gettype($a) !== gettype($b)) return null; if (gettype($a) === 'string') { return (mb_strlen($a) > mb_strlen($b)); } return ($a > $b); } var_dump(jePrvniVetsi('Superman', 'Batman')); echo('<br>'); var_dump(jePrvniVetsi(42, 73)); echo('<br>'); var_dump(jePrvniVetsi(56, 'Svíce'));
Výstup:
Jak vidíme, můžeme používat datový typ int
i
string
a k fatální chybě nedojde.
Funkce na odesílání e-mailu
Velmi užitečná může být funkce k posílání e-mailů. Nyní máme již dostatek znalostí k tomu, abychom si takovou funkci napsali. Když se podíváme zpět na kód k odeslání e-mailu v našem formuláři, dokážeme zformulovat následující funkci:
function odesliEmail(string $adresa, string $predmet, string $odesilatel, string $zprava): bool { $hlavicka = 'From:' . $odesilatel; $hlavicka .= "\nMIME-Version: 1.0\n"; $hlavicka .= "Content-Type: text/html; charset=\"utf-8\"\n"; return mb_send_mail($adresa, $predmet, $zprava, $hlavicka); }
Funkce bude mít všechny parametry typu string
, jelikož se
jedná o textové řetězce. Funkce si připraví hlavičku na základě
vstupních parametrů a tu potom předá funkci mb_send_mail()
,
která e-mail odešle. Tato funkce vrátí hodnotu
true
/false
podle toho, zda bylo odeslání
úspěšné, či nikoli. Tuto hodnotu potom vrátíme i naší funkci, jejíž
návratový typ je proto nastaven na hodnotu bool
.
Volání funkce bude následující:
$uspech = odesliEmail('[email protected]', 'Test e-mailu', '[email protected]', 'Text zprávy'); if (!$uspech) echo('E-mail se nepodařilo odeslat, zkontrolujte adresu a odesílatele');
Určitě uznáte, že psát těch pět řádků, které jsou nyní ve funkci,
na každém místě, kde potřebujeme odeslat e-mail, je minimálně
zdržující a nepřehledné. Takto napíšeme jen jeden řádek. Funkce však
funguje jen v tom souboru, ve kterém je deklarována. Můžete si také
vyzkoušet odeslat e-mail na číslo 5
a nebo dát text zprávy
jako integer
(také třeba 5
). Uvidíte, že vám
takovéto podivné využití funkce opět vyvolá fatální chybu kvůli
neplatným argumentům.
V následujícím kvízu, Kvíz - Textové řetězce, datové typy a vlastní funkce v PHP, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.