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 9 - Statika v PHP

V předešlém cvičení, Řešené úlohy k 7.-8. lekci OOP v PHP, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

V dnešním PHP tutoriálu se seznámíme s velmi kontroverzní technikou - statikou.

Pozor na statiku - Objektově orientované programování (OOP) v PHPPOZOR! Dnešní lekce vám ukáže statiku, tedy postupy, které v podstatě narušují objektový model. OOP je obsahuje jen pro speciální případy a obecně platí, že vše jde napsat bez statiky. Vždy musíme pečlivě zvážit, zda statiku opravdu nutně potřebujeme. Obecně bych doporučoval statiku vůbec nepoužívat, pokud si nejste naprosto jisti, co děláte. Podobně, jako globální proměnné je statika v objektovém programování něco, co umožňuje psát špatný kód a porušovat dobré praktiky. Znalosti použijte s rozvahou, na světe bude potom méně zla.

Třídní prvky

V objektově orientovaném programování jsme již trochu sběhlí a proto víme, že aplikace je složená z množiny komunikujících objektů. Objekty jsou vždy instance, vytvořené podle nějaké třídy. Každá instance je unikátní a má svůj vnitřní stav (atributy) a schopnosti (metody).

Objektově orientované programování však kromě instančních prvků umožňuje definovat i prvky třídní. Jedná se o atributy a metody, které nepatří instanci, ale patří třídě. Třídní metodu tedy voláme na třídě, místo na instanci a instanci vůbec nepotřebujeme. Takovou metodu označíme klíčovým slovem static a můžeme ji zavolat odkudkoli z programu aniž bychom měli instanci. Zkusme si to na jednoduchém příkladu.

Matematická třída

Vytvořme si ve složce tridy/ novou třídu Matematika.php. Do ní nejprve umístíme instanční metodu naDruhou(), jak jsme byli zvyklí doposud:

class Matematika
{

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

}

V souboru index.php si zkusme nechat vypočítat druhou mocninu pomocí naší metody. Vytvoříme si instanci naší třídy Matematika a zavoláme na ní příslušnou metodu:

require_once('tridy/Matematika.php');

$matematika = new Matematika();
echo($matematika->naDruhou(12));
class Matematika
{

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

}

Výstupem programu je samozřejmě číslo 144, ale o ten tu teď nejde. Představte si, že v nějaké výpočetní aplikaci používáte na mnoha místech v mnoha třídách nějaké podobné pomocné výpočty. Bylo by poněkud nepraktické stále tvořit nové instance nebo si předávat instanci třídy Matematika. Z toho důvodu uděláme metodu naDruhou() statickou. Upravme třídu Matematika na tuto podobu:

class Matematika
{

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

}

V souboru index.php odstraníme vytvoření instance a metodu naDruhou() budeme volat přímo na třídě Matematika. K volání třídních prvků nepoužíváme operátor šipky (->), ale čtyřtečku (::):

require_once('tridy/Matematika.php');

echo(Matematika::naDruhou(12));
class Matematika
{

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

}

Metodu jsme zavolali bez instance. Dá se říci, že je také globální a viditelná odkudkoli, což může při špatném použití způsobit velmi špatnou čitelnost kódu (viz dále). Víme, že OOP se snaží spíše co nejvíce věcí skrývat. V tomto případě je to však v pořádku a dále v seriálu si vysvětlíme další správná i špatná užití.

Kombinace statických a instančních prvků

Třída nemusí obsahovat jen instanční nebo jen statické prvky, ale může obsahovat jejich kombinaci. Instanční prvky jsou poté přístupné jen na instanci, statické zase jen na třídě.

Číslování instancí

O statických prvcích můžeme zjednodušeně říci, že jsou společné pro všechny instance, jelikož patří té jedné třídě. Vraťme se zas k našim lidem a dejme tomu, že je budeme potřebovat číslovat. Každý člověk bude mít atribut $id, ve kterém bude unikátní číslo. První vytvořený člověk bude mít $id=1, druhý $id=2 a tak dále. Kde ale uchovávat kolik lidí jsme již vytvořili?

