17

假设我声明了一个数组:

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

4

1 回答 1

11

在 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 虚拟机在尝试解析数组访问时没有资源限制。

于 2013-10-22T13:15:38.143 回答