在PHP内部,所有内容都存储在称为ZVAL的变体容器中。 由 ZVAL 表示,每个键和每个值都是 ZVAL,依此类推。$data
$data
因此,在初始分配之后,PHP创建了三个ZVAR:
/-------------------\ /-------------------\
| ZVAL #1 | /==>| ZVAL #2 |
| type: array | | | type: string |
| data: [ | | | data: "foo" |
| { | | \-------------------/
| key: =======/ /-------------------\
| val: ================================>| ZVAL #3 |
| } | | type: string |
| ] | | data: "bar" |
\-------------------/ \-------------------/
注意:数组项的内部表示与上面显示的内容不对应;我不想用不必要的细节来负担答案。出于同样的原因,ZVAL 的表示形式也进行了简化。如果您想了解有关PHP内部的更多信息,请阅读源代码和/或本文。
您可以看到,和 被用作数组键/值对的事实无法通过查看其 ZVAR 来确定:您必须知道它们正在被数组引用。"foo"
"bar"
在赋值之后,你现在有一个循环引用:在ZVAL #1中的某个地方有一个指针回到ZVAL #1:$data['baz'] = &$data
/-------------------\ /-------------------\
| ZVAL #1 | /==>| ZVAL #2 |
/=>| type: array | | | type: string |
| | data: [ | | | data: "foo" |
| | { | | \-------------------/
| | key: =======/ /-------------------\
| | val: ================================>| ZVAL #3 |
| | }, | | type: string |
| | { | | data: "bar" |
| | key: =========================\ \-------------------/
| | val: =========\ |
| | } | | | /-------------------\
| | ] | | \======>| ZVAL #4 |
| \-------------------/ | | type: string |
| | | data: "baz" |
\===========================/ \-------------------/
那么PHP是如何解决的呢?它知道它由 ZVAL#1 表示,并且它看到您正在尝试使用数组语法将其索引到其中。它查看 ZVAL,查看它是一个数组,找到具有键的项,并获取表示它的 ZVAL。你知道什么?这又是ZVAL#1。的分辨率到此结束。$data['baz']['baz']
$data
"baz"
$data['baz']
在下一步中,它会看到您正在尝试将索引作为数组。它知道这是由ZVAL#1表示的,所以同样的事情最终会再次发生,依此类推。$data['baz']
$data['baz']
您已经注意到,上述过程不涉及存储任何中间结果(第一步和第二步是完全独立的),这意味着在尝试解析阵列访问时,PHP 虚拟机不会达到任何资源限制。