Урок по треку PHP 8 мин

Массивы PHP изнутри

Упорядоченная хеш-таблица, ключи, copy-on-write, ссылки в foreach, функции массивов.

Массив в PHP один тип на все случаи: список, словарь, стек, очередь, множество. На собесе спрашивают, как он устроен, как приводятся ключи и где ловушки со ссылками.

Устройство

Массив это упорядоченная хеш-таблица (HashTable): элементы лежат в порядке вставки, а хеш-индекс позволяет найти элемент по ключу в среднем за O(1). Если ключи идут подряд с нуля, используется компактный packed-режим без хеш-части.

Следствия:

  • foreach всегда идёт в порядке добавления;
  • isset($a[$k]) и array_key_exists работают за O(1), а in_array за O(n);
  • множество делают ключами: $seen[$x] = true.

Ключи

Ключом бывает только int или string. Остальное приводится:

$a = [1 => 'a', "1" => 'b', true => 'c', 1.7 => 'd'];
count($a); // 1: всё попало в ключ 1
$a["05"] = 'x'; // строка "05" остаётся строкой

Автоключ $a[] = ... берёт максимальный целый ключ плюс один.

Копирование и ссылки

Массивы имеют семантику значения, но копируются лениво (copy-on-write): $b = $a и передача в функцию бесплатны, пока никто не меняет массив. Объекты внутри массива не копируются: копируются дескрипторы.

Ловушка foreach по ссылке:

$arr = [1, 2, 3];
foreach ($arr as &$v) {}
foreach ($arr as $v) {}   // перезаписывает последний элемент
print_r($arr);            // [1, 2, 2]

После цикла по ссылке всегда unset($v).

Объединение

array_merge([1, 2], [3, 4, 5]); // [1, 2, 3, 4, 5]: числовые ключи перенумерованы
[1, 2] + [3, 4, 5];             // [1, 2, 5]: только отсутствующие ключи
[...$a, ...$b];                 // как array_merge (строковые ключи с PHP 8.1)

Для строковых ключей array_merge берёт последнее значение, а + первое.

Функции на каждый день

  • array_map, array_filter (сохраняет ключи!), array_reduce, array_column, array_combine.
  • array_keys, array_values, array_flip, array_key_first, array_key_last.
  • array_unique, array_diff, array_intersect и их _key-варианты (быстрее, по хешу).
  • array_slice, array_splice, array_chunk.
  • array_is_list (8.1): ключи 0..n-1 по порядку.

После array_filter или unset массив может стать «дырявым», и json_encode выдаст объект вместо списка. Лечится array_values.

Сортировки

sort (значения, ключи сбрасываются), asort (значения, ключи сохраняются), ksort (по ключам), usort, uasort, uksort (свой компаратор), r-варианты по убыванию. С PHP 8.0 все сортировки стабильны. Компаратор удобно писать через <=>:

usort($users, fn($a, $b) => [$b->score, $a->name] <=> [$a->score, $b->name]);

Производительность

  • array_shift на большом списке O(n): перенумерация ключей. Для очередей SplQueue или указатель.
  • in_array в цикле даёт O(n²): заменяйте на isset по ключам.
  • Миллион элементов занимает десятки мегабайт: большие данные обрабатывайте генераторами.

Частые ошибки

  • foreach по ссылке без unset.
  • array_merge в цикле (каждый раз копия): собирайте массив массивов и делайте один array_merge(...$parts).
  • Числовые строки-ключи, которые неожиданно становятся int.
  • in_array без строгого режима.

Что спросят на собесе

  • Как устроен массив PHP и какова сложность операций?
  • Чем array_merge отличается от +?
  • Что будет после foreach по ссылке?
  • Как приводятся ключи массива?
  • Когда массив реально копируется?

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.