Nový Model Objektov

V PHP 5 je nový Model Objektov. Zaobchádzanie s objektami v PHP bolo kompletne prepísané, umožňujúc tak lepší výkon a viac vlastností. V predošlej verzii PHP boli objekty chápané ako primitívne typy (napr. integere a stringy). Nevýhodou tejto metódy bolo, že sa celý objekt sémanticky skopíroval pri priradení premennej alebo sa metóde poslala ako parameter. Po novom sú objekty odkazované cez handle a nie hodnotu (handle si môžete predstaviť ako idetifikátor objektu).

Mnoho PHP programátorv sa neobáva problému kopírovania starého modelu objektov, a preto väčšina PHP aplikácii bude fungovať mimo počítača alebo len s málo modifikáciami.

Členovia Private a Protected

PHP 5 predstavuje členské premenné private a protected, umožňujú vám definovať viditeľnosť vlastností triedy.

Príklad B-4. Prístupnosť členov Private a Protected

K členskym premenným Protected sa dá pristupovať v triede rozširujúcej triedy, v ktorej sú deklarované, kdežto k členským premenným Private sa dá pristupovať iba cez triedu, ktorej patria.

<?php
class MyClass {
    
private $Hello = "Hello, World!\n";
    
protected $Bar = "Hello, Foo!\n";
    
protected $Foo = "Hello, Bar!\n";

    function
printHello() {
        print
"MyClass::printHello() " . $this->Hello;
        print
"MyClass::printHello() " . $this->Bar;
        print
"MyClass::printHello() " . $this->Foo;
    }
}

class
MyClass2 extends MyClass {
    
protected $Foo;
              
    function
printHello() {
        
MyClass::printHello();                          /* Malo by vypisat */
        
print "MyClass2::printHello() " . $this->Hello; /* Nemalo by vypisat nic */
        
print "MyClass2::printHello() " . $this->Bar;   /* Nemalo by vypisat (nedeklarovane)*/
        
print "MyClass2::printHello() " . $this->Foo;   /* Malo by vypisat */
    
}
}

$obj = new MyClass();
print
$obj->Hello;  /* Nemalo by vypisat nic */
print $obj->Bar;    /* Nemalo by vypisat nic */
print $obj->Foo;    /* Nemalo by vypisat nic */
$obj->printHello(); /* Malo by vypisat */

$obj = new MyClass2();
print
$obj->Hello;  /* Nemalo by vypisat nic */
print $obj->Bar;    /* Nemalo by vypisat nic */
print $obj->Foo;    /* Nemalo by vypisat nic */
$obj->printHello();
?>

Metódy Private a Protected

Spolu s PHP 5 boli predstavené aj metódy private a protected.

Príklad B-5. Príklad metódy Protected

<?php
class Foo {
    
private function aPrivateMethod() {
        echo
"Foo::aPrivateMethod() called.\n";
    }

    
protected function aProtectedMethod() {
        echo
"Foo::aProtectedMethod() called.\n";
        
$this->aPrivateMethod();
    }
}

class
Bar extends Foo {
    
public function aPublicMethod() {
        echo
"Bar::aPublicMethod() called.\n";
        
$this->aProtectedMethod();
    }
}

$o = new Bar;
$o->aPublicMethod();
?>

Starý kód, ktorý nemal žiadne užívateľom definované triedy alebo funkcie nazvané "public", "protected" alebo "private" by mal bežať bez modifikácii.

Triedy a Metódy Abstract

PHP 5 tiež predstavuje triedy a metódy abstract. Metóda abstract deklaruje iba signatúru metódy a neposkytuje implementáciu. Trieda, ktorá obsahuje metódy abstract musí byť deklarovaná ako abstract.

Príklad B-6. Príklad triedy Abstract

<?php
abstract
class AbstractClass {
    
abstract public function test();
}

class
ImplementedClass extends AbstractClass {
    
public function test() {
        echo
"ImplementedClass::test() volana.\n";
    }
}

$o = new ImplementedClass;
$o->test();
?>

Triedy Abstract nemôžu byť inštanciované. Starý kód, ktorý nemá žiadne užívateľom definované triedy alebo funkcie nazvané 'abstract' by mal bežať bez modifikácii.

Rozhrania

PHP 5 predstavuje rozhrania. Trieda môže implementovať ľubovoľný zoznam rozhraní.

Príklad B-7. Príklad rozhrania

