Ссылка PHP в ключе массива

PHP:

$a = array("key" => 23);
var_dump($a);

$c = &$a["key"];
var_dump($a);

unset($c);
var_dump($a);

Выход:

array(1) {
["key"]=>
int(23)
}
array(1) {
["key"]=>
&int(23)
}
array(1) {
["key"]=>
int(23)
}

Во втором дампе значение «ключ» отображается в виде ссылки. Это почему?
Если я делаю то же самое с обычной переменной вместо ключа массива, этого не происходит.

Мое единственное объяснение состоит в том, что ключи массива обычно хранятся в виде ссылок, и если в таблице символов есть только одна запись, она отображается в виде скаляра в дампе.

0

Решение

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

По сути, ваш массив выглядит так:

typedef struct _zval_struct {
zvalue_value value;
zend_uint refcount__gc;
zend_uchar type;
zend_uchar is_ref__gc;
} zval;
//zval_value:
typedef union _zvalue_value {
long lval;
double dval;
struct {
char *val;
int len;
} str;
HashTable *ht;
zend_object_value obj;
} zvalue_value;

В случае массива zval.type будет установлен, чтобы указать, что zval значение является массивом, и поэтому zval_value.ht член будет использоваться.
Что происходит, когда вы пишете $c = &$a['key'] это то, что zval который назначен $a['key'] будет обновлено: zval.refcount__gc будет увеличен, и is_ref__gc будет установлен в 1. Просто потому, что значение не копируется, но значение используется более чем одной переменной: это значение является ссылка. Однажды ты unset($c);, refcount уменьшается, и ссылка теряется, и так is_ref установлен в 0,

Теперь о большом: почему вы не видите то же самое, когда используете обычные скалярные переменные? Ну, это потому, что массив является HashTable, в комплекте со своим собственным, внутренним подсчетом ссылок (zval_ptr_dtor). Если сам массив пуст, его тоже следует уничтожить. Создавая ссылку на значение массива, и вы сбрасываете массив, zval должен быть GC’ed. Но это будет означать, что у вас есть ссылка на уничтоженный zval плавать вокруг.
Следовательно zval в массиве тоже изменяется ссылка: ссылку можно безопасно удалить. Так что, если вы должны были сделать это:

$foo = array(123);
$bar = &$foo[0];
unset($foo[0]);
echo $bar, PHP_EOL;

Ваш код все еще будет работать, как и ожидалось: $foo[0] больше не существует, но $bar теперь единственная существующая ссылка на 123.

Это просто очень, очень короткое и неполное объяснение, но посмотрите на внутреннюю часть PHP и то, как работает управление памятью, как обрабатываются ссылки внутри, и как сборщик мусора использует is_ref а также refcount члены для управления памятью.
Обратите особое внимание на внутренние механизмы, такие как копирование при записи, и (просматривая первую ссылку, которую я предоставил здесь), найдите фрагмент, который выглядит следующим образом:

$ref = &$array;
foreach ($ref as $val) {}

Потому что он имеет дело с некоторыми странностями с точки зрения ссылок и массивов.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]