Pri používaní konštánt v PHP je potrebné mať na pamäti dve záludnosti.
Konštantu možno v PHP definovať buď staticky priamo v triede (najlepšie riešenie), napríklad takto:
class Region{public const PREFIX = 420;}
A použitie je celkom jasné. V čase kompilácie triedy je hodnota konštanty určená a môžeme k nej pristupovať volaním názvu triedy a samotnej konštanty. Najčastejšie zápisom Region::PREFIX
.
Druhý (oveľa horší spôsob) je definovať konštantu dynamicky za behu (najčastejšie niekde v konfiguračnom skripte), kde je potom niečo ako:
define('BASE_DIR', __DIR__ . '/../');
Hlavnou nevýhodou definovania konštanty pomocou funkcie define
je, že skript, ktorý definuje konštantu, nemusí byť zavolaný, takže konštanta nebude existovať, keď sa ju pokúsite prečítať.
V kombinácii s použitím dynamickej konštanty v rámci definície statickej konštanty v triede to môže dokonca viesť k fatálnej chybe reflexie:
class InvoiceGenerator{// To je úplne nesprávne!public const DATA_DIR = BASE_DIR . '/data/invoice';}
Vysvetlenie:
Použitie dynamickej konštanty v rámci statickej konštanty má tú hlavnú nevýhodu, že hodnotu dynamickej konštanty nemožno prečítať v čase kompilácie. Tento skript sa preto musí pri každej požiadavke spracovať znova (t. j. nemôže sa uložiť do vyrovnávacej pamäte OPCache kvôli optimalizácii rýchlosti), a ak konštanta vôbec neexistovala, vyhodí sa fatálna chyba kompilácie a aplikácia sa vôbec nemôže spustiť.
Ak používate program PhpStan, môže vás na tento problém automaticky upozorniť:
Reflection error: Could not locate constant "BASE_DIR" while evaluating expression in InvoiceGenerator at line 6
Učenie:
Hodnota všetkých konštánt by mala byť vždy konštantná.
V niektorých prípadoch má zmysel použiť dedičnosť na prepísanie hodnoty konštanty. V takom prípade však predok nemôže (alebo by nemal) čítať hodnotu z potomka.
Príkladom je definovanie krajín a regiónov:
abstract class Region{public function getPrefix(): int{// Osudová chyba!return static::REGION;}}final class CzechRepublic extends Region{public const REGION = 420;}
Paradoxom je, že uvedený kód nemusí nutne vyhodiť chybu, ale môže byť vyhodený nevhodným použitím dedičnosti.
Ak zavoláme metódu getPrefix()
na potomkovi CzechRepublic
, všetko bude správne, pretože hodnota konštanty bude načítaná správne. Ak by však potomok nenastavil hodnotu konštanty, vyhodila by sa fatálna chyba neexistujúcej konštanty. Najhoršie na celej veci je, že ide o skrytú závislosť, ktorá sa vytvára v implementácii metódy, a vývojár, ktorý triedu zdedí, o tejto závislosti nemusí ani vedieť.
Najlepším riešením v tomto prípade je buď definovať konštantu priamo v predkovi s predvolenou hodnotou (aby logika vždy prešla), alebo aspoň vyhodiť výnimku v getteri.
abstract class Region{public const REGION = null;public function getPrefix(): int{if (static::REGION === null) {throw new \LogicException("Región nebol definovaný.);}return static::REGION;}}final class CzechRepublic extends Region{public const REGION = 420;}
PhpStan reaguje na túto chybu takto:
Access to undefined constant static(Region):REGION.
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu:
Články píše Jan Barášek © 2009-2024 | Kontakt | Mapa webu
Status | Aktualizováno: ... | sk