Как ссылки PHP работают под капотом для массивов?

Я читаю эту статью о ссылках на переменные PHP: http://derickrethans.nl/talks/phparch-php-variables-article.pdf
и хотел проверить правильность моего понимания относительно того, когда создаются новые переменные контейнеры.

Для не массивов контейнеры переменных создаются каждый раз, когда вы назначаете переменную, которая не указывает на контейнер с установленным is_ref.

Ex 1 (let {..} be a container):
$a = 1;     // "a" => {integer, 1, is_ref = 0, ref_count = 1}

$b = $a;    // "a", "b" => {integer, 1, is_ref = 0, ref_count = 2}

$b = 2;     // "a" => {integer, 1, is_ref = 0, ref_count = 1}
// "b" => {integer, 2, is_ref = 0, ref_count = 1}

Ex 2:
$a = 1;     // "a" => {integer, 1, is_ref = 0, ref_count = 1}

$b = &$a;    // "a", "b" => {integer, 1, is_ref = 1, ref_count = 2}

$b = 2;     // "a", "b" => {integer, 2, is_ref = 1, ref_count = 2}

Как это работает для массивов? Это не похоже на то же самое. Например,

$a = array(1, 2, 3);
$b = $a;
$c = &$b[2];
$c = 4;
print_r($a); // prints (1, 2, 3) instead of (1, 2, 4)
print_r($b); // prints (1, 2, 4)

Мое ожидание:

$ a и $ b указывают на один и тот же контейнер. В этом контейнере у нас есть 3 numeric_keys «0», «1», «2», которые указывают на контейнеры для целых чисел 1, 2 и 3 соответственно.

Когда мы делаем $c = &$b[2]обновляем контейнер, содержащий целое число 3:

  • is_ref = 0 становится is_ref = 1
  • ref_count = 1 становится ref_count = 2.

Когда мы делаем $c = 4обновляем контейнер, содержащий целое число 3:

  • целое число 3 становится целым числом 4, так как is_ref установлен

Однако что-то не так с моим ожиданием, потому что $a[2] != 4 в конце. Я пытаюсь понять, почему. Мое лучшее предположение состоит в том, что когда мы пытаемся ссылаться на элементы массива или свойства объекта, механизм PHP сначала проверяет сам массив / объект, чтобы увидеть, является ли is_ref = 1. Если это так, все работает в соответствии с моими ожиданиями. Если is_ref = 0, то происходит что-то еще, что я и вижу. Может кто-нибудь объяснить мне, что это за «что-то еще»?

РЕДАКТИРОВАТЬ
Похоже, это то, что на самом деле происходит. Этот код должен все прояснить!

$a = array(1, 2, 3);
$b = $a;
$c = &$b[2];      // $b points to a new container where $b[0], $b[1] still point to same container as $a[0], $a[1], but $b[2] points to a new container also pointed to by $c
$d = $b;        // $d points to $b's container, this means changing $c will also change $d[2]
$d[0] = 5;      // The container pointed to by $d[0] is the same as the one pointed to by $a[0] and $b[0]. Since this container has is_ref = 0, $d[0] will now point to a new container

// At this point $a = (1, 2, 3), $b = (1, 2, 3), $c = 3, $d = (5, 2, 3)

$d[2] = 25;     // The container pointed to by $d[2] is the same as the one pointed to by $b[2] and $c. Since this container has is_ref = 1, Changing $d[2] will affect both $b[2] and $c.

// At this point $a = (1, 2, 3), $b = (1, 2, 25), $c = 25, $d = (5, 2, 25)

$e = $d[2];     // Since $d[2]'s container has is_ref = 1, $e will point to its own container

$c = 4;         // Same idea as $d[2] = 25; except $e won't get affected

// At this point $a = (1, 2, 3), $b = (1, 2, 4), $c = 4, $d = (5, 2, 4), $e = 25

// only way to have $d[2] be different from $b[2] is to make the container's is_ref = 0
unset($b[2]);
unset($c);
$b[2] = $d[2];
$d[2] = 55;

// At this point $a = (1, 2, 3), $b = (1, 2, 4), $d = (5, 2, 25), $e = 25

8

Решение

Что вы создали $a это была простая переменная. Но когда вы создали $bпо умолчанию PHP скопировал переменную. Так $b теперь полностью отделен от $aТак же, как это было в вашем первом примере.

Затем вы установите $c равный ссылка в $b[2], Таким образом, они оба указывают на то же адрес памяти. Обновите один, и он обновляет другой. Проблема в том, что вы думаете, что $a следует обновить, но это не должно быть, потому что $b это его собственная переменная. Посмотрите, что происходит, когда мы меняемся $b на ссылку на $a

$a = array(1, 2, 3);
$b = &$a;
$c = &$b[2];
$c = 4;
print_r($a); // prints (1, 2, 4)
print_r($b); // prints (1, 2, 4)

Это работает, как вы описываете, потому что $b а также $a ссылаться на то же самое (технически $b теперь символ, указывающий на $a)

Если вы хотите еще глубже погрузиться в тему, вот отличная статья, которая подробно ее освещает. http://webandphp.com/how-php-manages-variables

2

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

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

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