在内部PHP中,所有内容都存储在名为
ZVAL的
variant容器中。$数据由ZVAL表示,每个键和$数据中的每个值都是ZVAL等。
所以在初始赋值后,从PHP创建了三个ZVAL:
/-------------------\ /-------------------\
| ZVAL #1 | /==>| ZVAL #2 |
| type: array | | | type: string |
| data: [ | | | data: "foo" |
| { | | \-------------------/
| key: =======/ /-------------------\
| val: ================================>| ZVAL #3 |
| } | | type: string |
| ] | | data: "bar" |
\-------------------/ \-------------------/
注意:数组项目的内部表示与上述不相符;我不想用不必要的细节来负担这个答案。由于相同的原因,ZVAL的表示也被简化。如果您想了解有关PHP内部的更多内容,请阅读源和/或this。
您可以看到,使用“foo”和“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’]?它知道$数据由ZVAL#1表示,并且它看到您正在尝试使用数组语法对其进行索引。它查看ZVAL,看到它是一个数组,找到具有键“baz”的项,并获取表示它的ZVAL。你知道什么?再次是ZVAL#1。最终解决了$ data [‘baz’]。
在下一步,它会看到您正在尝试将数据索引到$ data [‘baz’]。它知道$数据[‘baz’]由ZVAL#1表示,所以同样的事情会再次发生,等等。
您将注意到,上述过程不涉及存储任何中间结果(第一步和第二步完全独立),这意味着在尝试解析阵列访问时,没有资源限制被PHP虚拟机击中。