Witaj, Gościu O nas | Kontakt | Mapa
Wortal Forum PHPEdia.pl Planeta Kubek IRC Przetestuj się!

Dlaczego PHP5?

Hokus, pokus - funkcje magiczne

Podczas omawiania nowego modelu obiektowego PHP5, natknęliśmy się na dwie metody, których nazwy rozpoczynają się od podwójnego znaku podkreślenia: __construct() i __destruct(). Jeśli dokładniej przeszukamy standardową dokumentację PHP, znajdziemy kilka innych funkcji o podobnym schemacie nazewnictwa, np. __call, __autoload() czy __wakeup(). Mimo karkołomnego nazewnictwa, funkcje te mogą spełniać niezwykle pożyteczne zadania.

AOP

W PHP5 pojawiła się nowa, bardzo ciekawa metoda: __call. Jest ona wywoływana automatycznie, gdy w obiekcie danej klasy nie zdefiniowano wywoływanej metody.

Nie zatrzymując się dłużej nad suchą definicją, przyjrzyjmy się przykładowi, który pokaże zastosowanie metody __call do bardzo łatwej implementacji wzorca projektowego o nazwie dekorator. Ilość zastosowań dekoratora jest naprawdę olbrzymia, a jego istota sprowadza się do wzbogacenia funkcjonalności ("udekorowania") klasy, bez jej modyfikowania. Jak to możliwe? Aby to wyjaśnić, posłużmy się przykładem: załóżmy, że chcielibyśmy zapisać do pliku log wszystkie wykonywane instrukcje SQL i czas ich trwania. Podejście takie wymaga jednak modyfikacji klasy bibliotecznej i jest zupełnie niewykonalne przy korzystaniu z PDO (nowy interfejs bazodanowy, ang. PHP5 Data Objects Abstraction Layer), o którym opowiem w dalszej części artykułu). Na szczęście możemy udekorować pierwotną klasę, dodając potrzebną nam funkcjonalność (Listing 9).

O ile metoda __call jest uruchamiana w momencie braku definicji docelowej metody, to dzięki __get i __set możemy osiągnąć identyczny efekt dla pól klasy. Może to być bardzo pomocne przy śledzeniu niewłaściwych odwołań do pól klasy, np. tak jak na Listingu 10.

__autoload

Prawdziwą zmorą PHP, niestety - obecną również w PHP5, jest brak przestrzeni nazw i konieczność ręcznego włączania potrzebnych definicji klas i funkcji. Powoduje to, z jednej strony, konieczność umieszczania wielu wierszy z instrukcjami include czy require na początku każdego skryptu korzystającego z bibliotek. Co więcej, bardzo łatwo o konflikt, gdy chcemy zastosować dwie biblioteki, w których znajdują się klasy o identycznych nazwach.

Próbą rozwiązania wspomnianych problemów jest przyjmowanie konwencji nazewnictwa, tak jak ma to miejsce w pakietach PEAR. Wypracowane konwencje mogą być bardzo różne, ale najczęściej sprowadzają się do takiego pisania klas, by w ich nazwach znalazła się nazwa producenta, modułu itd., np.: DB_DataObject_FormBuilder_QuickForm_ElementTable.

Uzgodnione standardy nazewnictwa pomagają w uniknięciu konfliktów w nazewnictwie klas, ale nie rozwiązują problemu włączania ich definicji. Tu z pomocą przychodzi nowa funkcja PHP5: __autoload(). Jej działanie można opisać bardzo prostym algorytmem: jeśli w programie pojawi się użycie klasy, która nie jest jeszcze zadeklarowana, wywoływana jest wspomniana funkcja __autoload(). Wewnątrz niej możemy próbować odnaleźć i załadować potrzebną definicję, np. tak jak na Listingu 11.

O ile na pierwszy rzut oka funkcja __autoload() wydaje się szalenie użyteczna, to przed podjęciem decyzji o jej stosowaniu warto zdawać sobie sprawę z dwóch poważnych ograniczeń. Po pierwsze, bardzo ściśle łączymy nazewnictwo klas z nazwami i położeniem skryptów w systemie plików. Powoduje to, że trudniej nam będzie zmienić nazwę klasy, co ma często miejsce przy poprawianiu wewnętrznej struktury kodu (ang. refactoring). Po drugie, możemy popaść w spore kłopoty, jeśli znajdziemy bibliotekę, która używa zupełnie innej konwencji nazewniczej (np. przechowuje wszystkie pliki w jednym folderze, bez względu na obecność znaków podkreślenia): funkcję __autoload definiujemy przecież raz, dla całego skryptu!

Listing 9. Dekorator zaimplementowany przy użyciu __call

<?php
class PDODecorator {
    public $_decoratedPDO;
    public function __construct(PDO $decoratedPDO){
        $this->_decoratedPDO = $decoratedPDO;
    }
    public function __call($name, $arguments){
        echo "Przed wykonaniem metody '$name' w PDO";
        $result = call_user_func_array(array(&$this->_decoratedPDO, $name),$arguments);
        echo "Po wywo&#322;aniu metody '$name' w PDO";
        return $result;
    }
}
try {
    $dbh = new PDODecorator(new PDO($dsn, $user, $password));
    ...
}
catch(PDOException $e) {
    echo 'DB error: '. $e->getMessage();
}
?>

