2009-10-08 5 views
9

Moja sytuacja jest najlepiej opisane z kawałkiem kodu:PHP na podklasy

class Foo { 
    function bar() { 
     echo "called Foo::bar()"; 
    } 
} 

class SubFoo extends Foo { 
    function __call($func) { 
     if ($func == "bar") { 
      echo "intercepted bar()!"; 
     } 
    } 
} 

$subFoo = new SubFoo(); 

// what actually happens: 
$subFoo->bar(); // "called Foo:bar()" 

// what would be nice: 
$subFoo->bar(); // "intercepted bar()!" 

wiem, że mogę uzyskać to do pracy poprzez przedefiniowanie bar() (i wszystkie inne odpowiednie metody) w pod- klasa, ale dla moich celów byłoby dobrze, gdyby funkcja __call mogła sobie z nimi poradzić. To sprawi, że rzeczy będą łatwiejsze i łatwiejsze w zarządzaniu.

Czy to możliwe w PHP?

Odpowiedz

13

__call() jest wywoływany tylko wtedy, gdy funkcja nie zostanie znaleziona inaczej, więc twój przykład, jak napisano, nie jest możliwy.

2

Nie można zrobić bezpośrednio, ale jest to jeden z możliwych alternatyw:

class SubFoo { // does not extend 
    function __construct() { 
     $this->__foo = new Foo; // sub-object instead 
    } 
    function __call($func, $args) { 
     echo "intercepted $func()!\n"; 
     call_user_func_array(array($this->__foo, $func), $args); 
    } 
} 

Takie rzeczy jest dobre dla debugowania i testowania, ale chcesz uniknąć __call() i przyjaciół jak najwięcej w kod produkcyjny, ponieważ nie są bardzo wydajne.

+0

To. Musisz postępować zgodnie ze wzorem Fasady. Posiadaj klasę opakowania, która "jest właścicielem" obiektu, który chcesz zastąpić wszystkimi tymi funkcjami. Użyj funkcji __call(), aby w razie potrzeby przekazać metody, wykonując dodatkową pracę. Nie marnuj wydajności, chyba że Twój kod jest ciągle wywoływany, a twoja aplikacja ma ograniczoną moc obliczeniową (prawie nigdy tak nie jest) - czas pracy programisty jest prawie zawsze ważniejszy niż wydajność przy podejmowaniu decyzji o tego rodzaju kompromisach. –

0

Jeśli chcesz dodać coś dodatkowego do paska rodzica(), czy byłoby to możliwe?

class SubFoo extends Foo { 
    function bar() { 
     // Do something else first 
     parent::bar(); 
    } 
} 

czy to tylko pytanie z ciekawości?

+1

problem wynika z faktu, że klasa nadrzędna może mieć mnóstwo funkcji i nie chcę ich duplikować w klasie podrzędnej, tylko po to, aby zastosować to samo zachowanie ('// coś innego najpierw 'part) do wszystkich z nich – nickf

+0

@nickf Absolutnie, to wydaje mi się czymś tak niezbędnym Nie rozumiem, dlaczego nie jest to w PHP. –

0

Co można zrobić, aby mieć ten sam efekt jest następujący:

<?php 

class hooked{ 

    public $value; 

    function __construct(){ 
     $this->value = "your function"; 
    } 

    // Only called when function does not exist. 
    function __call($name, $arguments){ 

     $reroute = array(
      "rerouted" => "hooked_function" 
     ); 

     // Set the prefix to whatever you like available in function names. 
     $prefix = "_"; 

     // Remove the prefix and check wether the function exists. 
     $function_name = substr($name, strlen($prefix)); 

     if(method_exists($this, $function_name)){ 

      // Handle prefix methods. 
      call_user_func_array(array($this, $function_name), $arguments); 

     }elseif(array_key_exists($name, $reroute)){ 

      if(method_exists($this, $reroute[$name])){ 

       call_user_func_array(array($this, $reroute[$name]), $arguments); 

      }else{ 
       throw new Exception("Function <strong>{$reroute[$name]}</strong> does not exist.\n"); 
      } 

     }else{ 
      throw new Exception("Function <strong>$name</strong> does not exist.\n"); 
     } 

    } 

    function hooked_function($one = "", $two = ""){ 

     echo "{$this->value} $one $two"; 

    } 

} 

$hooked = new hooked(); 

$hooked->_hooked_function("is", "hooked. "); 
// Echo's: "your function is hooked." 
$hooked->rerouted("is", "rerouted."); 
// Echo's: "our function is rerouted." 

?> 
1

Jedno można spróbować to ustawić zakres funkcji, które prywatne lub chronione. Kiedy jedna prywatna funkcja jest wywoływana spoza klasy, wywołuje metodę magiczną __call i możesz ją wykorzystać.