抽象超类中的魔法__call方法

时间:2013-12-01 14:56:54

标签: php magic-methods

这个问题与另一个问题有关:PHP's magic method __call on subclasses,但我对接受的答案不满意。

我要做的是实现创建方法别名的通用方法,而不必使用魔术__call方法为每个别名定义命名函数。

此系统将使用关联数组作为"alias" => "actualMethod

形式的查找表
abstract class Super {

    private $aliases;

    protected function __construct(array $aliases) {
        $this->aliases = $aliases;
    }

    public function __call($name, $arguments) {

        /* if $name is an alias, replace it */
        if (isset($this->aliases[$name])) {
            $name = $this->aliases[$name];
        }

        /* throw an exception if the method is undefined */
        if (!method_exists($this, $name)) {
            throw new Exception("The specified method or method alias is undefined in the current context");
        }

        /* finally, call the method by its actual name */
        return $this->$name($arguments);
    }

}

问题似乎是我或PHP人员都不理解多态性。

class Sub extends Super {

    public function __construct() {
        parent::__construct(array(
            "alias" => "actualMethod"
        ));
    }

    private function actualMethod() {
        echo "Inside the actual method";
    }

}

当我在抽象类上定义__call方法,然后在子类上定义actualMethod时,当我尝试调用{{__call时,PHP进入actualMethod内的无限递归循环1}}由alias

组成
try {
    $object = new Sub();
    $object->alias(); /* causes infinite __call recursion inside Super */
} catch (Exception $exc) {
    echo $exc->getTraceAsString();
}

这很有趣,因为对method_exists__call的调用会返回 TRUE

当然,我不能成为第一个注意到这种行为的人,对吧?这是什么交易?

修改

基本上,正常的继承规则不适用于魔术方法吗?似乎我无法从__call()(*)内部的继承树中进一步调用私有方法。但是,如果它们在同一个类中定义,我仍然可以调用私有方法。

(*):即使__call是公共的,对象也是定义私有方法的子类的实例。

这是如何运作的?

2 个答案:

答案 0 :(得分:0)

是的,这很奇怪 - 我没有为什么的答案,但问题的解决方法可能是:

    /* finally, call the method by its actual name */
    return call_user_func_array(array($this, $name), $arguments);

答案 1 :(得分:0)

看起来我找到了办法。我不确定它是 方式来做它还是只是一个肮脏的黑客。无论如何:

class Sub extends Super {

    public function __construct() {
        parent::__construct(array(
            "alias" => "actualMethod"
        ));
    }

    public function __call($name, $arguments) {
        if (!method_exists($this, $name)) {
            return parent::__call($name, $arguments);
        }
        return $this->$name($arguments);
    }

    private function actualMethod() {
        echo "Inside the actual method";
    }

}

如果指定的方法在__callSub中不存在,则仅在Sub内调用Super方法。如果没有,则调用Sub::__call(),然后调用Super::__call。结果是抛出异常,或者将控制权交还给Sub::__call,然后调用actualMethod()

我希望这是有道理的。

修改

我完全忘记在我的示例中添加return个关键字。显然,如果你试图返回除void之外的任何东西,这些都是至关重要的。