Hlavní navigace

Zaostřeno na PHP (po roce) 3

9. 9. 2003
Doba čtení: 4 minuty

Sdílet

Dokončení prohlídky vlastností připravovaného jazyka PHP 5.

Statické metody

S metodami v PHP 4 byl tak trochu problém. Nerozlišovalo se volání metody objektu od statického volání. PHP 5 zavádí po vzoru ostatních objektově orientovaných jazyků klíčové slovo static. Jeho užitečnost je ale v mých očích prozatím docela sporná (možná se do finální verze ještě leccos změní).

Použití nebo vynechání slova static v záhlaví metody vás nijak neomezuje ve volání metody oběma způsoby. Asi jediná výhoda spočívá v tom, že vás interpret PHP při pokusu o použití pseudo-proměnné $this uvnitř statické metody upozorní chybovou hláškou.

Nechci spekulovat, ale jistě by se dalo zařídit, aby PHP varovalo programátora i při statickém volání nestatické metody nebo nestatickém volání statické metody…

Příklad:

<?php
class NejakaTrida {
  public static function statickaFunkce() {
    ...
  }
  ...
}

NejakaTrida::statickaFunkce(); // statické volání
?>

Metody __get(), __set() a __call()

Další výbornou vlastnost jazyka PHP přináší trojice vyhrazených metod tříd z nadpisu tohoto odstavce. Pomocí nich můžete definovat akce spouštěné automaticky při přístupu k atributům nebo metodám objektu.

Vy, kteří už nějaké zkušenosti s objektově orientovaným programováním máte, jistě znáte ono dilema, zda k nějaké hodnotě objektu přistupovat přímo, nebo ji zapouzdřit do dvojice metod typu setHodnota($hod­nota) a getHodnota().

Od verze 5 alespoň v PHP tento „problém“ řešit nemusíte. V každé třídě můžete definovat metody __get() a __set() určené k tomu, aby obsluhovaly přístup k neznámým atributům. Funguje to asi takhle. Napíšete-li v programu příkaz:

$objekt->x = 3.8;

a proměnná x v definici třídy objektu $objekt neexistuje, pokusí se místo toho PHP vykonat příkaz

$objekt->__set('x', 3.8);

Vtip je v tom, že záleží jen a jen na vás, jak metodu __set() implementujete.

K doplňkovému účelu slouží i metoda __get() – PHP ji volá pro vrácení hodnoty nedefinovaného atributu.

Příklad:

<?php
class UpovidanaTrida {
    public $n;
    private $x = array("a" => 1, "b" => 2, "c" => 3);

    function __get($nazev) {
        echo "Pokus o získání '$nazev'\n";

        if (isset($this->x[$nazev])) {
            $hodnota = $this->x[$nazev];
            echo "Vracím: $hodnota\n";
            return $hodnota;
        } else {
            echo "Nelze!\n";
        }
    }

    function __set($nazev, $hodnota) {
        echo "Pokus o nastavení '$nazev' na $hodnota\n";

        if (isset($this->x[$nazev])) {
            $this->x[$nazev] = $hodnota;
            echo "OK!\n";
        } else {
            echo "Nelze!\n";
        }
    }
}

$objekt = new UpovidanaTrida();

echo "n:\n";
$objekt->n = 1;

echo "\na:\n";
$objekt->a += $objekt->b;

echo "\nz:\n";
$objekt->z++;

echo "\n";
var_dump($objekt);
?>

Na výstupu by se mělo objevit:

n:

a:
Pokus o získání 'b'
Vracím: 2
Pokus o získání 'a'
Vracím: 1
Pokus o nastavení 'a' na 3
OK!

z:
Pokus o získání 'z'
Nelze!
Pokus o nastavení 'z' na 1
Nelze!

object(upovidanatrida)#1 (2) {
  ["n"]=>
  int(1)
  [""]=>
  array(3) {
    ["a"]=>

    int(3)
    ["b"]=>
    int(2)
    ["c"]=>
    int(3)
  }
}

Vidíme, že v případě atributu $n se metody __set() a__get() nevolají, protože jde o atribut, který byl ve třídě náležitě definován. Rovněž si můžeme všimnout, že atributy lze používat nadále tak, jak jsme zvyklí, a že i složité výrazy PHP dekóduje do správné posloupnosti volání __set() a __get().

Dlužím vám ještě ukázat, k čemu je metoda __call(). Půjdeme rovnou k příkladu:

<?php
class DalsiUpovidanaTrida {
    function __call($nazev, $parametry) {
        echo "Volám $nazev:\n";
        var_dump($parametry);
        return 1;
    }

    function jarmilo($x) {
      return $x + 1;
    }
}

$objekt = new DalsiUpovidanaTrida();

$a = $objekt->hynku(1, '2', 3.4, true);
var_dump($a);
echo "\n";

$a = $objekt->vileme(3, 2, 1, 'start');
var_dump($a);
echo "\n";

$a = $objekt->jarmilo(3.1);
var_dump($a);
?>

Výstup:

Volám hynku:
array(4) {
  [0]=>

  int(1)
  [1]=>
  string(1) "2"
  [2]=>
  float(3.4)
  [3]=>
  bool(true)
}
int(1)

Volám vileme:
array(4) {
  [0]=>
  int(3)
  [1]=>
  int(2)
  [2]=>

  int(1)
  [3]=>
  string(5) "start"
}
int(1)

float(4.1)

Jak jste si asi všimli, metoda __call() má něco do činění s nedefinovanými metodami. Volání $objekt->hynku() a$objekt->vileme() vedlo k vykonání metody __call(), protože PHP nemohlo v definici třídy DalsiUpovidanaTrida nalézt jejich implementaci. V případě volání metody jarmilo() už PHP do metody __call() nezabruslilo.

__autoload()

Mechanismus __autoload() je spíše už jen kosmetickou úpravou nové syntaxe jazyka; onou často vzpomínanou třešinkou na dortu nebo gramem ptačího trusu na hnojišti velkokapacitního kravína. Umožňuje vykonat určitý kód v okamžiku instanciace nedeklarované třídy. Jako jediný argument se funkci předává název třídy.

Typické využití ukazuje následující příklad:

<
function __autoload($jmenoTridy) {
  include_once('moje_knihovna_trid/' . $jmenoTridy . '.php');
}

V souvislosti s tímto „vylepšením“ si ale dovedu představit určitý problém: dá se předpokládat, že __autoload() budou používat zejména knihovny tříd. Co když ale programátor bude chtít použít knihovny dvě, přičemž každá z nich si ponese svůj __autoload()? Inu, uvidíme, jak se s tím poperou…

root_podpora

Tímto náš obnovený seriál o PHP verze 5 končí. PHP přináší mnoho změn. Pro někoho to asi přinese řadu problémů s kompatibilitou se stávajícím kódem, ale to je bohužel nutné zlo na cestě vývojem, které pro dlouholeté uživatele tohoto skriptovacího jazyka jistě není novinkou. Nicméně objekty ve čtyřkové verzi byly opravdu hrozné (narážím zejména na přiřazování hodnotou) a zasloužily předělat.

PS: Pokud se v budoucnosti v syntaxi jazyka objeví nějaká další vylepšení, určitě se o nich na Rootu dočtete.

Byl pro vás článek přínosný?

Autor článku

Michal Burda vystudoval informatiku a aplikovanou matematiku a nyní pracuje na Ostravské univerzitě jako odborný asistent. Zajímá se o data mining, Javu a Linux.