Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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í.

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:

$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ů.');

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

}
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:

Validace hesla
localhost

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:

require_once('tridy/Matematika.php');

$polomer = 5;
$obsah = Matematika::PI * Matematika::naDruhou($polomer);
echo("Obsah kruhu je $obsah cm<sup>2</sup>.");
class Matematika
{

    const PI = 3.14159265358979;

    public static function naDruhou(float $zaklad) : float
    {
        return $zaklad * $zaklad;
    }

}

Výsledek:

Konstanty
localhost

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 851x (5.23 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

Předchozí článek
Statika v PHP
Všechny články v sekci
Objektově orientované programování (OOP) v PHP
Přeskočit článek
(nedoporučujeme)
Statika v PHP do třetice
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
141 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 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