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如何处理一个引用自身作为元素的数组?的主要内容,如果未能解决你的问题,请参考以下文章

PHP如何处理整数索引的非连续键控数组?

如何处理我的 JavaScript 作业? [关闭]

如何处理对尚不存在的对象的 ID 引用的 RestKit 嵌套数组?

PHP XML RPC - 如何处理返回的数组

Angularjs如何处理来自php(json)的二维数组

如何处理 API 有时将属性作为数组返回,有时作为对象返回?