Какие бывают ключи
Бирки в гардеробе PHP бывают только двух видов: целое число или строка. Номерок 8 и бирка с именем 'anna' — обычные ключи. А что будет, если повесить вещь под биркой '8' — строкой из одной цифры?
Давайте посмотрим, какие бирки PHP повесит на самом деле:
<?php
$seats = [];
$seats['8'] = 'Anna';
$seats[8] = 'Boris';
$seats['08'] = 'Vera';
echo count($seats), ' ', $seats['8'], "\n"; // 2 Boris
echo implode(' ', array_keys($seats)), "\n"; // 8 08Разберём по строкам:
$seats['8'] = 'Anna';пишет в ключ8: строку'8'PHP превращает в число.$seats[8] = 'Boris';пишет в тот же ключ8и заменяет Анну Борисом.$seats['08'] = 'Vera';создаёт новый ключ: строка'08'остаётся строкой.countдаёт2, а$seats['8']читает ключ8и печатаетBoris.- Функция
array_keysвозвращает список всех ключей массива, аechoпечатает его черезimplode:8 08.
Во что превращается ключ
Ключом может быть только целое число или строка. PHP приводит значение другого типа к одному из них, а некоторые строки — к числу.
Строка с записью целого числа
Если строка выглядит ровно так, как PHP пишет целое число, она становится числом: '8' превращается в 8, '-3' — в -3. Поэтому $a['8'] и $a[8] — один и тот же ключ.
Остальные строки
Все остальные строки остаются строками, даже если похожи на число:
'08'— с ведущим нулём;'+8'— со знаком плюс;'8.5'— с дробной частью;' 8'— с пробелом впереди.
true, false, дробь и null
Значения других типов PHP приводит так:
trueстановится ключом1, аfalse— ключом0;- дробное число теряет дробную часть:
$a[8.7]пишет в ключ8и выдаётDeprecated: Implicit conversion from float 8.7 to int loses precision; nullстановится пустой строкой'', а с PHP 8.5 такая запись тоже выдаётDeprecated.
Deprecated — уведомление: так писать не стоит, и будущая версия PHP может это запретить. Но пока скрипт работает дальше. Дробные и null-ключи лучше не использовать вовсе.
Следующий индекс
Как вы уже знаете, $a[] = … вешает элемент на крючок после самого большого номера. Уточним правило: PHP берёт самый большой целый ключ из всех, что были в массиве, и прибавляет к нему единицу.
$queue = [10, 20, 30];
unset($queue[2]);
$queue[] = 40;
echo implode(' ', array_keys($queue)), "\n"; // 0 1 3
$codes = ['x' => 5];
$codes[] = 6;
echo implode(' ', array_keys($codes)), "\n"; // x 0Разберём оба случая:
- После
unset($queue[2])ключа2нет, но PHP помнит, что он был. Поэтому40получает ключ3, а не2. - В
$codesнет ни одного целого ключа, и первый[]получает ключ0.
Здесь гардероб из картинки и PHP расходятся. Живой гардеробщик выдал бы освободившийся номерок 2 следующему гостю, а PHP номера не возвращает и продолжает счёт.
Если самый большой целый ключ отрицательный, например -5, следующий получит -4. Так PHP считает начиная с версии 8.3.
Ключи и значения отдельно
array_keys
array_keys($queue) возвращает новый список из ключей массива: [0, 1, 3].
array_values
array_values($queue) возвращает новый список из значений, пронумерованный заново с 0: [10, 20, 40]. Так убирают дыры в номерах после unset.
array_is_list
array_is_list($queue) проверяет, список ли это: ключи должны идти подряд 0, 1, 2 и так далее.
$values = array_values($queue);
echo implode(' ', array_keys($values)), "\n"; // 0 1 2
var_dump(array_is_list($queue)); // bool(false)
var_dump(array_is_list($values)); // bool(true)В $queue ключи 0, 1 и 3, поэтому это уже не список. После array_values ключи снова идут подряд.
Один тип под капотом
На собеседовании часто спрашивают, чем индексный массив отличается от ассоциативного под капотом. Ответ: для вашего кода — ничем, тип массива в PHP один. Его устройство называют упорядоченной хеш-таблицей: это таблица, которая быстро находит значение по ключу и помнит порядок добавления.
Списком массив делают только его ключи 0..n-1, то есть от 0 до числа элементов минус один, и array_is_list проверяет именно их. Из этого следует всё, что вы видели в теме:
- ключи — только
intилиstring, и'8'с8— один ключ; - порядок обхода — порядок добавления;
unsetномера не сдвигает: после удаления из начала или середины в номерах остаётся дыра.
Частая ошибка
Из списка удалили элемент, а потом прошли по нему циклом for с индексом:
$tasks = ['wash', 'cook', 'read'];
unset($tasks[0]);
for ($i = 0; $i < count($tasks); $i++) {
echo $tasks[$i], "\n"; // ✗
}Warning: Undefined array key 0После unset в списке ключи 1 и 2, а count равен 2. Цикл проходит $i от 0 до 1: на $tasks[0] PHP выдаёт предупреждение и печатает пустую строку, а потом cook. Задача read под ключом 2 не печатается вовсе. Обходите такой массив через foreach или перенумеруйте его: $tasks = array_values($tasks);.
Главное
- Ключ массива —
intилиstring:'8'становится8,true—1, а'08'остаётся строкой. $a[]берёт самый большой целый ключ из всех, что были в массиве, плюс один;unsetномер не освобождает.array_keysиarray_valuesвозвращают ключи и значения новыми списками, аarray_is_listпроверяет, что ключи идут подряд с0.- Тип массива в PHP один — упорядоченная хеш-таблица; списком массив делают только ключи
0..n-1.