我想知道这是一个错误还是正常。假设我有一个具有一些神奇功能的课程:
class Foo {
public function __toString() {
return '`__toString` called.';
}
public function __get($key) {
return '`__get(' . $key . ')` called.';
}
public function __invoke($x = "") {
return '`__invoke(' . $x . ')` called.';
}
}
然后在对象属性中创建一个实例,如下所示:
$object = (object) [
'foo' => 'bar',
'baz' => new Foo
];
然后测试一下:
echo $object->baz;
echo $object->baz->qux;
echo $object->baz('%'); // :(
它在最后一个回声中被打破:Call to undefined method stdClass::baz()
目前,我能做的唯一解决方案是将__invoke
部分存储在临时变量中,然后将该变量作为这样的函数调用:
$x = $object->baz;
echo $x('%'); // :)
当我在数组属性中实例化类时,它工作正常:
$array = [
'baz' => new Foo
];
echo $array['baz'];
echo $array['baz']->qux;
echo $array['baz']('%'); // :)
顺便说一句,我需要在我的对象上使用此功能来处理与API相关的事情:
$foo = (object) ['bar' => new MyClass];
echo $foo->bar;
→应触发__toString
echo $foo->bar->baz;
→应触发__get
echo $foo->bar();
→应触发__invoke
echo $foo->bar->baz();
→应触发__call
所有这些都应该返回一个字符串。
这完全可以在PHP中完成吗?感谢。
答案 0 :(得分:5)
没有办法。
有问题的行很简单,错误消息会告诉您如何...尝试访问baz()
对象的$object
方法更合乎逻辑。
这只是解析器在看到$object->baz()
正如评论中已经提到的,你可以消除这种歧义,通过告诉它$object->baz
本身就是一个需要首先执行的表达来帮助解析器:
($object->baz)('arg');
PHP本身也是一个程序,必须知道如何在执行之前执行某些操作。如果它可以盲目地在$foo->bar->baz->qux
链中的每个对象上尝试每种可能的“魔法”方法,那么它就无法告诉你遇到它时的错误 - 它会无声地崩溃。
答案 1 :(得分:1)
我通过在类的__invoke
方法中检测到__call
方法的存在来解决我的问题。
class MyStdClass extends stdClass {
protected $data = [];
public function __construct(array $array) {
$this->data = $array;
}
public function __get($key) {
return isset($this->data[$key]) ? $this->data[$key] : null;
}
public function __call($key, $args = []) {
if (isset($this->data[$key])) {
$test = $this->data[$key];
// not an object = not an instance, skip!
if (!is_object($test)) {
return $this->__get($key);
}
if (!empty($args) && get_class($test) && method_exists($test, '__invoke')) {
// or `return $test(...$args)`
return call_user_func([$test, '__invoke'], ...$args);
}
}
return $this->__get($key);
}
public function __set($key, $value = null) {
$this->data[$key] = $value;
}
public function __toString() {
return json_encode($this->data);
}
public function __isset($key) {}
public function __unset($key) {}
}
因此,我没有使用(object)
将数组转换为对象,而是使用:
$object = new MyStdClass([
'foo' => 'bar',
'baz' => new Foo
]);