Lekce 10 - Třídní prvky v PHP podruhé - konstanty
V minulé lekci, Statika v PHP, jsme si uvedli statiku a ukázali dvě její praktická využití.
Dnes si řekneme něco více o třídních prvcích a uvedeme si konstanty.
Ověření hesla
Rozšiřme ještě náš příklad z minula. Dejme tomu, že se naši lidé registrují do nějakého systému. Kromě svého jména zadávají ještě heslo. Než člověka vytvoříme, je nutné heslo ověřit, zda není moc krátké.
Metoda k ověření délky hesla k člověku logicky patří. Ve chvíli, kdy
heslo ověřujeme (např. z formuláře) ale ještě nemáme člověka
vytvořeného. Abychom ho vytvořili, potřebujeme znát jméno, příjmení,
věk a heslo, jelikož člověk je vyžaduje v konstruktoru. Abychom však
zavolali metodu pro ověření hesla, potřebujeme mít vytvořenou instanci
člověka. Problém vyřešíme tak, že metodu k ověření hesla napíšeme
jako statickou. Tak ji budeme moci zavolat i bez instance a zároveň bude
logicky zařazena ve třídě Clovek
, kam patří.
Ověření je značně zjednodušené, v reálu by se ověřovalo zda heslo
není stejné jako jméno a podobně. Třídě Clovek
přidáme
privátní instanční proměnnou $heslo
. Proč je atribut
$heslo
privátní snad nemusím vysvětlovat.
Dále upravíme konstruktor tak, aby se uživatel mohl vytvořit jen se zadaným heslem:
public function __construct(public string $jmeno, public string $prijmeni, public int $vek, private string $heslo) { self::$pocetLidi++; $this->id = self::$pocetLidi; }
Podobně upravíme i konstruktor javisty z minulých lekcí, aby se nám nerozbil, ještě ho budeme potřebovat:
public function __construct(string $jmeno, string $prijmeni, int $vek, string $heslo, public string $ide) { parent::__construct($jmeno, $prijmeni, $vek, $heslo); }
Heslo by se mělo nějaký způsobem hashovat, nyní to pro jednoduchost
zanedbáme, podrobněji se to řeší v dalších seriálech o bezpečnosti. Třídě nakonec přidejme statickou
veřejnou metodu validniHeslo()
:
public static function validniHeslo(string $heslo): bool { return (mb_strlen($heslo) >= 5); }
Metoda vrátí hodnotu true
pokud je délka hesla větší než
5 znaků, jinak vrátí hodnotu false
. Až na použití funkce
mb_strlen()
v ní není nic zajímavého. Asi víte, že funkce
mb_strlen()
umí na rozdíl od zastaralé funkce
strlen()
pracovat s kódováním UTF-8
. Kódování
pro tyto funkce však musíme v indexu nastavit. Někam na začátek souboru
index.php
tedy přidejme:
mb_internal_encoding("UTF-8");
Více informací o funkci naleznete v českém PHP manuálu.
V indexu si zkusíme jednoduše realizovat scénář registrace nového člověka do systému:
{PHP} require_once('tridy/Clovek.php'); require_once('tridy/Javista.php'); mb_internal_encoding("UTF-8"); $heslo = 'heslojeveslo'; if (Clovek::validniHeslo($heslo)) { $clovek = new Clovek('Jan', 'Novák', 32, $heslo); echo('Heslo OK'); } else echo('Heslo musí být dlouhé minimálně 5 znaků.');
{PHP} class Clovek { private int $unava = 0; public int $id; private static int $pocetLidi = 0; public function __construct(public string $jmeno, public string $prijmeni, public int $vek, private string $heslo) { self::$pocetLidi++; $this->id = self::$pocetLidi; } public function spi(int $doba): void { $this->unava -= $doba * 10; if ($this->unava < 0) $this->unava = 0; } public function behej(int $vzdalenost): void { if ($this->unava + $vzdalenost <= 20) $this->unava += $vzdalenost; else echo('Jsem příliš unavený.'); } public function pozdrav(): void { echo('Ahoj, já jsem ' . $this->jmeno); } protected function celeJmeno(): string { return $this->jmeno . ' ' . $this->prijmeni; } public function __toString(): string { return $this->jmeno . ' - ' . $this->id; } public static function validniHeslo(string $heslo): bool { return (mb_strlen($heslo) >= 5); } }
{PHP} class Javista extends Clovek { public function __construct(string $jmeno, string $prijmeni, int $vek, string $heslo, public string $ide) { parent::__construct($jmeno, $prijmeni, $vek, $heslo); } public function programuj(): void { echo("Programuji v {$this->ide}..."); } public function pozdrav(): void { echo('Hello world! Jsem ' . $this->jmeno); } }
Výstup:
Validační metodu je možné volat bez instance, což se v naší situaci
hodí. Tak samo voláme i atribut MINIMALNI_DELKA_HESLA
.
Konstanty
Mezi třídní prvky patří také konstanty. Jak jistě z matematiky známe,
jedná se o nějakou neměnnou hodnotu. Konstanty jsou vždy statické, ačkoli
se před ně modifikátor static
nepíše. Definují se klíčovým
slovem const
a patří vždy nějaké třídě. Jejich název se
píše velkými písmeny, místo mezer se používají podtržítka. Píší se
jako první ve třídě, ještě před atributy.
Konstanta Pí
Přidejme si konstantu PI do naší třídy Matematika.php
z
minula:
class Matematika { const PI = 3.14159265358979; public static function naDruhou(float $zaklad) : float { return $zaklad * $zaklad; } }
V souboru index.php
si ji zkusme použít:
{PHP} require_once('tridy/Matematika.php'); $polomer = 5; $obsah = Matematika::PI * Matematika::naDruhou($polomer); echo("Obsah kruhu je $obsah cm<sup>2</sup>.");
{PHP} class Matematika { const PI = 3.14159265358979; public static function naDruhou(float $zaklad) : float { return $zaklad * $zaklad; } }
Výsledek:
Konstanta minimální délky hesla
Konstanta se nám hned hodí pro minimální délku hesla. Když se ji někdy
rozhodneme změnit nebo ji budeme chtít použít na více místech, bude
přehledně definována jednou na začátku třídy. Přidejme k naší třídě
Clovek.php
konstantu MINIMALNI_DELKA_HESLA
nastavenou
na hodnotu 5:
const MINIMALNI_DELKA_HESLA = 5;
Nyní nahradíme pětku v metodě validniHeslo()
za tuto
konstantu. Jelikož se jedná o třídní prvek, použijeme k přístupu ke
konstantě zevnitř třídy klíčové slovo self
:
public static function validniHeslo(string $heslo) : bool { return (mb_strlen($heslo) >= self::MINIMALNI_DELKA_HESLA); }
Program funguje jako dříve a důležitá konstanta je přehledně na začátku třídy. Ke konstantě můžeme samozřejmě opět přistupovat i mimo třídu:
echo('Vítejte v registraci, zvolte si uživatelské jméno a heslo o minimální délce ' . Clovek::MINIMALNI_DELKA_HESLA);
Konstanty bychom měli využívat ve svých aplikací pokaždé, když definujeme nějakou neměnnou hodnotu. Často je využívám pro různé limity, např. maximální počet událostí na člena:
const MAXIMUM_UDALOSTI = 50;
Když už nyní víme, že něco takového jde, upravíme si v souboru
index.php
funkci echo()
tak, jak by měla být
správně:
if (Clovek::validniHeslo($heslo)) { $clovek = new Clovek('Jan', 'Novák', 32, $heslo); echo('Heslo OK'); } else echo('Heslo musí být dlouhé minimálně ' . Clovek::MINIMALNI_DELKA_HESLA . ' znaků.');
Výčtové typy (ENUM) v PHP
Konstanty se v PHP často využívaly pro nahrazení tzv. výčtových typů, které PHP až do verze PHP 8.1 neobsahovalo. Sice se daly nahradit pomocí tříd, ale používání konstant se uchytilo více, protože bylo jednodušší. V PHP 8.1 byly výčtové typy přidány a proto si je zde ukážeme. Nicméně na konstanty používané namísto výčtových typů budete narážet velice často, a tak si zde ukážeme využití obou dvou možností.
Když máme v aplikaci proměnnou, která nabývá vždy jedné hodnoty z
určitého výčtu hodnot, použijeme právě enum
, nebo (máme-li
k dispozici pouze starší verze PHP) konstanty.
Typ zprávy
Ukažme si využití výčtových typů na reálném příkladu. Budeme uchovávat zprávu pro uživatele, kterou vygeneroval náš redakční systém. Zpráva může být buď informační (modrá s ikonkou i), chybová (červená s ikonkou křížku) nebo úspěšná (zelená s fajfkou).
K reprezentaci zprávy můžeme použít obyčejné asociativní pole, kde v klíči bude typ zprávy a v hodnotě její text. Bez znalostí konstant bychom asi použili k uchování typu textový řetězec:
$zprava = array( 'typ' => 'uspech', 'nadpis' => 'A je to!' 'text' => 'Článek byl publikován.', );
Takovéto zprávě potom podle typu přiřadíme CSS třídu, můžeme je
podle typu řadit a podobně. Abychom nemuseli přemýšlet nad tím, zda jsme
zvolili hodnotu uspech
, uspesna
nebo ok
,
uložíme si povolené možnosti do výčtu nebo konstant.
Použití výčtového typu
Výčtové typy píšeme mimo třídy a obvykle i do zvláštních souborů, které se nazývají stejně. Autoloader zvládá poté takovéto výčtové typy načítat stejným způsobem, jakým načítá třídy:
enum TypZpravy { case Info; case Uspech; case Chyba; }
Hodnot uvozených klíčovým slovem case
můžeme mít ve
výčtu neomezeně. Jejich názvy však musí splňovat stejná pravidla, jako
jména tříd či proměnných. Není v nich možné použít například
diakritiku. V tomto případě nám to nevadí, jelikož hodnoty
Info
, Uspech
a Chyba
uživateli ukazovat
nebudeme. V případě, že by naše zprávy měly mít uživatelsky
přívětivý text v češtině, bude nám toto omezení vadit. Pro tento
případ existují tzv. Backed enumerations. Výčet nám umožňuje
každé z možností přiřadit nějakou hodnotu:
enum TypZpravy: string { case Info = 'Nová zpráva'; case Uspech = 'A je to!'; case Chyba = 'A jéje...'; }
Výčtový typ poté bude využívat například třída
SpravceZprav
, jejíž metoda pridejZpravu()
vytvoří
pole se zprávou, a to si uloží. Metoda vypadá následovně:
public function pridejZpravu(TypZpravy $typ, string $zprava) : void { $this->zpravy[] = array( 'typ' => $typ->name, // "Info", "Uspech" nebo "Chyba" 'nadpis' => $typ->value, // "Nová zpráva", "A je to!" nebo "A jéje..." 'text' => $zprava, ); }
Všimněte si, že v seznamu parametrů můžeme název výčtu specifikovat jako datový typ. Stejně tak, jako když chceme přijímat instanci nějaké třídy.
Metoda pridejZpravu()
se volá tímto způsobem:
$spravceZprav = new SpravceZprav(); $spravceZprav->pridejZpravu(TypZpravy::Uspech, 'Článek byl publikován.');
Použití konstant
Používáme-li konstanty, nepotřebujeme žádný soubor pro povolené
hodnoty. Jednoduše je napíšeme do vhodné třídy, která s nimi souvisí. V
našem případě do třídy SpravceZprav
:
/** * Správce zpráv */ class SpravceZprav { /** * Info */ const TYP_INFO = 'Info'; /** * Úspěch */ const TYP_USPECH = 'Uspech'; /** * Chyba */ const TYP_CHYBA = 'Chyba'; /** * Nadpisy zpráv */ const NADPISY_ZPRAV = array( 'Info' => 'Nová zpráva', 'Uspech' => 'A je to!', 'Chyba' => 'A jéje...', ); /** * Přidá zprávu pro uživatele * @param string $typ Typ zprávy, MUSÍ BÝT JEDNOU Z HODNOT KONSTANT TÉTO TŘÍDY * @param string $zprava Obsah zprávy */ public function pridejZpravu(string $typ, string $zprava): void { $this->zpravy[] = array( 'typ' => $typ, 'nadpis' => self::NADPISY_ZPRAV[$typ], 'text' => $zprava, ); } }
A ještě zavoláme příslušnou metodu:
$spravceZprav = new SpravceZprav(); $spravceZprav->pridejZpravu(SpravceZprav::TYP_USPECH, 'Článek byl publikován.');
Nevýhodou tohoto přístupu je, že nelze vynutit předání platné hodnoty
v prvním argumentu. Sice jsme si nad metodu pridejZpravu()
přidali poznámku, že první argument musí být jedna z konstant. Nicméně
pokud místo některé z nich předáme například hodnotu 5
, PHP
nás o problému neinformuje a můžou tak vznikat chyby. V případě použití
výčtového typu, PHP vynutí využití jedné ze specifikovaných hodnot pro
daný výčet.
Podobná třída je opravdu použita v seriálu o tvorbě MVC objektového redakčního systému v PHP k zobrazování
zpráv uživateli. Časem se tam jistě dostanete
Konstanty se dají definovat i mimo třídy, a to metodou
define()
. V novějších verzích PHP to lze i klíčovým slovem
const
, stejně jako ve třídách. Taková konstanta nepatřila
žádné třídě a trpěla nedostatky neobjektového programování, které
jsme si zmiňovali v začátcích seriálu.
Soubory z dnešní lekce jsou jako vždy ke stažení v archivu níže.
V příští lekci, Statika v PHP do třetice, si ukážeme využití statického databázového wrapperu, statického registru a pobavíme se teoreticky o správných a špatných využitích třídních prvků.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 877x (5.23 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP