抽象超类中的魔术__call方法


Magic __call method in abstract superclass

这个问题与另一个问题有关:PHP';s的神奇方法__调用子类,但我对接受的答案不满意。

我想做的是实现一种创建方法别名的通用方法,而不必使用magic__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时,当我试图通过alias调用actualMethod时,PHP在__call中进入无限递归循环。

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

这很有趣,因为__call内部对method_exists的调用返回TRUE

我肯定不是第一个注意到这种行为的人,对吧?这是怎么回事?

编辑

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

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

这到底是怎么回事?

是的,这很奇怪-我不知道为什么,但解决问题的方法可能是:

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

看起来我找到了一种方法。我不确定这是方法还是一个肮脏的黑客。总之:

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

如果指定的方法在SubSuper中都不存在,则仅在Sub内部调用__call方法。如果没有,则调用Sub::__call(),进而调用Super::__call。结果是,要么抛出异常,要么将控制权交还给Sub::__call,后者随后调用actualMethod()

我希望这是有道理的。

编辑

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