Listing 10. Wychwytywanie niewłaściwego dostępu do pól przez __set i __get

<?php
class CheckAccessViolation {
    public $field1;
    public function __set($fieldName, $value ){
        $this->throwAccessViolation($fieldName);
    }
    public function __get($fieldName ){
        $this->throwAccessViolation($fieldName);
    }
    private function throwAccessViolation($fieldName){
        throw new Exception("Pole '$fieldName' nie istnieje w klasie '".__CLASS__."'");
    }
}
$obj = new CheckAccessViolation();
$obj->field2 = 1;
?>
__sleep i __wakeup

Ostatnie z omawianych funkcji magicznych są najczęściej używane razem i pomagają kontrolować zachowanie obiektu podczas jego serializacji i odtwarzania. Jak łatwo wywnioskować z nazw tych metod, __sleep jest wywoływane automatycznie przekształcamy obiekt z jego naturalnej postaci na ciąg znaków (serializacja), __wakeup natomiast odwrotnie - przy konwersji reprezentacji znakowej do instancji obiektu. Podjęcie specjalnych środków w czasie serializacji może być konieczne, jeśli w obiekcie są pola, które odwołują się do zewnętrznych zasobów, np. uchwyty (ang. handler) do pliku czy bazy danych. Aby prawidłowo przywrócić taki obiekt do życia, trzeba własnoręcznie zadbać o ponowne połączenie pól obiektu z zewnętrznymi zasobami. Może się to odbywać podobnie jak na Listingu 12.

Listing 11. Przykładowa implementacja __autoload

function __autoload($class_name)
{
    $PATH_SEPARATOR = '_';
    $base_path = dirname(__FILE__);
    $full_path = join(split($PATH_SEPARATOR, $class_name), '/');
    require_once $base_path.'/'.$full_path.'.php';
}
$obj = new Pakiet_Klasa();

Listing 12. Przykład użycia __sleep oraz __wakeup

<?php
class ClassWithDBConnection {
    private $_dbhandle = null;
    private $_field1 = 5;
    private $_field2 = 3;
    function __sleep()
    {
        $this->_dbhandle = null;
        $objectVars = get_object_vars($this);
        $serializeVars = array();
        foreach($objectVars as $key => $val) {
            $serializeVars[] = $key;
        }
        return $serializeVars;
    }
    function __wakeup()
    {
        $this->connectToDB();
    }
    private function connectToDB(){
        //metoda do pobierania połączenia, np.:
        $this->_dbhandle = DBManager::getConnection();
    }
}
$obj = new ClassWithDBConnection();
$serialstr = serialize(new ClassWithDBConnection());
$unserializedObj = unserialize($serialstr);
?>
« SPL
PDO »
Informacje na podobny temat:
Wasze opinie
Wszystkie opinie użytkowników: (5)
Brakujące listingi
Czwartek 11 Grudzień 2008 8:58:45 am - feft

Widzę, że do dzisiaj nie pojawiły się brakujące listingi - wielka szkoda.
Piotr

listingi
Wtorek 07 Sierpień 2007 8:14:16 am - Olimpia_ona

"Na Listingu 16 zaprezentowaliśmy prosty plik XML, którym będziemy manipulować w kolejnych przykładach."
A gdzie ten listing? Nie ma też listingu 17.
XML / WebServices to interesujący mnie temat, szkoda że z tego artykułu nic się nie dowiedziałam.

super
Czwartek 21 Czerwiec 2007 3:35:34 pm - pawelad <pawelkostomloty_at_op.pl>

Kocham obiektowosc a ostatnio interesuje sie php. Wasza stronka to miod na moje serce. No to macie kolejnego wyznawce.

magiczne metody, przestrzenie nazw
Sobota 11 Marzec 2006 11:38:11 am - mysz

Od wersji php5.1 doszły dwie magiczne metody: __isset i __unset, wywoływane, jak ich nazwa wskazuje, gdy na własności obiektu użyje się funkcji isset() lub unset(). Bardzo przyjemne.

A co do przestrzeni nazw, to mój sposób na implementacje ich w PHP5: http://diary.urzenia.net/?p=261

techniki obiektwe rulezzz
Sobota 28 Styczeń 2006 5:37:00 pm - emp

Ja sobie osbiście nie wyobrażam robić projekt w php bez udogodnien dla technik obiektowych... choć te narazie w wersji 5 wygladaja dość żałośnie to wole to od braku takich udogodnien.. pozatym łapanie wyjątków to podstawowe udogodnienie bez ktorego ciężko się obejśc w większych projektach... tak ja wiem ze takie powstały tylko przy użyciu php5 moze je wykonać jeden człowiek a nie zespół... czcionka jest qrefsko mała ledwo widze co pisze..

Mentax.pl    NQ.pl- serwery z dodatkiem świętego spokoju...   
O nas | Kontakt | Mapa serwisu
Copyright (c) 2003-2022 php.pl    Wszystkie prawa zastrzeżone    Powered by eZ publish Content Management System eZ publish Content Management System