Pracuje właśnie nad mechanizmem automatycznie generującym klasy potomne dziedziczące po istniejącej już w systemie klasie bazowej.
class A { public function __construct() {} public function index() { // do something } } |
Mając powyższą klasę bazową wystarczy wygenerować klasę potomną, która w konstruktorze wywoła konstruktor parenta.
class B extends A { public function __construct() { parent::__construct(); } } |
Tyle, że konstruktor klasy bazowej może mieć jakieś parametry.
class A { private $param; public function __construct($param) { $this->param = $param; } public function index() { // do something } } |
Ponieważ chcę napisać generator w miarę uniwersalny, a nie wiem ile parametrów i jakie może mieć, każda z klas bazowych, które będę chciał rozszerzyć dobrym rozwiązaniem wydawało by się wywoływanie klasy parenta przy pomocy funkcji „call_user_func_array”.
class B extends A { public function __construct() { $args = func_get_args(); call_user_func_array(array('parent', '__construct'), $args); } } |
Po utworzeniu obiektu:
$obj = new B('foo'); |
Wszystko działało zgodnie z oczekiwaniami, było fajnie tylko jak włączyłem raportowanie błędów ze sprawdzaniem składni
error_reporting(E_ALL^E_STRICT); ini_set("display_errors", 1); |
To zobaczyłem taki nieprzyjemny komunikat „**Strict standards: Non-static method A::__construct() cannot be called statically, assuming $this from compatible context B**”.
Powiem szczerze, że tak nie za bardzo wiedziałem co mam począć w tej sytuacji bo najbardziej intuicyjne rozwiązanie okazało się niezgodne ze standardami. Zajrzałem oczywiście do manuala gdzie znalazłem rozwiązanie. Przy okazji dowiedziałem się jeszcze, że inaczej powinno to wyglądać dla PHP w wersji 5.0 – 5.2, a już inaczej w PHP 5.3 i prawdopodobnie 6.
class D extends A { public function __construct() { $args = func_get_args(); if (version_compare(PHP_VERSION, '5.3.0', '>')) { call_user_func_array('parent::__construct', $args); } else { call_user_func_array(array($this, 'parent::__construct'), $args); } } } |
Gdyby to zamykało temat zapewne nie stworzyłbym tego wpisu, tyle, że pojawił się kolejny problem.
W moim eksperymentalnym frameworku używam loadera klas, który w razie nieznalezienia wywoływanej klasy skanuje zadane foldery w poszukiwaniu plików php, a następnie sprawdza czy znajdują się w nich definicje klas lub interfejsów. Następnie tworzy mapę w postaci tablicy, w której kluczem jest nazwa klasy, a wartością ścieżka do pliku zawierającego definicję owej klasy.
Otóż zauważyłem, że przy każdym wywoływaniu „call_user_func_array(array($this, ‚parent::__construct’), $args);” następuje uruchomienie autoloadera i w moim przypadku skanowanie plików w poszukiwaniu klasy „parent”. Gdyby nie związane z tym wydłużenie obsługi requesta zapewne nie zauważyłbym nawet wywołanie autoloadera gdyż pomimo, że funkcja autoloadera zwraca false kod wykonuje się prawidłowo nie generując żadnych błędów lub ostrzeżeń.
Nie mniej koniecznym okazało się spatchowanie autoloadera
function autoload($class) { if ($class == 'parent') { return false; } // ... } spl_autoload_register('autoload'); |