这个问题与另一个问题有关: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
是公共的,对象也是定义私有方法的子类的实例。
这是如何运作的?
答案 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";
}
}
如果指定的方法在__call
或Sub
中不存在,则仅在Sub
内调用Super
方法。如果没有,则调用Sub::__call()
,然后调用Super::__call
。结果是抛出异常,或者将控制权交还给Sub::__call
,然后调用actualMethod()
。
我希望这是有道理的。
修改强>
我完全忘记在我的示例中添加return
个关键字。显然,如果你试图返回除void之外的任何东西,这些都是至关重要的。