<?php
interface Throwable
{
    
public function getMessage();
}

class
MyException implements Throwable {
    
public function getMessage() {
        
// ...
    
}
}
?>

Starý kód, ktorý nemá žiadne užívateľom definované triedy alebo funkcie nazvané 'interface'alebo 'implements' by mal bežať bez modifikácii.

Typové Hinty triedy

PHP 5, stále voľne typové, predstavuje schopnosť použiť Typové hinty triedy na deklarovanie očakávanej triedy objektov, ktoré sú poslané metóde ako parametre.

Príklad B-8. Príklad Typového hintu triedy

<?php
interface Foo
{
    function
a(Foo $foo);
}

interface Bar {
    function
b(Bar $bar);
}

class
FooBar implements Foo, Bar {
    function
a(Foo $foo) {
        
// ...
    
}

    function
b(Bar $bar) {
        
// ...
    
}
}

$a = new FooBar;
$b = new FooBar;

$a->a($b);
$a->b($b);
?>

Teto typové hinty triedy sa nekontrolujú počas kompilácie, ako by to bolo v prípade typových jazykov, ale počas runtime. To znamená, že:

<?php
function foo(ClassName $object) {
    
// ...
}
?>

je zhodné s:

<?php
function foo($object) {
    if (!(
$object instanceof ClassName)) {
        die(
"Argument 1 musi byt instanciou ClassName");
    }
}
?>

final

PHP 5 predstavuje kľúčové slovo "final" na deklarovanie členov a metód final. Metódy a členovia deklarovavý ako final sa nemôžu preťažiť sub-triedami.

Príklad B-9. Metóda final

<?php
class Foo {
    
final function bar() {
        
// ...
    
}
}
?>

Ba čo viac, je možné vytvoriť triedy final. Toto zabraňuje triede byť špecializovanou (nemôže byť zdedená inou triedou). Nie je nutné samotné metódy triedy final deklarovať ako final.

Príklad B-10. trieda final

<?php
final
class Foo {
    
// definicia triedy
}

// nasledujuci riadok nie je mozny
// class Bork extends Foo {}
?>

Vlastnosti nemôžu byť final-ne.

Starý kód, ktorý nemal žiadne užívateľom definované triedy alebo funkcie nazvané 'final' by mal bežať bez modifikácii.

Klonovanie objektov

PHP 4 neponúkalo žiadny spôsob, akým by užívateľ mohol rozhodnúť, ktorý kopírovací conštruktor spustiť pri zdvojení objketu. Počas duplikácie, PHP 4 kopírovalo bit po bite tvoriac tak identickú repliku všetkých vlastností objektu.

Tvorba kópie objektu s plne replikovaými vlastnosťami nie je vždy žiadaným správaním. Dobrým príkladom potreby kopírovaích konštruktorov je, ak máte objekt, ktorý reprezentuje GTK okno a objekt drží zdroj tohto GTK okna, keď vytvárate duplikát, môžete chcieť vytvoriť nové okno s rovnakými vlastnosťami a mať nový objekt, ktorý drží zdroj nového okna. Ďalším príkladom je, ak váš objekt drží odkaz na ďalší objekt, ktorý on používa a keď replikujete rodičovský objekt, chcete vytvoriť novú inštanciu tohto druhého objektu, takže replika má svoju vlastnú oddelenú kópiu.

Kópia objektu je vytvorená pomocou kľúčového slova clone (ktoré volá, ak je to možné, metódu __clone() objektu). Metóda __clone() objetku sa nedá volať priamo.

Keď developer žiada vytvorenie novej kópiu objektu, PHP 5 skontroluje, či bola metóda __clone() definovaná alebo nie. Ak nie, zavolá východziu __clone(), ktorá skopíruje všetky vlastnosti objektu. Ak bola metóda __clone() definované, potom bude zodpovedná za nastavenie nutných vlastností vo vytvorenom objekte. Engine vhodne dodá funkciu, ktorá importuje všetky vlastnosti zo zdrojového objektu, takže môžu začať s by-value replikou zdrojového objektu a prelomiť vlastnosti, ktoré sa musia zmeniť.

Príklad B-11. Klonovanie objektov

<?php
class MyCloneable {
    static
$id = 0;

    function
MyCloneable() {
        
$this->id = self::$id++;
    }

    function
__clone() {
        
$this->address = "New York";
        
$this->id = self::$id++;
    }
}

