Ошибка или взлом? $ GLOBALS

#php #arrays #global-variables

#php #массивы #глобальные переменные

Вопрос:

 $GLOBALS["items"] = array('one', 'two', 'three', 'four', 'five' ,'six', 'seven');
$alter = amp;$GLOBALS["items"]; // Comment this line
foreach($GLOBALS["items"] as $item) {
  echo get_item_id();
}

function get_item_id(){
  var_dump(key($GLOBALS["items"]));
}
  

Проверьте вывод этого кода с закомментированной и раскомментированной второй строкой.
Мой результат (PHP 5.3.0).
Со второй строкой

 int(1) int(2) int(3) int(4) int(5) int(6) NULL
  

Без второй строки:

 int(1) int(1) int(1) int(1) int(1) int(1) int(1)
  

Почему такой странный результат?

Комментарии:

1. Я честен.. Понятия не имею.. получение указателя на $ GLOBALS не должно изменять переменную.

2. Хороший взлом. По-видимому, $ alter берет на себя управление. Если вы установите значение $alter равным NULL после присвоения массива, массив даже станет недействительным и вызовет ошибку в следующем цикле.

3. Возможно, это как-то связано с тем фактом, что $GLOBALS сам по себе является массивом ссылок. А ссылки в PHP всегда обалденные.

4. @BoltClock, ты можешь заменить $ GLOBALS на другую переменную массива, и этот хак тоже работает )))

5. Я не понимаю, почему это должно быть «взломом». В чем преимущество? Имо, это либо ошибка, либо странное поведение, либо его можно объяснить.

Ответ №1:

Вот возможное объяснение:

Мы знаем, что foreach всегда выполняется цикл над копией массива, если на него нет ссылки:

Если на массив нет ссылки, foreach работает с копией указанного массива, а не с самим массивом. foreach имеет некоторые побочные эффекты для указателя на массив.

Это означает, что внутренний указатель исходного массива не изменен и key() всегда будет возвращать одно и то же значение (как мы можем видеть, когда закомментируем строку). И действительно, если мы сделаем var_dump($GLOBALS) , мы получим:

  ["items"]=>
  array(7) {
    [0]=>
    string(3) "one"
    [1]=>
    string(3) "two"
    [2]=>
    string(5) "three"
    [3]=>
    string(4) "four"
    [4]=>
    string(4) "five"
    [5]=>
    string(3) "six"
    [6]=>
    string(5) "seven"
  }
  

(ссылки нет)

Но как только мы генерируем ссылку на массив (с помощью $alter ), $GLOBALS['items'] ссылка тоже становится ссылкой, потому что обе записи должны указывать на один и тот же массив:

  ["items"]=>
  amp;array(7) {
    [0]=>
    string(3) "one"
    [1]=>
    string(3) "two"
    [2]=>
    string(5) "three"
    [3]=>
    string(4) "four"
    [4]=>
    string(4) "five"
    [5]=>
    string(3) "six"
    [6]=>
    string(5) "seven"
  }
  ["alter"]=>
  amp;array(7) {
    [0]=>
    string(3) "one"
    [1]=>
    string(3) "two"
    [2]=>
    string(5) "three"
    [3]=>
    string(4) "four"
    [4]=>
    string(4) "five"
    [5]=>
    string(3) "six"
    [6]=>
    string(5) "seven"
  }
  

Следовательно, foreach цикл выполняет итерацию по исходному массиву и изменяет внутренний указатель, что влияет key() .


Подводя итог: это проблема со ссылками, а не с $GLOBALS .

Комментарии:

1. 1 Я представляю какого-нибудь бедолагу, который пытается изменить значения этого скопированного массива в цикле foreach.