这是一个非常有效的问题。
1。 [...] 之间的优先级
首先,在查看
[ 的右侧,因为括号需要一个闭合的来配合它,所以
介于两者之间的每个运算符都优先于左括号。
例子:
$a[1+2]
+ 具有优先权,即必须先评估 1+2,然后 PHP 才能确定哪个
要从 $a 中检索的元素。
但运算符优先级列表不是这个。
2。关联性
其次,有一个评估连续对 [] 的顺序,如下所示:
$b[1][2]
PHP 将首先评估$b[1],然后应用[2]。这是从左到右
左结合的意图。
但是手头的问题不是关于关联性,而是关于其他运算符的优先级。
3。左侧运算符的优先级
该列表指出clone 和new 运算符优先于[,这并不容易测试。
首先,将new 与方括号组合的大多数构造都被认为是无效的
句法。例如,这两个语句:
$a = new myClass()[0];
$a = new myClass[0];
会报解析错误:
语法错误,意外'['
PHP 要求您添加括号以使语法有效。所以我们无法测试
像这样的优先规则。
但还有另一种方法,使用包含类名的变量:
$a = new $test[0];
这个是有效的语法,但现在的挑战是创建一个创建某些东西的类
就像一个数组。
这并非易事,因为对象属性的引用如下:obj->prop,不是
比如obj["prop"]。但是,可以使用可以处理方括号的ArrayObject class。我们的想法是扩展这个类并重新定义offsetGet method 以确保该类的新创建的对象具有要返回的数组元素。
为了使对象可打印,我最终使用了神奇的方法__toString,它在需要将对象转换为字符串时执行。
所以我想出了这个设置,定义了两个相似的类:
class T extends ArrayObject {
public function __toString() {
return "I am a T object";
}
public function offsetGet ($offset) {
return "I am a T object's array element";
}
}
class TestClass extends ArrayObject {
public function __toString() {
return "I am a TestClass object";
}
public function offsetGet ($offset) {
return "I am a TestClass object's array element";
}
}
$test = "TestClass";
通过这个设置,我们可以测试一些东西。
测试 1
echo new $test;
此语句创建一个新的 TestClass 实例,然后需要将其转换为
字符串,因此在该新实例上调用 __toString 方法,该方法返回:
我是一个TestClass对象
这符合预期。
测试 2
echo (new $test)[0];
这里我们从相同的操作开始,因为括号强制首先执行new 操作。这一次 PHP 不会将创建的对象转换为字符串,而是向它请求数组元素 0。此请求由 offsetGet 方法响应,因此上述语句输出:
我是一个TestClass对象的数组元素
测试 3
echo new ($test[0]);
这个想法是强制执行相反的顺序。可悲的是,PHP 不允许这种语法,因此必须将语句分成两部分才能获得预期的评估顺序:
$name = $test[0];
echo new $name;
所以现在首先执行[,取值的第一个字符
$test,即 "T",然后将new 应用于它。这就是为什么我
还定义了一个 T 类。 echo 在该实例上调用 __toString,产生:
我是T对象
现在是最后的测试,看看没有括号时的顺序:
测试 4
echo new $test[0];
这是有效的语法,并且...
4。结论
输出是:
我是T对象
所以事实上,PHP 在 new 运算符之前应用了 [,尽管在
operator precedence table!
5。比较 clone 和 new
clone 运算符与[ 组合具有类似的行为。奇怪的是,clone 和new 在语法规则方面并不完全相等。使用 clone 重复测试 2:
echo (clone $test)[0];
产生解析错误:
语法错误,意外'['
但是用clone 重复的测试4 表明[ 优先于它。
@bishop 告知这再现了长期存在的文档错误#61513: "clone operator precedence is wrong"。