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