Black Friday je tu! Využij jedinečnou příležitost a získej až 80 % znalostí navíc zdarma! Více zde
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
BF extended 2022

Lekce 15 - Tvorba vlastních funkcí v PHP

V minulé lekci, Datové typy v PHP, jsme si vysvětlili, jak fungují datové typy v PHP, co to je 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áš emailový formulář z minulých lekcí. Ten zvalidoval zda bylo vše vyplněné a poté odeslal email. Vše bylo napsáno na jednom místě a zamotané do sebe. Správně bychom na daném místě měli volat jen funkci pro validaci a funkci pro odeslání. Každá funkce potom obsahuje jen to, co do ní patří. Kód se stává přehlednějším.
  • 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. Pokud chceme 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 té jedné funkci.
  • Možnost použití knihoven - Funkce, které často používáte, můžete vkládat do samostatných souborů, kterým se někdy říká knihovny (anglicky library). Pokud si vytvoříte např. knihovnu pro přihlašování uživatelů, můžete ji potom jen vložit do jiného projektu, načíst a začít používat dané funkce, aniž byste něco znovu programovali.

V dalších seriálech si ukážeme i 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) 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 a pokud potřebujeme udělat 2 věci, vytvoříme si na to 2 funkce. Jako název funkce se mi 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:

function pozdrav()
{
    echo('Vítejte na mém webu');
}

pozdrav();

Výsledek:

Your page
localhost

Nahoře deklarujeme funkci, níže hotovou funkci zavolá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, narazilo by 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 a 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:

secti(10, 20);

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ůžete 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 jde 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 a neměli bychom hledat způsob, jak obejít zákaz, 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íší, jelikož nejsou univerzální. Představte 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řípadu.

Upravme naší 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:

$soucet = secti(10, 20);
echo("Součet: $soucet <br />");
echo("Dvojnásobek součtu: " . ($soucet * 2));

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 do parametru metody specifikovat, jaký má mít parametr datový typ. Jaký datový typ metoda vrací, lze také specifikovat.

Ukažme si to na stejném příkladu. Jelikož chceme, ať jde sečíst jak celé, tak i desetinné, uvedeme datový typ float. Funkce bude taktéž vracet float:

function secti(float $a, float $b) : float
{
    $soucet = $a + $b;
    return $soucet;
}

A volali bychom ji 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:

Your page
localhost

Můžeme klidně změnit návratový typ metody na 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 je možná znepokojující:

Your page
localhost

Součet čísel 5.333 a 4.333 je 9.666, čili by to mělo být správně zaokrouhleno 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éž jako 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 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 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:

Vlastní funkce
localhost

To samé musí platit i o návratové hodnotě. Pokud má funkce vracet například int, avšak vrátí 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:

Vlastní funkce
localhost

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éhokoliv datového typu, a to i v seznamu parametrů:

