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?
__call()
is only invoked when the function isn't otherwise found so your example, as written, is not possible.
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.
One thing you can try is to set your functions scope to private or protected. When one private function is called from outside the class it calls the __call magic method and you can exploit it.
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?
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."
?>