$obj = new MyCloneable();

$obj->name = "Hello";
$obj->address = "Tel-Aviv";

print
$obj->id . "\n";

$obj_cloned = clone $obj;

print
$obj_cloned->id . "\n";
print
$obj_cloned->name . "\n";
print
$obj_cloned->address . "\n";
?>

Konštruktory

PHP 5 umožňuje developerom deklarovať metódy konštruktora pre triedy. Triedy, ktoré majú metódu konštruktora, volajú túto metódu pri každom novo-vytvorenom objekte, takže je vhodná pre akúkoľvek incializáciu, ktorú objekt môže potrebovať pred tým, než je je použitý.

V PHP 4 metódy konštruktora boli metódy triedy, ktoré mali rovnaký názov ako trieda samotná. Nakoľko je veľmi bežné volať rodičovský konštruktor z odvodených tried, spôsob, ako PHP 4 fungovalo, bol troška krkolomý na presúvanie tried vo veľkej hierarchii tried. Ak sa trieda presunula pod iného rodiča, názov konštruktora toho rodiča sa zmení tiež a kód v odvodenej triede, ktorá volá rodičovského konštruktora, sa tiež zmenil.

PHP 5 predstavuje štandardný spôsob deklarovania metód konštruktora ich volaním pomocou názvu __construct().

Príklad B-12. Používanie novo unifikovaných konštruktorov

<?php
class BaseClass {
    function
__construct() {
        print
"Konstruktor v BaseClass\n";
    }
}

class
SubClass extends BaseClass {
    function
__construct() {
        
parent::__construct();
        print
"Konstruktor v SubClass\n";
    }
}

$obj = new BaseClass();
$obj = new SubClass();
?>

Kvôli spätnej kompatibilite, ak PHP 5 nedokáte nájsť funkciu __construct() pre danú triedu, vyhľadá funkciu konštruktora starého štýlu, podľa názvu triedy. Z hľadiska efektivity to znamená, že jediný prípad, ktorý by mal problémy kompatibility je, ak trieda nemala metódu nazvanú __construct(), čo bolo použité pre odlišnú sémantiku.

Deštruktory

Mať schopnosť definovať deštruktory pre objekty môže byť veľmi užitočné. Deštruktory dokážu sledovať správy pre ladenie, zatvárať databázové spojenia a robiť iné čistiace práce. V PHP 4 neexistoval žiadny mechanizmus pre deštruktory objektov, i keď PHP už malo podporu pre registrovanie funkcii, ktoré sa malo spustiť pri požadovanom vypnutí.

PHP 5 predstavuje koncept deštruktorov podobný tomu v iných objektovo orientovaných jazykoch, ako je Java: Ak sa zničí posledný odkaz na objekt, zavolá sa deštruktor objektu, ktorý je metódou triedy nazvanou __destruct(), ktorá neprijíma žiadne parametre, pred tým, než sa objekt uvoľní z pamäte.

Príklad B-13. Destructor

<?php
class MyDestructableClass {
    function
__construct() {
        print
"V konstruktore\n";
        
$this->name = "MyDestructableClass";
    }

    function
__destruct() {
        print
"Destrukcia " . $this->name . "\n";
    }
}

$obj = new MyDestructableClass();
?>

Podobne ako konštruktory, rodičovské deštruktory nebudú volané implicitne engine-om. Aby sa pustil rodičovský deštruktor, musí sa explicitne zavolať parent::__destruct() v tele deštruktora.

Konštanty

PHP 5 predstavujes triedne konštanty:

Príklad B-14. Príklad konštanty triedy

<?php
class Foo {
    const
constant = "constant";
}

echo
"Foo::constant = " . Foo::constant . "\n";
?>

Starý kód, ktorý nemá žiadne užívateľom definované triedy alebo fukcie nazvané 'const' bude bežať bez modifikácii.

Výnimky

PHP 4 nemalo žiadne riadenie výnimiek. PHP 5 predstavuje model výnimiek podobný tomu v iných programovacích jazykoch. Pozor na fakt, že existuje podpora pre "catch all", ale nie pre klazulu "finally".

