views:

595

answers:

4

My situation is best described with a bit of code:

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

I know I can get this to work by redefining bar() (and all the other relevant methods) in the sub-class, but for my purposes, it'd be nice if the __call function could handle them. It'd just make things a lot neater and more manageable.

Is this possible in PHP?

+4  A: 

__call() is only invoked when the function isn't otherwise found so your example, as written, is not possible.

cletus
+1  A: 

It can't be done directly, but this is one possible alternative:

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

This sort of thing is good for debugging and testing, but you want to avoid __call() and friends as much as possible in production code as they are not very efficient.

too much php
A: 

If you need to add something extra to the parent bar(), would this be doable?

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

or is this just a question from curiosity?

alex
the problem stems from the fact that the parent class could have a heap of functions, and I don't want to have to duplicate them all in the sub class, just to apply the same behaviour (the `// do something else first` part) to all of them
nickf
A: 

What you could do to have the same effect is the following:

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

?>
Frank de Jonge