开发者

PHP's magic method __call on subclasses

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 manag开发者_JAVA技巧eable.

Is this possible in PHP?


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


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.


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.


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

?>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