Не очевидное поведение цикла foreach в PHP

16 апреля 2011 г.

Здравствуйте! Нашёл интересную особенность работы цикла foreach в php при получении элемента массива по ссылке. Не знаю bug это или так и должно быть. Итак, приготовьтесь удивляться.


    // Заполняем простой двумерный массив, который содержит информацию о работниках
    $workers = array(
            array(
                    'first_name'=>'Ivan', 
                    'last_name'=>'Ivanov'
            ),
            array(
                'first_name'=>'Sergey',
                'last_name'=>'Fedorov'
            ),
            array(
                'first_name'=>'Homer',
                'last_name'=>'Simpson'
            )
        );
    // Делаем обход массива и добавляем новый элемент в подмассивы
    foreach ($workers as &$worker){
        $worker['department'] = 'it';
    }
    // Выводим результат и он вполне ожидаем
    print_r($workers);
    // Теперь делаем ещё один обход массива и готовимся удивляться
    foreach ($workers as $worker){
        echo $worker['first_name'] . '<br/>';
    }
    // Выводим ещё раз
    print_r($workers);

Как Вы видите, здесь всё достаточно очевидно. Первый print_r выводит всё, так как ожидается:

Array
(
    [0] => Array
        (
            [first_name] => Ivan
            [last_name] => Ivanov
            [department] => it
        )

    [1] => Array
        (
            [first_name] => Sergey
            [last_name] => Fedorov
            [department] => it
        )

    [2] => Array
        (
            [first_name] => Homer
            [last_name] => Simpson
            [department] => it
        )

)

Массив состоящий из трех подмассивов с добавленным элементом ‘department’. Далее, мы ещё раз перебираем все элементы в цикле и выводим имена сотрудников, но получаем неожиданный результат:

Ivan
Sergey
Sergey

Таким образом мы видим вместо всеми любимого персонажа симпсонов ещё одного Сергея. Ну а дальнейший вывод просто ввёл меня в состояние шока на несколько часов:

Array
(
    [0] => Array
        (
            [first_name] => Ivan
            [last_name] => Ivanov
            [department] => it
        )

    [1] => Array
        (
            [first_name] => Sergey
            [last_name] => Fedorov
            [department] => it
        )

    [2] => Array
        (
            [first_name] => Sergey
            [last_name] => Fedorov
            [department] => it
        )

)

Как? Куда таинственным образом пропал последний элемент массива? И почему на его месте очутилась копия предыдущего элемента? Этот вопрос мучает меня до сих пор… После проведения дальнейших экспериментов было выяснено, что после выполнения первого foreach’а последний элемент массива стал ссылкой. Вот результат var_dump’a:

...
  }
  [2]=>
  &array(3) {
    ["first_name"]=>
    string(5) "Homer"
    ["last_name"]=>
    string(7) "Simpson"
    ["department"]=>
    string(2) "it"
  }
}
...

Но тем не менее это не объясняет неожиданную трансформацию последнего элемента в предыдущий после выполнения второго foreach’a. В нем даже не производится никаких изменений массива.
Не знаю писать ли bug-report, но в любом случае данное не очевидное поведение может привести к многочасовым погоням за призраками, особенно это касается начинающих программистов.

Теги: рубрика PHP
  • Похожие статьи
  • Предыдущие из рубрики