这个问题与另一个问题有关: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";
}
}
如果指定的方法在Sub
或Super
中都不存在,则仅在Sub
内部调用__call
方法。如果没有,则调用Sub::__call()
,进而调用Super::__call
。结果是,要么抛出异常,要么将控制权交还给Sub::__call
,后者随后调用actualMethod()
。
我希望这是有道理的。
编辑
我完全忘记在示例中添加return
关键字。显然,如果你想返回除虚空之外的任何东西,这些都是至关重要的。