Магический метод PHP __call на подклассах

Моя ситуация лучше всего описывается небольшим количеством кода:

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()!" 

Я знаю, что могу заставить это работать, переопределяя bar() (и все другие соответствующие методы) в подклассе, но для моих целей было бы неплохо, если __call функция __call могла их обрабатывать. Это просто сделало бы вещи более аккуратными и более управляемыми.

Возможно ли это в PHP?

__call() вызывается только тогда, когда функция не встречается иначе, поэтому ваш пример, как написано, невозможен.

Это невозможно сделать напрямую, но это одна из возможных альтернатив:

 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); } } 

Такая вещь хороша для отладки и тестирования, но вы должны избегать __call() и друзей как можно больше в производственном коде, поскольку они не очень эффективны.

Одна вещь, которую вы можете попробовать, – это настроить объем ваших функций на частные или защищенные. Когда одна частная функция вызывается извне класса, она вызывает метод __call magic, и вы можете ее использовать.

Если вам нужно добавить что-то лишнее в родительский бар (), можно ли это сделать?

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

или это просто вопрос из любопытства?

То, что вы можете сделать, чтобы иметь такой же эффект, следующее:

  <?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." ?>