PHP 如何处理将自身作为元素引用的数组?

2022-08-30 23:40:58

假设我声明一个数组:

$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}


答案 1

在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 虚拟机不会达到任何资源限制。


答案 2