Przy używaniu stałych w PHP należy pamiętać o dwóch ważnych rzeczach.
Stałą można zdefiniować w PHP albo statycznie bezpośrednio w klasie (najlepsze rozwiązanie), na przykład w następujący sposób:
class Region{public const PREFIX = 420;}
A sposób wykorzystania jest dość jasny. W czasie kompilacji klasy wartość stałej jest ustalona i można się do niej dostać, wywołując nazwę klasy i samą stałą. Najczęściej przez napisanie Region::PREFIX
.
Innym (znacznie gorszym) sposobem jest zdefiniowanie stałej dynamicznie w czasie uruchamiania (najczęściej gdzieś w skrypcie konfiguracyjnym), gdzie jest to coś w rodzaju
define('BASE_DIR', __DIR__ . '/../');
Główną wadą definiowania stałej za pomocą funkcji define
jest to, że skrypt definiujący stałą mógł nie zostać wywołany, więc stała nie będzie istniała przy próbie jej odczytania.
W połączeniu z użyciem stałej dynamicznej w definicji stałej statycznej w klasie, może to nawet spowodować błąd krytyczny:
class InvoiceGenerator{// To jest całkowicie błędne!public const DATA_DIR = BASE_DIR . '/dane/faktura';}
Wyjaśnienie:
Użycie stałej dynamicznej wewnątrz stałej statycznej ma tę poważną wadę, że wartości stałej dynamicznej nie można odczytać w czasie kompilacji. Skrypt ten musi być zatem przetwarzany ponownie w każdym żądaniu (tzn. nie może być przechowywany w pamięci podręcznej OPCache w celu optymalizacji szybkości), a jeśli stała w ogóle nie istniała, wyrzucany jest fatalny błąd czasu kompilacji i aplikacja nie może w ogóle działać.
Jeśli używasz programu PhpStan, może on automatycznie ostrzec Cię o tym problemie:
Reflection error: Could not locate constant "BASE_DIR" whileevaluating expression in InvoiceGenerator at line 6
Kształcenie:
Wartość wszystkich stałych powinna być zawsze stała.
W niektórych przypadkach sensowne jest użycie dziedziczenia w celu nadpisania wartości stałej. Jednak w takim przypadku przodek nie może odczytać wartości od potomka (a przynajmniej nie powinien).
Przykładem może być definiowanie krajów i regionów:
abstract class Region{public function getPrefix(): int{// Fatalny błąd!return static::REGION;}}final class CzechRepublic extends Region{public const REGION = 420;}
Paradoks polega na tym, że powyższy kod nie musi wyrzucać błędu, ale może zostać wyrzucony przez niewłaściwe użycie dziedziczenia.
Jeśli wywołamy metodę getPrefix()
na potomku CzechRepublic
, wszystko będzie w porządku, ponieważ wartość stałej zostanie poprawnie odczytana. Jeśli jednak potomek nie ustawi wartości stałej, zostanie wyrzucony błąd fatalny nieistniejącej stałej. Najgorsze w tym wszystkim jest to, że jest to ukryta zależność, która jest tworzona w implementacji metody, a programista dziedziczący klasę może nawet nie wiedzieć o tej zależności.
Najlepszym rozwiązaniem w tym przypadku jest albo zdefiniowanie stałej bezpośrednio w przodku z wartością domyślną (tak aby logika zawsze działała), albo przynajmniej rzucenie wyjątku w getterze.
abstract class Region{public const REGION = null;public function getPrefix(): int{if (static::REGION === null) {throw new \LogicException('Region nie został określony.');}return static::REGION;}}final class CzechRepublic extends Region{public const REGION = 420;}
PhpStan odpowiada na ten błąd w następujący sposób:
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: ... | pl