Pokud známe statiku, není nic jednoduššího než přidat do třídy Clovek statický atribut pocetLidi, nastavený na 0. Třída si tedy pamatuje kolik lidí bylo vytvořeno. Je to i logicky správně, jelikož atribut je sdílený mezi všemi instancemi a proto je na třídě. V konstruktoru člověka poté tento statický atribut vždy zvýšíme o 1 a do instanční proměnné $id dosadíme toto číslo.

Třída Clovek bude vypadat s touto malou úpravou následovně:

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

Při bližším pohledu vidíme 2 nové atributy, instanční $id a třídní $pocetLidi.

V konstruktoru inkrementujeme (zvýšíme o 1) proměnnou $pocetLidi a hodnotu dosadíme nové instanci do jejího $id. Ke statickým prvkům se uvnitř třídy přistupuje pomocí klíčového slova self. Má podobný význam jako $this, které uchovávalo aktuální instanci, klíčové slovo self uchovává aktuální třídu. Matoucí může být, že se za čtyřtečku na rozdíl od $this píše dolar $.

Poslední změna spočívá v metodě __toString(), kde vracíme i ID, abychom jednoduše viděli, že nám jejich přidělování funguje.

V souboru index.php poupravíme cyklus tak, aby místo zdravení lidi vypisoval. Pro úplnost dodávám i jejich tvoření:

require_once('tridy/Clovek.php');
require_once('tridy/Javista.php');

$lide = array();
$lide[] = new Clovek('Karel', 'Novák', 30);
$lide[] = new Javista('Jan', 'Nový', 24, 'Eclipse');
$lide[] = new Clovek('Josef', 'Nový', 50);
$lide[] = new Javista('Tomáš', 'Marný', 28, 'NetBeans');
$lide[] = new Clovek('Marie', 'Nová', 32);

foreach ($lide as $clovek) {
    echo($clovek . '<br />');
}
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)
    {
        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;
    }

}
class Javista extends Clovek
{

    public function __construct(string $jmeno, string $prijmeni, int $vek, public string $ide)
    {
        parent::__construct($jmeno, $prijmeni, $vek);
    }

    public function programuj(): void
    {
        echo("Programuji v {$this->ide}...");
    }

    public function pozdrav(): void
    {
        echo('Hello world! Jsem ' . $this->jmeno);
    }

}

Výstup programu:

Tvoje stránka
localhost

Vidíme, že statický atribut $pocetLidi opravdu náleží třídě Clovek. Když PHP třídu poprvé načte, vytvoří atribut a nastaví do něj hodnotu 0. Statický atribut na třídě Clovek v paměti přetrvává do skončení skriptu bez ohledu na to, zda existují nějaké instance lidí nebo ne. S jeho hodnotou můžeme manipulovat z instančních metod (zde z konstruktoru). Ze statické metody nelze volat metoda instanční, jelikož k tomu potřebujeme instanci :) Naopak to tedy nelze.

Bez statiky by se tento příklad řešil pomocí objektu, který by lidi vyráběl. Takovým objektům se říká továrny, ale o tom zas někdy jindy.

Statika je velmi důležitá, velmi používaná a často velmi mylně chápaná programátorská technika. Z toho důvodu ji věnujeme více času. Řekneme si kdy ji používat a kdy nikoli. Budete na ni narážet často a je velmi důležité, abyste chápali jak funguje. Soubory z dnešní lekce jsou jako vždy ke stažení v archivu níže.

V příští lekci, Třídní prvky v PHP podruhé - konstanty, si vysvětlíme jak se v PHP používají konstanty.


 

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

 

Předchozí článek
Řešené úlohy k 7.-8. lekci OOP v PHP
Všechny články v sekci
Objektově orientované programování (OOP) v PHP
Přeskočit článek
(nedoporučujeme)
Třídní prvky v PHP podruhé - konstanty
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
149 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