Подтвердить что ты не робот

Как PHP обрабатывает массив с ссылкой на себя как на элемент?

Предположим, я объявляю массив:

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

4b9b3361

Ответ 1

Внутренне в PHP все хранится внутри контейнера , называемого ZVAL. $data представлен ZVAL, каждый ключ и каждое значение внутри $data является ZVAL и т.д.

Итак, после первоначального присваивания, из PHP создал три ZVALs:

   /-------------------\      /-------------------\   
   | 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']? Он знает, что $data представлен ZVAL # 1, и он видит, что вы пытаетесь проиндексировать его с синтаксисом массива. Он смотрит на ZVAL, видит, что это массив, находит элемент с ключом "baz" и получает ZVAL, который его представляет. Что ты знаешь? Это ZVAL # 1 еще раз. Это завершает разрешение $data['baz'].

На следующем шаге он видит, что вы пытаетесь индексировать в $data['baz'] как массив. Он знает, что $data['baz'] представлен ZVAL # 1, поэтому одно и то же происходит снова и т.д.

Вы заметили, что вышеописанный процесс не включает в себя сохранение промежуточных результатов (первый и второй шаги полностью независимы), что означает, что ограничение виртуальной машины PHP не будет ограничено при попытке решить доступ к массиву.