Blog » Интересное в PHP: ловушка со строковыми ключами массива и вызовом array_shift
Как вы думаете, что напечатает следующий код?
// Заполняем массив (значения такие же, как и ключи)
foreach(array('07', '08', '09', '10') as $n){
$a[$n] = $n;
}
// Убираем первый элемент массива
array_shift($a);
// Выводим значение элемента с ключом '10'
echo $a['10'];
Если вам, как и мне, кажется, что в элементе массива $a с ключом '10' должна быть строка со значением "10", то вы, к сожалению, неправы. Зато теперь имеет смысл читать эту статью дальше.
На самом деле, к моменту исполнения оператора 'echo', ключа '10' уже не будет в массиве. Об этом нам и сообщит PHP при отработке скрипта (только если бит E_NOTICE активен в error_reporting):
Notice: Undefined index: 10 in E:\string-keys-caveat.php on line 8
Куда же делся нужный нам элемент? Давайте, с помощью var_dump($a), посмотрим на содержимое массива и попробуем понять что произошло.
Вот что мы имеем в массиве $a после вызова array_shift($a):
array(3) {
["08"] => string(2) "08"
["09"] => string(2) "09"
[0] => string(2) "10"
}
Ключа '10' действительно нет. Зато, значение "10" есть в элементе с ключом '0' и как раз это значение мы ожидали увидеть напечатанным при исполнении echo $a['10']. Поскольку значения элементов массива у нас были уникальными и эквивалентными ключам, то можно заключить, что ключ '0', это бывший ключ '10'.
Хорошо, но сразу же после цикла ключ '10' просто обязан быть в массиве, правда? Сейчас запустим var_dump($a) сразу же после foreach, но до array_shift и проверим:
array(4) {
["07"] => string(2) "07"
["08"] => string(2) "08"
["09"] => string(2) "09"
[10] => string(2) "10"
}
Верно, искомый ключ на месте, однако одной чертой он отличается от остальных ключей массива. Все ключи строковые, а он — нет. Хотя мы и передавали "10" как строку, PHP сконвертировал её в число. Это случилось потому, что "10" это стандартное представление числа, в отличии от "07", "08" и "09". Об этом даже в документации говорится:
Key может быть либо integer, либо string. Если ключ - это стандартное представление integer, он так и будет интерпретироваться (т.е. "8" будет восприниматься как 8, тогда как "08" будет интерпретироваться как "08").
http://www.php.net/manual/ru/language.types.array.php
Обычно, это преобразование остаётся незамеченным, ведь к числовым ключам можно обращаться так же как и к строковым (echo $a['10']) и программист остаётся в неведении о существовании этой ловушки, которая срабатывает только если массив будет неявно переиндексирован. Как раз последнее и случается при вызове функции array_shift — она не просто удаляет первый элемент массива, но и изменяет числовые ключи массива так, чтобы они шли по-порядку (т.е. "0, 1, 2..."). Потому-то у нас после вызова array_shift($a) элемент массива '10' стал элементом '0', ведь он был единственным элементом с числовым ключом.
Ну вот, теперь вы узнали ещё об одной причуде языка PHP. Как по мне, то код размещённый в самом начале этой статьи можно использовать как задачу по PHP, включать в разные тесты и иногда задавать на собеседованиях.
PS: здесь, правда, остаётся один вопрос — как же заставить строку похожую на число остаться именно строкой в качестве ключа массива (никакие $a[(string) "10"] = 'hi!' не помогают). Понимаю, что такое не часто бывает нужно, а когда нужно, то можно найти другие решения, но интересно жутко.
На этой странице еще нет комментариев.