function pozdrav(string $jmeno, ?string $dodatek = null) : void
{
    echo('Vitej, ' . $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!');
Vlastní funkce
localhost

Povolení 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:

Vlastní funkce
localhost

Jak je vidět, můžeme používat datový typ int i string a k fatální chybě nedojde.

Funkce na odesílání emailu

Jako velmi užitečná funkce je funkce k posílání emailů. 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í emailu v našem formuláři, dáme dohromady 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";
    $uspech = mb_send_mail($adresa, $predmet, $zprava, $hlavicka);
    return $uspech;
}

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á email odešle. Tato funkce vrátí hodnotu true/false podle toho, zda bylo odeslání úspěšné či nikoli. Toto hodnotu potom vrátíme i naší funkcí, jejíž návratový typ je proto nastaven na bool.

Volání funkce bude následující:

$uspech = odesliEmail('[email protected]', 'Test emailu', '[email protected]', 'Text zprávy');
if (!$uspech)
    echo('Email se nepodařilo odeslat, zkontrolujte adresu a odesílatele');

Určitě uznáte, že psát těch 5 řádků, co je nyní ve funkci, na každém místě, kde potřebujeme odeslat email, je minimálně zdržující a nepřehledné. Takhle 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 email na číslo 5 a nebo dát text zprávy jako integer (taky třeba 5). Uvidíte, že vám takovéto podivné využití funkce vyvolá opět fatální chybu kvůli neplatným argumentům.


 

Předchozí článek
Datové typy v PHP
Všechny články v sekci
Základní konstrukce jazyka PHP
Přeskočit článek
(nedoporučujeme)
Kvíz - Textové řetězce, datové typy a vlastní funkce v PHP
Článek pro vás napsal David Čápka
Avatar
Uživatelské hodnocení:
83 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 13 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity

 

 

Komentáře
Zobrazit starší komentáře (10)

Avatar
Michaela Radimská:27.7.2018 7:07

Mně to funguje i takhle:

echo secti(10,20);

function secti (int $a, int $b) : int{
    return $a+$b;
}

Vypíše 30...

 
Odpovědět
27.7.2018 7:07
Avatar
Matěj Bína
Člen
Avatar
Odpovídá na Michaela Radimská
Matěj Bína:17.2.2019 15:41

Manuál praví:

Functions need not be defined before they are referenced, except when a function is conditionally defined...

Pokud tedy z nějakého důvodu vytvoříme funkci až jako součást dalšího kódu (v příkladech je to if ... anebo jiná funkce generující funkci), můžeme ji volat až poté, co bezpečně existuje. Jinak si jí teoreticky lze zavolat kdykoli...

Další otázka je, jestli tomu v nějaké starší verzi PHP nebylo jinak. Nezdá se, že by tomu tak bylo.

Každopádně volání funkce až po jejím definování mi přijde normální a přirozené. Je to jeden z důvodů, proč se nemůžu smířit s Javou.

 
Odpovědět
17.2.2019 15:41
Avatar
Daniel Vraspír:22.1.2020 13:46

Mohl bych požádat o přidání možnosti stáhnutí obsahu této lekce abych se mohl podívat, jak je to celé napsané? Děkuji.

Odpovědět
22.1.2020 13:46
Život je jedna velká hra.
Avatar
Robert Benedikt:9.4.2020 15:56

Jak je to s vkládáním souborů (include_once/re­quire_once) ve funkci?

Pokud vytvořím funkci, která má (relativně) dlouhý kód (stovky řádků) a chci ušetřit čas načtení stránky pro případ, že se zrovna tato funkce na dané stránce nepoužije, mohu celý kód této funkce vložit do samostatného souboru a ve funkci ho pouze vložím odkazem na soubor (include_once/re­quire_once).
Při načítání stránky se načte funkce (která má ale pouze 3 řádky).
Pokud na stránce není tato funkce použitá, načetl jsem zbytečně pouze ty 3 řádky.
Pokud se na stránce tato funkce někde volá, spustí se ta funkce a s ní i následující includa.

Nyní to používám, ale nevím, zda to není v něčem kontraproduktivní.

Poradí mi někdo, jestli má opravdu význam dlouhé kódy funkcí řešit raději vkládaným souborem?

 
Odpovědět
9.4.2020 15:56
Avatar
Kamil
Člen
Avatar
Kamil:2.5.2021 12:25

Ahoj,

co je to ta .= ?

 
Odpovědět
2.5.2021 12:25
Avatar
Milan Turyna
Tvůrce
Avatar
Odpovídá na Kamil
Milan Turyna:3.5.2021 8:06

Pripsani nejakeho textu do jiz vytvoreneho retezce. Priklad:

$text = "Ahoj! ";
$text .= "Dnes je 3.5.2021";
echo $text; // Ahoj! Dnes je 3.5.2021
Odpovědět
3.5.2021 8:06
Řeš pouze to, co dokážeš ovlivnit.
Avatar
Dušan Kovářík:6.7.2021 5:17

Díky za krásný článek. Všechno je napsáno jednoduše a jasně. Jsem rád, že vlastní funkce jsou popsány ještě v "neobjektovém" tutoriálu, protože si můžu už teď mnohem lépe strukturovat kód.

 
Odpovědět
6.7.2021 5:17
Avatar
Jan Hnilica
Člen
Avatar
Jan Hnilica:17.8.2021 12:09

čau, nemůžu rozběhat ty striktní typy, háže mi to error:
Fatal error: strict_types declaration must be the very first statement in the script in…
přitom ten řádek s deklarací strict types mám jako první. Na nějakém fóru jsem se dočetl, že důvodem může být kódování a že je potřeba skript uložit v kódování ANSI, ale to nepomohlo... nevíte někdo?

 
Odpovědět
17.8.2021 12:09
Avatar
Odpovídá na Jan Hnilica
Tomáš Teplík:9. ledna 13:01

Ahoj,
no zkus to dát hned za začátek php <?php declare(stric­t_types=1);
Nejsem si ale jistý, jestli je to třeba u vyšších verzí php. I bez tohoto mi hlásí php chybu ;-)
no Fatal error: Uncaught TypeError: secti(): Argument #2 ($b) must be of type float, string given

 
Odpovědět
9. ledna 13:01
Avatar
Ferda Mravenec:15. února 18:19

přijde mi trochu matoucí, používat v příkladech stejné názvy proměnných uvnitř funkce a v dalším zpracování výsledku, viz např zde ($soucet)

<?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));

?>

udělal bych změnu názvu proměnné třeba ve funkci, např. takto:

<?php

function secti($a, $b)
{
    $vysledek = $a + $b;
    return $vysledek;
}

$soucet = secti(10, 20);
echo("Součet: $soucet <br />");
echo("Dvojnásobek součtu: " . ($soucet * 2));

?>

připdadá mi to tak přehlednější a srozumitelnější, pro úplné začátečníky určitě

Editováno 15. února 18:21
 
Odpovědět
15. února 18:19
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 20. Zobrazit vše