Массивы в PHP 8.x: короткое практическое руководство
PHP-массивы — это на самом деле упорядоченные хеш-таблицы, и от этого зависит поведение сортировки, слияния и работы с ключами. Разбираем приёмы, которые ломаются чаще всего.

Массив в PHP — не то, что называют массивом в C или Java. Это единая структура: упорядоченная хеш-таблица, где ключами служат строки или целые числа. Отсюда растут почти все неожиданности: почему array_merge перенумеровывает элементы, а оператор + — нет; почему после unset в цикле индексы «разъезжаются»; почему in_array иногда находит то, чего вы не искали. Разбираем приёмы работы с массивами в PHP 8.1–8.3, которые чаще всего подводят.
Ключи: числа, строки и тихие приведения
PHP молча приводит ключи к каноническому виду. Строка "7" станет целым 7. Строка "07" — останется строкой. true превратится в 1, null — в пустую строку "". Это источник коллизий: два разных на вид ключа схлопываются в один.
<?php
// PHP 8.x
$data = [];
$data["7"] = "строковый семь";
$data[7] = "числовой семь"; // перезаписывает предыдущий
$data[true] = "true"; // ключ 1
$data[null] = "null"; // ключ ""
var_dump(array_keys($data));
// int(7), int(1), string(0) ""
Практический вывод: если ключи приходят извне (из формы, JSON, базы) и вам важно различать "07" и 7 — не используйте их как ключи массива напрямую. Держите их в значениях либо приводите к строке явно и следите, чтобы приведение не отменил сам PHP.
Массив в PHP — это не список и не словарь по отдельности, а гибрид. Пока вы помните, что под капотом хеш-таблица с сохранением порядка вставки, поведение перестаёт удивлять.
Слияние: array_merge против оператора +
Две операции выглядят похоже, но ведут себя по-разному, и выбор не той — классическая ошибка.
array_merge($a, $b)— строковые ключи из$bперезаписывают ключи$a, а числовые ключи перенумеровываются заново от нуля.- Оператор
$a + $b— берёт значение из$aпри совпадении ключа (то есть левый операнд «главнее»), числовые ключи сохраняются, ничего не перенумеровывается.
<?php
$first = [0 => "a", 1 => "b"];
$second = [0 => "c", 2 => "d"];
var_dump(array_merge($first, $second));
// [0 => "a", 1 => "b", 2 => "c", 3 => "d"] — ключи сброшены
var_dump($first + $second);
// [0 => "a", 1 => "b", 2 => "d"] — ключ 0 остался от $first
Правило: для списков (сквозная нумерация, порядок важен) — array_merge. Для карт значений по числовым ключам, где ключ несёт смысл (например, id) — оператор +, но помните, что при конфликте выигрывает левый массив, а не правый.
Изменение массива в цикле
Удалять элементы из массива, по которому идёте через foreach, можно, но с оговорками. foreach работает по внутренней копии, если вы не берёте элемент по ссылке. Проблема возникает, когда берёте &$value и забываете разорвать ссылку.
<?php
$numbers = [1, 2, 3];
foreach ($numbers as &$value) {
$value *= 10;
}
unset($value); // обязательно: иначе $value остаётся ссылкой на последний элемент
foreach ($numbers as $value) {
// без unset() выше здесь последний элемент затёрся бы
}
var_dump($numbers); // [10, 20, 30], а не [10, 20, 20]
Забытый unset($value) после цикла по ссылке — одна из самых живучих ошибок в PHP. Второй foreach начинает писать в ту же ячейку, и последний элемент «дублируется».
Сравнение и поиск: строгий режим по умолчанию
in_array и array_search по умолчанию сравнивают нестрого. До PHP 8 это приводило к тому, что in_array(0, ["a", "b"]) возвращал true из-за старых правил приведения строки к числу. В PHP 8 логика сравнения строки и числа изменилась, но нестрогое сравнение всё ещё способно удивить — например, "1" == 1 истинно.
Всегда передавайте третий аргумент true для строгого сравнения, если тип имеет значение:
<?php
$haystack = ["1", "2", "3"];
var_dump(in_array(1, $haystack)); // true — нестрого, "1" == 1
var_dump(in_array(1, $haystack, true)); // false — строго, int != string
Функции, которые заменяют ручные циклы
Большую часть обработки массивов PHP закрывает встроенными функциями. Они читаются лучше цикла и реже содержат ошибку индексации.
| Задача | Функция | Что вернёт |
|---|---|---|
| Преобразовать каждый элемент | array_map | новый массив той же длины |
| Отфильтровать по условию | array_filter | подмножество, ключи сохраняются |
| Свернуть в одно значение | array_reduce | скаляр или структуру |
| Достать один столбец | array_column | плоский массив значений |
| Собрать карту по ключу | array_column($a, null, 'id') | массив, индексированный по id |
Тонкость: array_filter сохраняет исходные ключи. Если после фильтрации нужен список с нуля, оберните результат в array_values, иначе JSON-сериализация превратит список в объект из-за дырок в индексах.
<?php
$users = [
["id" => 10, "name" => "Анна", "active" => true],
["id" => 11, "name" => "Борис", "active" => false],
["id" => 12, "name" => "Вера", "active" => true],
];
// только активные, ключи сброшены в 0,1,...
$active = array_values(array_filter($users, fn($u) => $u["active"]));
// карта id => имя
$names = array_column($users, "name", "id");
// [10 => "Анна", 11 => "Борис", 12 => "Вера"]
Сортировка: какая функция сохраняет ключи
Семейство сортировок в PHP большое, и половина ошибок — от выбора не той функции. Различаются они по двум признакам: сохраняют ли связь ключ-значение и по чему сортируют.
sort/rsort— сбрасывают ключи, переиндексируют. Для списков.asort/arsort— сортируют по значениям, ключи сохраняют. Для карт.ksort/krsort— сортируют по ключам.usort— своя функция сравнения, ключи сбрасываются.uasort— своя функция, ключи сохраняются.
Все они меняют массив на месте и возвращают bool, а не отсортированный массив. Запись $sorted = sort($arr) — ошибка: в $sorted попадёт true, а сам $arr изменится.
<?php
// сортировка массива объектов по полю
usort($users, fn($a, $b) => $a["id"] <=> $b["id"]);
// оператор <=> (spaceship) вернёт -1, 0 или 1
Что делать дальше
Если работаете с большими массивами и упираетесь в память — посмотрите на генераторы (yield) вместо построения полного массива в памяти: это отдельная тема, здесь мы её не разворачивали. Для гарантий типов внутри коллекций изучите типизированные свойства и readonly-классы PHP 8.1+ — они дисциплинируют структуру данных лучше, чем ассоциативные массивы. И держите под рукой раздел документации по функциям массивов: их около сотни, и почти под любую задачу уже есть готовая.
Проектная работа: От проблемы до защиты: продукт, этапы, вопросы комиссии
Спросить за 15 ₽Источники: PHP Manual: Array Functions, PHP Manual: Arrays
Частые вопросы
У меня PHP 7.4, а не 8.x — приёмы сработают?
Почему после array_filter массив в JSON стал объектом, а не массивом?
Почему последний элемент массива продублировался после двух циклов foreach?
Как объединить два ассоциативных массива, чтобы значения второго перезаписали первый?
Почему $sorted = sort($arr) вернул true вместо отсортированного массива?
Как отсортировать массив ассоциативных массивов по вложенному полю?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.