PHP如何处理一个引用自身作为元素的数组?
Posted
技术标签:
【中文标题】PHP如何处理一个引用自身作为元素的数组?【英文标题】:How does PHP handle an array with a reference to itself as an element? 【发布时间】:2013-10-31 08:42:11 【问题描述】:假设我声明了一个数组:
$data = array( 'foo' => 'bar' );
现在我将添加对自身的引用作为新元素:
$data['baz'] = &$data;
转储$data
的内容会导致:
Array
(
[foo] => bar
[baz] => Array
(
[foo] => bar
[baz] => Array
*RECURSION*
)
)
现在,我可以转储$data['baz']['baz']['baz']['baz']['baz']['baz']['baz']['baz']['baz']
的内容,结果将与上面完全相同,因为数组有一个指向自身作为元素的指针。
我想知道的是 php 是否将数组作为一组数据处理,其指针与我在使用 $data
时调用的指针完全相同,或者它是否做一些完全不同的事情。
另外,PHP 在返回$data['baz']*n
的内容时会耗尽内存吗?
【问题讨论】:
实际上,要理解这一点,您需要了解 PHP 数组是什么。它们不是列表或类似的东西,它们是散列,简而言之。所以 PHP 存储了一个引用,它是键。问题是在“递归”数组中,这样的引用指向它是另一个元素,仅此而已(即它仍然是一个引用,仅此而已)。而且 - 不,如果您在每次迭代时重新分配元素,您将不会出现内存溢出 就像一个循环链表。它仅将内存用于单个循环,而不是用于所有副本的无限内存。 数组没有“列”,列是您在将数组元素呈现为表格时选择的解释方式。 @meagar 这是真的。另外 - 虽然我对此有正确的想法,但我会等待能够以更正式和限制性的方式解释这一点的人:) 【参考方案1】:在 PHP 内部,所有内容都存储在称为 ZVAL 的 variant 容器中。 $data
用一个 ZVAL 表示,$data
里面的每个键和每个值都是一个 ZVAL 等等。
所以在初始赋值之后,从 PHP 中创建了三个 ZVAL:
/-------------------\ /-------------------\
| ZVAL #1 | /==>| ZVAL #2 |
| type: array | | | type: string |
| data: [ | | | data: "foo" |
| | | \-------------------/
| key: =======/ /-------------------\
| val: ================================>| ZVAL #3 |
| | | type: string |
| ] | | data: "bar" |
\-------------------/ \-------------------/
注意:数组item的内部表示与上图不对应;我不想用不必要的细节给答案增加负担。出于同样的原因,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']
的呢?它知道$data
由 ZVAL#1 表示,并且它看到您正在尝试使用数组语法对其进行索引。它查看 ZVAL,发现它是一个数组,找到具有键 "baz"
的项目并获取代表它的 ZVAL。你知道什么?这又是 ZVAL#1。 $data['baz']
的解析到此结束。
在下一步中,它发现您正在尝试将 $data['baz']
作为数组进行索引。它知道$data['baz']
由 ZVAL#1 表示,所以同样的事情最终会再次发生,依此类推。
您会注意到,上面的过程不涉及存储任何中间结果(第一步和第二步是完全独立的),这意味着 PHP 虚拟机在尝试解决问题时没有资源限制数组访问。
【讨论】:
感谢您的详细解答。这正是我想要的。 还有一个问题:你自己输入了ascii吗? ;) @BobKruithof:很高兴能提供帮助,是的,我做到了 :-)以上是关于PHP如何处理一个引用自身作为元素的数组?的主要内容,如果未能解决你的问题,请参考以下文章