Výnimky sa dajú viackrát thrown-úť v catch blokoch. Tiež je možné mať viacnásobné catch bloky. V takom prípade sa zachytená výnimka porovná s typom triedy každého catch bloku od hora na dol a prvý blok, ktorý sa zhoduje s 'instanceof', sa spracuje. Keď sa catch blok ukončí, spracovanie pokračuje na konci posledného catch bloku. Ak sa žiadny catch blok nezhoduje s 'instanceof', potom sa hľadá ďalší try/catch blok až pokiaľ ďalšie try/catch bloky nie sú dostupné. V takom prípade je výnimka nezachytenou výnimkou a program sa ukončí zobrazením výnimky.

Príklad B-15. Príklad tvorby výnimky

<?php
try
{
   
throw new Exception('Ahoj');
}
catch (Exception $exception) {
   echo
$exception;
}
?>

Starý kód, ktorý nemá žiadne užívateľom definované triedy alebo funkcie 'catch', 'throw' a 'try' bude bežať bez modifikácii.

Dereferencovanie objektov vratených z funkcií

V PHP 4 nebolo možné dereferencovať objekty vrátené funkciami a urobiť ďalšie volania metódy na tieto objekty. S PHP 5 je možné následujúce:

Príklad B-16. Príklad dereferencie

<?php
class Circle {
    function
draw() {
        print
"Kruh\n";
    }
}
      
class
Square {
    function
draw() {
        print
"Stvorec\n";
    }
}

function
ShapeFactoryMethod($shape) {
    switch (
$shape) {
        case
"Circle":
            return new
Circle();
        case
"Square":
            return new
Square();
    }
}

ShapeFactoryMethod("Circle")->draw();
ShapeFactoryMethod("Square")->draw();
?>

Inicializácia premenných statických členov

Premenné statických členov statických tried sa môžu teraz inicializovať.

Príklad B-17. Príklad inicializácie premennej Static

<?php
class foo {
    static
$my_static = 5;
    
public $my_prop = 'bla';
}

print
foo::$my_static;
$obj = new foo;
print
$obj->my_prop;
?>

Metódy Static

PHP 5 predstavuje kľúčové slovo 'static' na deklaráciu statický metód, teda volateľných mimo kontextu objektu.

Príklad B-18. Príklad metód Static

<?php
class Foo {
    
public static function aStaticMethod() {
        
// ...
    
}
}

Foo::aStaticMethod();
?>

Pseudo premenná $this nie je dostupná vo vnútri metódy, ktorá bola deklarovaná ako static.

instanceof

PHP 5 predstavuje kľúčové slovo instanceof, ktoré vám umožňuje zistiť, či objekt je alebo nie je inštanciou triedy, alebo či rozširuje triedu alebo implementuje rozhranie.

Príklad B-19. Príklad instanceof

<?php
class baseClass { }

$a = new baseClass;

if (
$a instanceof baseClass) {
    echo
"Hello World";
}
?>

Static function variables

Static-y sa teraz spracovávajú počas kompilácie, čo developer umožňuje priradiť premenné k static-om pomocou referencie. Táto zmena veľkolepo zlepšuje ich výkon, ale znamená, že nepriame odkazy na static-y nebude viac fungovať.

Parametre poslané referenciou

Parametre, ktoré sú poslané referenciou funkcii môžu teraz mať východzie hodnoty

Príklad B-20.

<?php
function my_function(&$var = null) {
    if (
$var === null) {
        die(
"$var musí mat hodnotu");
    }
}
?>

__autoload()

Zachytávacia funkcia __autoload() bude automaticky volaná, keď sa má nedeklarovaná trieda inštanciovať. Názov tejto triedy sa pošle zachytávacej funkcii __autoload() ako jej jediný argument.

Príklad B-21. Príklad __autoload()

<?php
function __autoload($className) {
    include_once
$className . ".php";
}

$object = new ClassName;
?>

Preťažiteľné volania metód a prístupy vlastností

Obe, volania metód a prístupy vlastností sa dajú preťažiť cez metódy __call(), __get() a __set().

Príklad B-22. __get() a __set()

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

    function
__get($nm) {
        print
"Ziskanie [$nm]\n";

        if (isset(
$this->x[$nm])) {
            
$r = $this->x[$nm];
            print
"Vratenie: $r\n";
            return
$r;
        } else {
            print
"Nic!\n";
        }
    }

    function
__set($nm, $val) {
        print
"Nastanie [$nm] na $val\n";

        if (isset(
$this->x[$nm])) {
            
$this->x[$nm] = $val;
            print
"OK!\n";
        } else {
            print
"Nie OK!\n";
        }
    }
}


