Баг php ?

Темы, не касающиеся фреймворка, но относящиеся к программированию в целом.
Ответить
Arnowt
Сообщения: 182
Зарегистрирован: 2013.09.13, 11:11

Баг php ?

Сообщение Arnowt »

Код: Выделить всё

$allList = [
    ['id'       => 1111,
     'facility' => [['title' => 'T1',
                     'item'  => [11, 12, 13]]]],
    ['id'       => 222,
     'facility' => [['title' => 'T2',
                     'item'  => [21, 22, 23]]]
    ]];
foreach ($allList as &$i) {}
foreach ($allList as $el) {
    print_r($allList);
    if (isset($el['facility']))
        foreach ($el['facility'] as $item)
            foreach ($item['item'] as $i)
                continue;
}
foreach ($item['item'] as $i) ломает массив $allList
Они в доке что-то пишут про &$i, но ладно бы в оказалось что-то не то в переменной, но ломается массив, а при этом ничего не присваивается
Изображение
skynin
Сообщения: 400
Зарегистрирован: 2017.12.12, 10:09

Re: Баг php ?

Сообщение skynin »

foreach ($allList as &$i)

после завершения в $i ссылка на второй массив

foreach ($item['item'] as $i)

записываем по ссылке от $i каждый $item['item']
последним из [11, 12, 13]
будет 13

второй проход foreach ($allList as $el)
его и вернет

другими словами
вы объявили переменную $i
и записали в нее - ссылку
вот здесь
foreach ($allList as &$i)

и далее и будет происходить запись - по ссылке

то есть вы сделали вот что:

Код: Выделить всё

$str = 'Начало';

$a = &$str; // берем ссылку на строку
print_r($a);

$a = 'Изменение'; // меняем оригинальную строку, потому что в $a - ссылка
print_r($a);

Не желайте странного, и не будет у вас головной боли чтобы достичь этого странного.
Тем более что окажется что оно вам и не нужно было, странное это.
Arnowt
Сообщения: 182
Зарегистрирован: 2013.09.13, 11:11

Re: Баг php ?

Сообщение Arnowt »

skynin писал(а): 2020.02.28, 15:57 foreach ($allList as &$i)

после завершения в $i ссылка на второй массив

foreach ($item['item'] as $i)

записываем по ссылке от $i каждый $item['item']
последним из [11, 12, 13]
будет 13

второй проход foreach ($allList as $el)
его и вернет

другими словами
вы объявили переменную $i
и записали в нее - ссылку
вот здесь
foreach ($allList as &$i)

и далее и будет происходить запись - по ссылке

то есть вы сделали вот что:

Код: Выделить всё

$str = 'Начало';

$a = &$str; // берем ссылку на строку
print_r($a);

$a = 'Изменение'; // меняем оригинальную строку, потому что в $a - ссылка
print_r($a);

Это все так, но строго говоря тут(foreach ($item['item'] as $i)) i должна реинициализироваться.
Потому что на ванильном тесте это как-то более менее читается, а вот в реальном коде, выловить фичу(а на деле баг) оказывается не так просто и быстро
skynin
Сообщения: 400
Зарегистрирован: 2017.12.12, 10:09

Re: Баг php ?

Сообщение skynin »

-- Это все так, но строго говоря тут(foreach ($item['item'] as $i)) i должна реинициализироваться.

не должна. Она объявлена выше. все соответствует логике php по видимости и области действия переменных

есть ЯП у которых да, область действия переменных объявленных в операторе for ограничена этим оператором.

но тогда нужен дополнительный код при выходе по break, если нам интересно на каком элементе был превран цикл.

-- а вот в реальном коде
не надо писать опасного, грязноватого кода.
есть много бест практик на эту тему, который автоматически застраховывает от таких проблем
1. что именно в массиве? вот и назовите $eachFoo, $itemFoo
2. не используйте повторно переменные для итерации в пределах файла, независимо от их области действия, чтобы при рефакторинге не появилась такая ошибка
3. разбивайте код на мелкие функции-методы (в данном случае модифицирующий foreach был бы в другой функции)
4. не используйте ссылочные переменные без надобности
5. называйте ссылочные переменные по особому, с каким-то префиксом или суффиксом.

-- выловить фичу(а на деле баг) оказывается не так просто и быстро
сейчас идет обсуждение другого опасного синтаксиса на эту теме

Код: Выделить всё

function foo(&args) { ... }

foo($myArray); // здесь никак не видно что мы передали по ссылке
думают как обязать указывать явно

чтобы не было такого

Код: Выделить всё

// была безопасная
function foo(args) { ... }
// стала опасная
function foo(&args) { ... }
а вызывающий код и "не знает" об этом ничего. и тем более о том что там внутри ему стали портить $myArray

и никаких бэст практик тут придумать не получится...
Не желайте странного, и не будет у вас головной боли чтобы достичь этого странного.
Тем более что окажется что оно вам и не нужно было, странное это.
Ответить