2012-03-01 27 views
9

pracy z klasy biblioteki PHP, i chciałbym, aby owinąć wszystkich swoich funkcji publicznych w podklasie ... coś wzdłuż linii:PHP: Wrap wszystkie funkcje klasy w podklasie

class BaseClass 
{ 
    function do_something() 
    { 
     some; 
     stuff; 
    } 

    function do_something_else() 
    { 
     other; 
     stuff; 
    } 

    /* 
    * 20-or-so other functions here! 
    */ 
} 

class SubClass extends BaseClass 
{ 
    function magicalOverrideEveryone() 
    { 
     stuff-to-do-before;  // i.e. Display header 
     call_original_function(); // i.e. Display otherwise-undecorated content 
     stuff-to-do-after;   // i.e. Display footer 
    } 
} 

Gotując, wolałbym nie nadpisywać każdej metody nadklasy tym samym kodem opakowania, jeśli istnieje [nieco elegancki/czysty] sposób na zrobienie tego wszystkiego w jednym miejscu.

Czy to możliwe? Podejrzewam, że jestem w METAPROGRAMOWANIE ziemi tutaj, i nawet nie wiem, czy PHP oferuje taką bestię, ale pomyślałam, pytam ...

+0

Aby wyjaśnić - istnieją tylko niektóre metody z "BaseClass", które chcesz zastąpić w "SubClass", nie wszystkie z nich? –

+0

@ s992, niezupełnie - szukałem sposobu na zastosowanie tego samego kodu opakowania do każdej metody w klasie bazowej. Wygląda na to, że metoda PHP __call zrobi to za pomocą opakowania proxy, dzięki @meagar! – mtbkrdave

+0

Mam cię, pytanie nie było dokładnie jasne. Jeśli odpowiedź meagara ci pomogła, powinieneś to zaakceptować. :) –

Odpowiedz

16

Można to łatwo zrobić z __callmagic method i ogólną klasą "proxy", która nie dziedziczy bezpośrednio z klasy bazowej.

Oto (bliska) kompletna implementacja klasy proxy, która owija obiekt, który go przekazałeś. Będzie wywoływał kod "przed" i "po" wokół każdego wywołania metody.

class MyProxy { 
    function __construct($object) { 
    $this->object = $object; 
    } 

    function __call($method, $args) { 
    // Run before code here 

    // Invoke original method on our proxied object 
    call_user_func_array(array($this->object, $method), $args); 

    // Run after code here 
    } 
} 


$base = new BaseClass(); 
$proxy = new MyProxy($base); 

$proxy->doSomething(); // invoke $base->doSomething(); 

Można by oczywiście chcą dodać trochę obsługi błędów, jakby prosząc proxy obiektu, jeśli reaguje na danej metody w __call i podnoszenie błąd, jeśli tak nie jest. Można nawet zaprojektować klasę Proxy jako klasę bazową dla innych serwerów proxy. Podrzędne klasy proxy mogą implementować metody before i after.

Minusem jest to, że „klasa dziecko” nie jest już implementuje BaseClass, czyli jeśli używasz type-hinting i chcą domagać się tylko obiekty typu BaseClass są przekazywane do funkcji, to podejście się nie powiedzie.

+0

Fenomenalny - dokładnie tego szukałem! – mtbkrdave

+0

Również ... WOW, który był szybki! Nigdy nie lekceważ potęgi Stackoverflow! – mtbkrdave

+1

Jednym ze sposobów obejścia problemu typu jest sprawdzenie, w jaki sposób wersja PHP Mockito, Phockito, generuje klasy Mock. https://github.com/hafriedlander/phockito/blob/master/Phockito.php#L170 –

0

może być patrząc na wzór dekorator:

class A 
{ 
    function do1(){ 

    } 
} 
class DecoratorForA extends A 
{ 
    private A original; 
    public DecoratorForA(A original) 
    { 
     this.original = original; 
    } 
    function do1(){ //<-- override keyword in php? 
     stuffBefore(); 
     original->do1(); 
     stuffAfter();  
    } 
} 

ponieważ nie jest to, co chcesz, może ten link jest pomocny? http://code.google.com/p/php-aop/

+0

oh, teraz przeczytałem ją poprawnie, zgadnij, że tego NIE szukasz. – mindandmedia

+0

Próbuje znaleźć prosty sposób, aby ogólnie zastosować wzór dekoratora * bez * zapisania wielkości kodu standardowego. Właśnie nadałeś jego potrzebom nazwę, a nie rozwiązanie ... – rodneyrehm

+0

Prawidłowo, szukałem metaprogramującej aplikacji wzoru dekoratora ... Dziękujemy za Twój wkład! – mtbkrdave

2

Jeśli nazwy metod SubClass mogą nieznacznie różnić się od oryginalnych nazw metod obiektu BaseClass, można napisać ogólne opakowanie o numerze __call(). Jeśli nazwy metod muszą być zgodne, nie widzę sposobu, w jaki można osiągnąć cel bez ręcznego nadpisywania każdej metody. Może mógłbyś użyć do tego celu funcall PECL - ale musisz najpierw załadować PECL.

Jeśli możesz wykonać metody BaseClass protected, podejście __call() w SubClass będzie działać.

Jeśli nie chcesz potrzebujesz do rozszerzenia klasy, podejście @magera jest całkowicie w porządku. Zwróć uwagę, że __call() i call_user_func_array() narzucają pewien narzut.

+0

Jest to ważna rzecz, na którą należy zwrócić uwagę - dziękuję. Nie myślę (myślę), że muszę rozszerzyć klasę, więc przewiduję, że podejście proxy będzie działać. (Zależy od tego, jak przebiega routing CodeIgniter pod maską) – mtbkrdave

0

Jeśli dobrze cię rozumiem, chcesz rozszerzyć klasę, ale nie możesz zezwolić na wywoływanie żadnej metody od rodzica. Zamiast tego chcesz wywołać wszystkie metody samodzielnie w jednej metodzie w nowej klasie.

Dlaczego więc chcesz w ogóle dziedziczyć? To brzmi tak, jakbyś po prostu tworzył adapter/dekorator dla swojej BaseClass.

class SubClass 
{ 
    function magicalOverrideEveryone() 
    { 
     BaseClass bc = new BaseClass(); 
     bc.do_something(); 
     bc.do_something_else(); 
    } 
} 
Powiązane problemy