$foo = new Setter();
$foo->n = 1;
$foo->a = 100;
$foo->a++;
$foo->z++;
var_dump($foo);
?>

Príklad B-23. Príklad __get()

<?php
class Caller {
    
private $x = array(1, 2, 3);

    function
__call($m, $a) {
        print
"Metoda $m volala:\n";
        
var_dump($a);
        return
$this->x;
    }
}

$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
?>

Iterácia

Objekty sa môžu iterovať spôsobom preťaženia, pri použití s foreach. Východzie správanie je iterovať cez všetky vlastnosti.

Príklad B-24. Príklad iterácie objektu

<?php
class Foo {
    
public $x = 1;
    
public $y = 2;
}

$obj = new Foo;

foreach (
$obj as $prp_name => $prop_value) {
    
// pomocou vlastnosti
}
?>

Každá trieda, ktorej inštancie sa dajú iterovať s foreach by mala implementovať prázdne rozhranie Traversable. Teda každý objekt, ktorý hovorí, že implementuje Traversable sa môže použiť s foreach.

Rozhrania IteratorAggregate a Iterator vám umožňujú určiť ako sa objekty triedy iterujú v PHP kóde. Prvý z nich má metódu getIterator(), ktorá musí vrátiť pole alebo objekt, ktorý buď implementuje rozhranie Iterator alebo je inštaniciovaný z internej triedy, ktorá môže byť iterovaná

Príklad B-25. Príklad tvorby Iterátora

<?php
class ObjectIterator implements Iterator {

    
private $obj;
    
private $num;

    function
__construct($obj) {
        
$this->obj = $obj;
    }
    function
rewind() {
        
$this->num = 0;
    }
    function
valid() {
        return
$this->num < $this->obj->max;
    }
    function
key() {
        return
$this->num;
    }
    function
current() {
        switch(
$this->num) {
            case
0: return "1.";
            case
1: return "2.";
            case
2: return "3.";
            default: return
$this->num.".";
        }
    }
    function
next() {
        
$this->num++;
    }
}

class
Object implements IteratorAggregate {

    
public $max = 3;

    function
getIterator() {
        return new
ObjectIterator($this);
    }
}

$obj = new Object;

// tento foreach ...
foreach($obj as $key => $val) {
    echo
"$key = $val\n";
}

// sa zhoduje s nasledujucimi 7 riadkami s direktivou for.
$it = $obj->getIterator();
for(
$it->rewind(); $it->valid(); $it->next()) {
    
$key = $it->current();
       
$val = $it->key();
          echo
"$key = $val\n";
}
unset(
$it);
?>

Konštanta __METHOD__

Nová pseudo konštanta __METHOD__ ukazuje aktuálnu triedu a metódu, keď sa použije vo vnútri metódy a funkciu, keď sa použije mimo triedu.

Príklad B-26. Príklad použitia __METHOD__

<?php
class Foo {
    function
show() {
        echo
__METHOD__;
    }
}

class
Bar extends Foo {
}

Foo::show(); // posiela na vystup Foo::show
Bar::show(); // posiela na vystup Foo::show pretoze __METHOD__ je
             // token vyhodnoteny pocas kompilacie

function test() {
    echo
__METHOD__;
}

test();      // posiela na vystup test
?>

Metóda __toString()

Nová zázračná metóda __toString() vám umožňuje preťažiť objekt na konverziu reťazca.

Príklad B-27. Príklad __toString()

<?php
class Foo {
    function
__toString() {
        return
"Cokolvek";
    }
}

$obj = new Foo;

echo
$obj; // volaj __toString()
?>

Reflexné API

PHP 5 prichádza s kompletnou API reflexiou, ktorá pridáva schopnosť tvorbu-opančných tried, rozhraní, funkcíí a metód ronvako ako rozšírení.

Reflexné API tiež ponúkajú získanie doc komentárov pre funkcie, triedy a metódy.

Takmer všetky aspekty objektovo orientovaného kódu sa dajú reflektovať pomocou reflexného API, ktoré je zdokumentované oddelene.

Príklad B-28. Príklad použita reflexného API

<?php
class Foo {
    
public $prop;
    function
Func($name) {
        echo
"Ahoj $name";
    }
}

reflectionClass::export('Foo');
reflectionObject::export(new Foo);
reflectionMethod::export('Foo', 'func');
reflectionProperty::export('Foo', 'prop');
reflectionExtension::export('standard');
?>