Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 17 · С нуля → практика

Массивы: списки, словари и выбор операции

От органайзера с ключами и значениями до корзины, импорта и ответа API: запись, чтение, изменение, обход, копирование и выбор операции.

Обычный массив: один список покупок вместо отдельных переменных

Покупатель выбрал чай и кофе. Нужно сохранить список, прочитать нужный элемент, затем добавить какао. Массив — упорядоченный набор пар «ключ → значение». У простого списка ключи PHP назначит сам: 0 и 1. Сначала оба значения — обычные строки; вложенных массивов здесь ещё нет.

Список $shopping
  1. Ключ 0 → строка Чай.
  2. Ключ 1 → строка Кофе.
  3. $shopping[0] выбирает ячейку с ключом 0 → Чай.
  4. echo показывает выбранную строку.
СимволКак читать
[ ... ] справа от =Создать массив из перечисленных элементов.
Запятая между значениямиЗакончить один элемент и начать следующий.
$shopping[0]Прочитать значение по ключу 0 у массива shopping. Ноль — первый автоматический ключ.
[] без значенийПустой массив: элементы пока отсутствуют.
Создать список и прочитать каждый выбранный товарСамостоятельный пример
<?php
$shopping = ['Чай', 'Кофе'];
echo 'Первый товар: ', $shopping[0], "\n";
echo 'Второй товар: ', $shopping[1], "\n";
Ожидаемый результат
Первый товар: Чай
Второй товар: Кофе

shopping хранит весь массив. Выражения с [0] и [1] возвращают отдельные строки для echo. Аналогия с органайзером помогает найти ячейку, но ключ не обязан быть номером физического места: PHP допускает и текстовые ключи.

Изменить известную ячейку можно присваиванием по её ключу. Пустые скобки слева $shopping[] = ... означают добавить новый элемент с автоматическим целым ключом. Это другая запись, чем пустой массив [] справа.

Исправить выбор и добавить третий товарСамостоятельный пример
<?php
$shopping = ['Чай', 'Кофе'];
$shopping[1] = 'Какао';
$shopping[] = 'Печенье';
echo $shopping[0], "\n";
echo $shopping[1], "\n";
echo $shopping[2], "\n";
Ожидаемый результат
Чай
Какао
Печенье

Ключ 1 уже существовал, поэтому Кофе заменён на Какао. Добавление создало ключ 2. Для этого плотного списка ключи идут подряд; после удаления или явных числовых ключей нельзя всегда угадывать следующий индекс по числу элементов.

Одна карточка: именованные поля вместо номеров

У товара нужно сохранить разные сведения: название, цену и остаток. В списке одинаковых вещей ключ 0 удобен, а у карточки понятнее подписи title, price и stock. Стрелка => связывает ключ с его значением. И ключи, и строковые значения записаны в кавычках; цена и количество — числа.

Карточка чая и изменение остаткаСамостоятельный пример
<?php
$product = ['title' => 'Чай', 'price' => 150, 'stock' => 3];
echo $product['title'], ': ', $product['price'], ' руб.', "\n";
$product['stock'] = 2;
echo 'После выдачи осталось: ', $product['stock'], ' уп.', "\n";
Ожидаемый результат
Чай: 150 руб.
После выдачи осталось: 2 уп.

title, price и stock — ключи одного массива, не самостоятельные переменные $title и $price. По ключу stock изменено только число 3. У product[0] здесь нет значения: карточка создана со строковыми ключами.

У двух товаров могут быть одинаковые поля, но разные значения. Чтобы хранить несколько карточек вместе, значениями внешнего массива станут внутренние массивы. Это следующая глава о вложенности. Сначала научимся автоматически проходить обычный набор.

foreach ($stockByTitle as $title => $stock) означает: взять очередную пару; её ключ поместить в title, значение — в stock; выполнить тело. Затем повторить для следующей пары. Имена локальных переменных обхода выбирает автор; имена ключей массива от этого не меняются.

Получить ведомость из словаря остатковСамостоятельный пример
<?php
$stockByTitle = ['Чай' => 3, 'Кофе' => 2];
$total = 0;
foreach ($stockByTitle as $title => $stock) {
    echo $title, ': ', $stock, ' уп.', "\n";
    $total = $total + $stock;
}
echo 'Всего на полке: ', $total, ' уп.', "\n";
Ожидаемый результат
Чай: 3 уп.
Кофе: 2 уп.
Всего на полке: 5 уп.

Первый проход: title = Чай, stock = 3, total меняется 0 → 3. Второй: Кофе, 2, total меняется 3 → 5. После последнего элемента foreach завершается. Итоговый echo стоит за циклом и выполняется один раз.

СостояниеЧто происходит
До foreachtotal = 0; пока ничего не накоплено.
После пары Чай → 3В выводе первая строка; total = 3.
После пары Кофе → 2В выводе вторая строка; total = 5.
Пустой массивТело не выполняется, total остаётся 0 и выводится итог нулевого набора.

Форма foreach ($shopping as $title) берёт только значения. Она подходит списку, если ключи не нужны. foreach не требует непрерывных индексов; он обходит реально существующие элементы в порядке массива.

Прочитать значение или изменить исходный массив

Магазин получил по одной дополнительной упаковке каждого товара. Обычная переменная $stock в foreach получает значение; её изменение само не записывается в исходный массив. Для понятной записи назад используем текущий ключ $title.

Обновить остатки и затем прочитать изменённый наборСамостоятельный пример
<?php
$stockByTitle = ['Чай' => 3, 'Кофе' => 2];
foreach ($stockByTitle as $title => $stock) {
    $stockByTitle[$title] = $stock + 1;
}
foreach ($stockByTitle as $title => $stock) {
    echo $title, ': ', $stock, ' уп.', "\n";
}
Ожидаемый результат
Чай: 4 уп.
Кофе: 3 уп.

Первый цикл использует ключ как адрес записи: stockByTitle[Чай] получает 4, stockByTitle[Кофе] получает 3. Второй заново читает уже изменённые значения. Мы изменяем существующие значения, сохраняя состав ключей.

Существует и перебор по ссылке через &; он подробно разобран ниже. Его остаточная связь с последним элементом стала причиной повреждения последнего значения в обсуждении SO. Здесь запись по ключу делает изменение явным. После цикла по ссылке требуется разорвать связь переменной через unset, прежде чем повторно использовать её для обычного обхода.

Создать массив: [], array(), запятые и пустой набор

Для списка доставки нужно хранить несколько названий. Сначала просто создадим набор и прочитаем один элемент. Здесь ещё нет ни цикла, ни функции для обработки данных.

Запись списка и чтение по номеру
  1. ["Чай", "Кофе"] → два элемента
  2. Первому назначен ключ 0, второму — 1
  3. $products[1] → значение «Кофе»
Один и тот же список в двух вариантах записиСамостоятельный пример
<?php
$products = ['Чай', 'Кофе'];
$sameProducts = array('Чай', 'Кофе');
echo $products[1], "\n";
echo $sameProducts[1], "\n";
Ожидаемый результат
Кофе
Кофе

[] справа от = создают массив; запятая отделяет элементы. Array() — другая запись той же конструкции, встречающаяся в старых проектах. Скобки после $products читают элемент. Ключ 1 обозначает здесь второй элемент: нумерация нового списка начинается с нуля.

Список можно начать без единого элементаСамостоятельный пример
<?php
$products = [];
$products[] = 'Чай';
$products[] = 'Кофе';
echo $products[0], "\n";
echo $products[1], "\n";
Ожидаемый результат
Чай
Кофе

Первая строка создаёт пустой массив. В последующих строках пустые [] слева от = означают добавление с очередным целочисленным ключом. Это удобно, когда товары появляются постепенно. Всегда явно начинайте накопитель с []: по коду сразу видно его назначение.

В многострочной записи допустима запятая после последнего элемента. Она не создаёт пустой элемент. Пустая строка "", null и пустой массив [] — разные значения. Массив может содержать числа, строки, логические значения и вложенные массивы; одинаковый тип элементов сам собой не проверяется.

Именованные поля: ключ — это адрес, а значение — содержимое

Одна карточка товара
  1. Ключ name → строка «Чай»
  2. Ключ stock → число 12
  3. Ключ available → логическое true
Выбираем поле карточки по его имениСамостоятельный пример
<?php
$product = [
    'name' => 'Чай',
    'stock' => 12,
    'available' => true,
];
$field = 'stock';
echo $product['name'], "\n";
echo $product[$field], "\n";
Ожидаемый результат
Чай
12

=> связывает ключ и значение внутри массива. При чтении ['name'] ключ написан буквально, а [$field] берёт ключ из переменной. Не пишите ['$field']: тогда PHP будет искать именно текст $field. В обычном PHP-выражении вне строки запись [name] без кавычек означала бы константу; неопределённая константа в PHP 8 вызывает Error.

У простой подстановки внутри двойных кавычек есть отдельное правило: "$product[stock]" допустимо. Более явно писать "{$product['stock']}". Оба варианта и границы синтаксиса разобраны в интерполяции. Старый доступ $product{0} через фигурные скобки удалён в PHP 8. Для чтения элемента пишите $product[0]; фигурные скобки вокруг подстановки в строке выполняют другую роль.

Внутри одного массива допустимы и строковые, и целочисленные ключи. На практике договор о структуре обычно проще: либо список карточек, либо одна карточка с полями, либо словарь по артикулу. Читателю и следующему участку программы важно понимать, какая модель используется. Когда ключ действительно задан константой разобрано отдельно.

Изменить, добавить, удалить: три разных действия

Работа с остатком
  1. В stock лежит 12
  2. Присвоить stock = 9 → заменить значение
  3. Присвоить reserved = 3 → добавить поле
  4. unset(reserved) → убрать поле целиком
Записываем остаток после продажиСамостоятельный пример
<?php
$product = ['name' => 'Чай', 'stock' => 12];
$product['stock'] = 9;
$product['reserved'] = 3;
echo $product['stock'], ' / ', $product['reserved'], "\n";
unset($product['reserved']);
echo $product['name'], "\n";
Ожидаемый результат
9 / 3
Чай

Присваивание по существующему ключу заменяет его значение. По новому — добавляет пару. Unset — языковая конструкция удаления: здесь она удаляет одну пару; unset($product) убрал бы переменную целиком. Удаление не означает запись нуля или null.

Не рассчитывайте на случайное превращение переменной в массив. Добавление через [] к null или необъявленной переменной может создать массив, но это скрывает забытое начало накопителя. Превращение false в массив устарело с PHP 8.1. Добавлять через [] к строке нельзя. Явное $items = [] избавляет от этой неоднозначности.

Поле отсутствует, равно null или равно нулю: три разных решения

Склад прислал stock = 0: товар закончился, и карточка должна честно показать ноль. Поле delivery = null означает, что срок доставки ещё не рассчитан. Если обязательного stock вообще нет, карточку нельзя считать проверенной. Эти состояния требуют разных сообщений; проверка «пусто ли» объединит их слишком рано.

ПроверкаЧто получаетЧто возвращает
array_key_exists('stock', $product)Ключ и конкретный массив.true, если ключ существует, включая значение null; иначе false. Вложенные массивы не обходит.
isset($product['stock'])Обращение к элементу.true, если элемент существует и не null; ноль допустим.
empty($product['stock'])Обращение к элементу.true при отсутствии или ложном значении, в том числе 0 и "0".
$product['title'] ?? 'Без названия'Необязательная подпись и резерв.Значение элемента либо резерв при отсутствии/null; пустая строка не заменяется.
Для product = [stock → 0, delivery → null]
  1. Проверить ключ stock: он есть.
  2. Получить stock = 0 и показать фактический остаток.
  3. Проверить isset(delivery): false, потому что поле равно null.
  4. Показать осмысленную подпись «Срок доставки уточняется».
Выбрать сообщения по смыслу каждого поляСамостоятельный пример
<?php
$product = ['stock' => 0, 'delivery' => null];
if (array_key_exists('stock', $product)) {
    echo 'Остаток: ', $product['stock'], ' уп.', "\n";
} else {
    echo "Карточка не проверена: склад не передал остаток\n";
}
if (isset($product['delivery'])) {
    echo 'Доставка: ', $product['delivery'], "\n";
} else {
    echo "Срок доставки уточняется\n";
}
echo 'Название: ', $product['title'] ?? 'Без названия', "\n";
Ожидаемый результат
Остаток: 0 уп.
Срок доставки уточняется
Название: Без названия

Каждый bool используется в if и превращается в понятное сообщение. array_key_exists не проверяет, целое ли значение stock: по этому учебному договору присутствующий остаток уже целый. delivery существует, но null выбирает вторую ветвь. Для необязательного title применён резерв.

Не превращать отсутствие обязательного остатка в нольСамостоятельный пример
<?php
$product = ['title' => 'Чай'];
if (!array_key_exists('stock', $product)) {
    echo 'Нельзя подтвердить наличие товара ', $product['title'], ': нет поля stock', "\n";
} else {
    echo 'Остаток: ', $product['stock'], ' уп.', "\n";
}
Ожидаемый результат
Нельзя подтвердить наличие товара Чай: нет поля stock

! меняет false проверки наличия на true условия отказа. Прямое чтение stock выполнялось бы только в ветви, где ключ есть. Присваивание отсутствующему остатку значения 0 исказило бы смысл: данных нет, а не товар закончился.

Прямое чтение несуществующего ключа в PHP 8 выдаёт E_WARNING и даёт null. Проверка array_key_exists требует именно массива вторым аргументом, поэтому сначала подтверждают внешний тип. Ни isset, ни empty, ни ?? не проверяют права, схему всех полей или положительность числа. Подробные контракты: array_key_exists и isset/empty.

Ключи преобразуются: почему карточка с кодом 12 заменила другую

Поставщик дал один код строкой "12", другой числом 12. При записи в PHP-массив эти ключи совпадут, и более поздняя подпись заменит первую. Код "012" останется другой строкой. Прежде чем строить индекс каталога, согласуйте представление идентификаторов и решите, допустима ли повторная запись.

Входной ключКлюч массиваПоследствие
"12" и 12int 12Обращаются к одной ячейке; позднее значение заменяет раннее.
"012" и "+12"Строковые ключиНе превращаются в int 12.
true и falseint 1 и int 0Могут совпасть с уже существующими числовыми ключами.
nullПустая строкаСовпадает с ключом "".
floatint с отброшенной дробной частьюНеявная потеря дроби вызывает deprecation в современных PHP; не выбирайте дробный ключ.
Массив или объектНедопустимый типСовременный PHP выдаёт TypeError.
Увидеть итоговые подписи после столкновения ключейСамостоятельный пример
<?php
$labels = ['12' => 'Первая карточка', 12 => 'Обновлённая карточка', '012' => 'Карточка 012'];
foreach ($labels as $code => $label) {
    echo 'Код ', $code, ': ', $label, "\n";
}
Ожидаемый результат
Код 12: Обновлённая карточка
Код 012: Карточка 012

В результате действительно две строки, а не просто счётчик 2. Первая подпись исчезла при построении массива, потому что "12" превратился в int 12. Ключ "012" сохранился строкой и остался отдельно.

Если повтор кода считается ошибкой, обнаруживайте его до присваивания в индекс. Следующий массив содержит только уже согласованные строковые артикулы. Каждый элемент rows — карточка, то есть вложенный массив; путь row[sku] выбирает поле одной карточки.

Остановить запись дубликата и сохранить первую карточкуСамостоятельный пример
<?php
$rows = [
    ['sku' => 'TEA-01', 'title' => 'Чай'],
    ['sku' => 'COF-02', 'title' => 'Кофе'],
    ['sku' => 'TEA-01', 'title' => 'Другой чай'],
];
$index = [];
foreach ($rows as $row) {
    $sku = $row['sku'];
    if (array_key_exists($sku, $index)) {
        echo 'Повтор артикула отклонён: ', $sku, "\n";
        continue;
    }
    $index[$sku] = $row;
}
echo 'Сохранённая карточка TEA-01: ', $index['TEA-01']['title'], "\n";
Ожидаемый результат
Повтор артикула отклонён: TEA-01
Сохранённая карточка TEA-01: Чай

На третьей строке индекс уже содержит TEA-01, поэтому continue пропускает присваивание и первая карточка сохраняется. Это договор «принять первую и сообщить о дубликате». Для операции, которая должна принять весь файл или ничего, вместо частичного принятия нужен общий отказ.

Ключи — только int и string; значения могут иметь любой тип. Числовые и строковые ключи могут сосуществовать в одном массиве. Если исходный набор уже записан с повторными одинаковыми ключами, восстановить затёртые значения из этого массива невозможно.

После удаления номер не переиспользуется автоматически

Из очереди заявок удалили вторую и третью, затем добавили новую. Запись [] не ищет первую свободную позицию. Обычно она берёт следующий целый ключ после наибольшего ранее использованного числового ключа с момента создания или переиндексации. Поэтому после 0, 1, 2 новая заявка получит 3, хотя сейчас в массиве могла оставаться только заявка 0.

Числовой ключ и место в списке — разные вещи
  1. Начальные ключи: 0 → A, 1 → B, 2 → C.
  2. unset удаляет B и C; остаётся ключ 0.
  3. Добавление [] создаёт ключ 3 → D.
  4. array_values делает новый плотный список: 0 → A, 1 → D.
Сравнить исходные ключи и новый список для показаСамостоятельный пример
<?php
$queue = ['Заявка A', 'Заявка B', 'Заявка C'];
unset($queue[1], $queue[2]);
$queue[] = 'Заявка D';
foreach ($queue as $key => $request) {
    echo 'Ключ ', $key, ': ', $request, "\n";
}
$list = array_values($queue);
foreach ($list as $position => $request) {
    echo 'Позиция ', $position + 1, ': ', $request, "\n";
}
Ожидаемый результат
Ключ 0: Заявка A
Ключ 3: Заявка D
Позиция 1: Заявка A
Позиция 2: Заявка D

unset удаляет указанные элементы, но не сдвигает оставшиеся и не сбрасывает историю следующего индекса. array_values($queue) возвращает новый массив, сохраняя порядок значений и назначая ключи 0, 1. Внешний queue от этого вызова не изменяется; результат сохранён в list.

Следующий ключ определяется максимумом, а не последней вставкойСамостоятельный пример
<?php
$shelves = [6 => 'Полка A', 2 => 'Полка B'];
$shelves[] = 'Полка C';
foreach ($shelves as $number => $label) {
    echo 'Ключ ', $number, ': ', $label, "\n";
}
Ожидаемый результат
Ключ 6: Полка A
Ключ 2: Полка B
Ключ 7: Полка C

Хотя последним явным ключом записан 2, раньше уже использован 6; следующий назначен 7. Порядок обхода остался порядком добавления 6, 2, 7. Строковые ключи вида shelf-A не участвуют в расчёте следующего числового индекса.

Есть версионный крайний случай: с PHP 8.3 после единственного отрицательного ключа -3 автоматический следующий ключ здесь будет -2; в PHP 8.2 он был бы 0. Следующий опыт предназначен для 8.3+ и показывает адрес результата, а не только true проверки.

PHP 8.3+: сохранить последовательность отрицательных ключейСамостоятельный пример
<?php
$measurements = [-3 => 17];
$measurements[] = 18;
foreach ($measurements as $minute => $temperature) {
    echo 'Минута ', $minute, ': ', $temperature, ' °C', "\n";
}
Ожидаемый результат
Минута -3: 17 °C
Минута -2: 18 °C

Это локальный пример отрицательной шкалы времени, совместимый по этому результату с PHP 8.3+. Для реальных временных отметок надёжнее задавать ключ явно: внутренний счётчик массива не знает ни единицу времени, ни пропуски измерений.

Для PHP_INT_MAX уже нет следующего допустимого int: автоматическое добавление не является безграничным генератором идентификаторов. array_pop имеет собственную семантику удаления последнего элемента; не переносите на него все выводы об unset. array_values выбирают только для списка: у словаря переиндексация уничтожит значимые артикулы.

Массив внутри массива: читаем путь по одному шагу

Путь к названию из заказа
  1. $order → вся запись заказа
  2. ["items"] → список позиций
  3. [0] → первая карточка
  4. ["name"] → строка «Чай»
Одна и та же запись коротким путём и понятными промежуточными шагамиСамостоятельный пример
<?php
$order = [
    'number' => 17,
    'items' => [
        ['name' => 'Чай', 'quantity' => 2],
        ['name' => 'Кофе', 'quantity' => 1],
    ],
];
echo $order['items'][0]['name'], "\n";
$items = $order['items'];
$firstItem = $items[0];
echo $firstItem['name'], "\n";
Ожидаемый результат
Чай
Чай

Здесь структура записана руками, чтобы увидеть её устройство. В настоящем сервисе похожую структуру даст json_decode ответа API, запрос к базе или несколько полей формы. Каждый дополнительный [...] читает элемент результата предыдущего шага. Если промежуточное значение оказалось строкой или null, договор уже нарушен.

Переходы к источникам этих данных: JSON → проверка → многомерный массив, HTML-поля → массив запроса, строки из базы данных. Отдельный массив ещё не гарантирует правильные ключи и типы внутри.

Foreach: прочитать каждую пару без ручного подсчёта индексов

Проход по остаткам
  1. A17 → 3: присвоить $sku и $stock, выполнить тело
  2. B20 → 0: присвоить следующую пару, выполнить тело
  3. Пары закончились → продолжить после цикла
Ключ и значение доступны в теле циклаСамостоятельный пример
<?php
$stocks = ['A17' => 3, 'B20' => 0];
foreach ($stocks as $sku => $stock) {
    echo $sku, ': ', $stock, "\n";
}
echo "Готово\n";
Ожидаемый результат
A17: 3
B20: 0
Готово

Foreach берёт очередную пару. As отделяет исходный массив от переменных текущего прохода; $sku получает ключ, $stock — значение. Фигурные скобки ограничивают повторяемые команды. Если нужен только товар без ключа, достаточно foreach ($products as $product). Пустой массив не выполнит тело ни разу.

Посчитать новые остатки, не изменяя входной массивСамостоятельный пример
<?php
$stocks = ['A17' => 3, 'B20' => 0];
$afterDelivery = [];
foreach ($stocks as $sku => $stock) {
    $afterDelivery[$sku] = $stock + 5;
}
echo $stocks['A17'], "\n";
echo $afterDelivery['A17'], "\n";
Ожидаемый результат
3
8

Мы строим второй массив, сохраняя ключи. Переменная $stock в обычном проходе содержит значение, поэтому присваивание только ей не записало бы результат обратно. Такой отдельный результат удобен для сравнения «до/после». Когда данных слишком много для двух коллекций, выбирают иной способ обработки.

Почему не for от 0 до count? У словаря ключи могут быть строковыми, а после удаления у списка появляются пропуски. Foreach не предполагает непрерывную нумерацию. Выбор for, while и foreach нужен, когда повторение определяется числом шагов, условием или готовой коллекцией.

Порядок добавления, порядок ключей и сортировка — разные вещи

Числовой ключ не определяет положение в обходеСамостоятельный пример
<?php
$labels = [20 => 'Вторая полка', 10 => 'Первая полка'];
foreach ($labels as $id => $label) {
    echo $id, ': ', $label, "\n";
}
ksort($labels);
echo "После сортировки ключей\n";
foreach ($labels as $id => $label) {
    echo $id, ': ', $label, "\n";
}
Ожидаемый результат
20: Вторая полка
10: Первая полка
После сортировки ключей
10: Первая полка
20: Вторая полка

Первый проход сохраняет порядок вставки. Ksort изменяет исходный массив и упорядочивает его по ключам. Sort сортирует значения и перенумеровывает ключи; asort сохраняет связь ключа со значением. До выбора сортировки решите, можно ли потерять исходные идентификаторы.

Для пустого массива array_is_list вернёт true. Для непустого списка нужны целочисленные ключи 0, 1, 2… именно в таком порядке. Массив [1 => "B", 0 => "A"] содержит обе цифры, но списком в этом смысле не является. Это важно для формы JSON-ответа.

Разложить известную запись по переменным и подготовить строку отчёта

Строка импорта имеет заранее установленный порядок: артикул, название, количество. Вместо трёх отдельных чтений можно разложить её в переменные. [$sku, $title, $stock] = $row; использует [] слева от присваивания как разбор структуры, а не создание массива. Каждый элемент получает значение соответствующего числового ключа.

Получить три понятных имени из одной строки импортаСамостоятельный пример
<?php
$row = ['TEA-01', 'Чай', 3];
[$sku, $title, $stock] = $row;
echo 'Принять на склад: ', $sku, ' — ', $title, ', ', $stock, ' уп.', "\n";
[, , $onlyStock] = $row;
echo 'Для счётчика упаковок: ', $onlyStock, "\n";
Ожидаемый результат
Принять на склад: TEA-01 — Чай, 3 уп.
Для счётчика упаковок: 3

Первое присваивание читает ключи 0, 1, 2. Во второй записи первые две позиции пропущены пустыми местами, поэтому onlyStock получает только ключ 2. Порядок значения определяется договором строки, а не тем, какое имя переменной выбрано.

Для карточки с именами полей устойчивее разложение по ключам. Порядок записи полей тогда неважен. 'sku' => $sku слева означает «из поля sku получить значение в переменную sku». Именованное и позиционное разложение нельзя произвольно смешивать в одном списке.

Выбрать поля по именам при любом их порядкеСамостоятельный пример
<?php
$product = ['stock' => 3, 'title' => 'Чай', 'sku' => 'TEA-01'];
['sku' => $sku, 'title' => $title] = $product;
echo 'Этикетка: ', $sku, ' — ', $title, "\n";
echo 'Неизменённое поле остатка: ', $product['stock'], "\n";
Ожидаемый результат
Этикетка: TEA-01 — Чай
Неизменённое поле остатка: 3

sku и title выбраны независимо от того, что первым в product был stock. Исходный массив не удаляет прочитанные поля и не меняет их порядок. Результат — два отдельных значения для подписи.

Конструкция list($sku, $title, $stock) = $row; — альтернативная запись разложения. Она не является обычным вызовом функции. Числовой ключ можно указать явно: [2 => $stock] = $row;. В одной раскладке нельзя смешивать позиции без ключей и элементы с явно указанными ключами. Троеточие для сбора «остальных» элементов в такой левой части не поддерживается.

Поменять направление маршрута без потери одного городаСамостоятельный пример
<?php
$from = 'Москва';
$to = 'Тула';
echo 'Прямой маршрут: ', $from, ' → ', $to, "\n";
[$from, $to] = [$to, $from];
echo 'Обратный маршрут: ', $from, ' → ', $to, "\n";
Ожидаемый результат
Прямой маршрут: Москва → Тула
Обратный маршрут: Тула → Москва

Справа сначала строится массив из прежних значений [Тула, Москва], затем они присваиваются двум получателям слева. Поэтому исходное Москва не потерялось. Два последовательных присваивания from = to и to = from без промежуточной копии дали бы другой результат.

Отсутствующий нужный ключ вызывает предупреждение и даёт null, поэтому форма входа должна быть известна заранее. Попытка разложить скаляр не проверяет и не превращает его в правильную строку импорта: получатели окажутся null. Допустимы вложенные формы разложения и применение в foreach; каждый их уровень должен соответствовать реальному массиву. Для явного связывания с исходной ячейкой с PHP 7.3 применяется &, но это меняет договор мутации, как в главе ссылок.

Копия корзины: подготовить изменение, затем сравнить до и после

Покупатель меняет количество чая с 2 на 3. Прежде чем сохранить новую корзину, нужно показать разницу. Обычное присваивание массива создаёт другое значение массива: изменение обычного числового элемента копии не изменит исходный. Это удобно для черновика и сравнения.

Рассчитать разницу, сохранив исходный наборСамостоятельный пример
<?php
$original = ['TEA-01' => 2, 'COF-02' => 1];
$draft = $original;
$draft['TEA-01'] = 3;
echo 'Чая было: ', $original['TEA-01'], ' уп.', "\n";
echo 'Чая станет: ', $draft['TEA-01'], ' уп.', "\n";
echo 'Добавляется: ', $draft['TEA-01'] - $original['TEA-01'], ' уп.', "\n";
Ожидаемый результат
Чая было: 2 уп.
Чая станет: 3 уп.
Добавляется: 1 уп.

Вычисление разницы использует одновременно исходное 2 и новое 3. Само присваивание draft не сохраняет заказ в БД. Результат пока существует в текущем выполнении; дальнейшее подтверждение и запись — отдельные действия.

Если вместо этого написать $alias =& $original;, имена будут связаны. Тогда alias не является независимым черновиком: запись по одному имени видна по другому. Амперсанд не следует добавлять «для скорости».

Два имени показывают одну изменённую корзинуСамостоятельный пример
<?php
$original = ['TEA-01' => 2];
$alias =& $original;
$alias['TEA-01'] = 3;
echo 'Исходная корзина после записи через alias: ', $original['TEA-01'], ' уп.', "\n";
unset($alias);
echo 'После удаления имени alias: ', $original['TEA-01'], ' уп.', "\n";
Ожидаемый результат
Исходная корзина после записи через alias: 3 уп.
После удаления имени alias: 3 уп.

unset удаляет псевдоним, но не отменяет уже выполненную запись 3. Это связь имён PHP, а не веб-ссылка и не универсальный способ откатить изменение.

Независимость обычных элементов не означает глубокое клонирование произвольных значений. Объект внутри массива остаётся тем же объектом; явно ссылочный элемент может сохранить общую связь и после копирования контейнера. Эти два случая сохранены в следующем справочном разделе ссылок и объектов внутри массива. Внутренние оптимизации хранения не отменяют наблюдаемый договор.

Объединение наборов: выбрать правило для совпадающих ключей

Нужно соединить основные и дополнительные пункты меню. У списка важен порядок значений, поэтому удобно развернуть оба набора в новом массиве: [...$main, ...$extra]. Троеточие здесь разворачивает элементы внутри создаваемого массива; это не вызов функции и не разбор JSON.

Подготовить меню из двух готовых группСамостоятельный пример
<?php
$main = ['Главная', 'Каталог'];
$extra = ['Контакты'];
$menu = [...$main, ...$extra];
foreach ($menu as $index => $caption) {
    echo 'Пункт ', $index + 1, ': ', $caption, "\n";
}
Ожидаемый результат
Пункт 1: Главная
Пункт 2: Каталог
Пункт 3: Контакты

Новый menu содержит три строки с числовыми ключами 0, 1, 2. Исходные main и extra сохранены. Числовые ключи при таком объединении перенумеровываются; это подходит позициям меню, но может потерять смысл числовых ID.

У настроек другие ключи и задача: есть базовые timeout = 5 секунд и retries = 1 повтор, пользователь меняет только timeout на 12. При строковом совпадении распаковка берёт более позднее значение. Сам синтаксис распаковки массива существует с PHP 7.4, строковые ключи поддерживаются с PHP 8.1.

Наложить выбранные настройки поверх базовыхСамостоятельный пример
<?php
$defaults = ['timeout' => 5, 'retries' => 1];
$custom = ['timeout' => 12];
$settings = [...$defaults, ...$custom];
echo 'Ожидание ответа: ', $settings['timeout'], ' с', "\n";
echo 'Повторных попыток: ', $settings['retries'], "\n";
echo 'Базовое ожидание осталось: ', $defaults['timeout'], ' с', "\n";
Ожидаемый результат
Ожидание ответа: 12 с
Повторных попыток: 1
Базовое ожидание осталось: 5 с

Поздний timeout заменил 5 на 12 в новом settings; retries сохранился, потому что в custom нет такого ключа. defaults не изменён. Это подготовленные настройки, а не реально выполненный сетевой запрос.

ИнструментСовпадающий ключЧисловые ключи
[...$a, ...$b]Поздний строковый ключ заменяет ранний.Значения добавляются в список и перенумеровываются.
array_merge($a, $b)То же правило строковых ключей.Добавление с перенумерацией.
array_replace($a, $b)Позднее значение заменяет раннее по любому совпадающему ключу.Сохраняются как ключи, не превращаются в позиции списка.
$a + $bДля совпадающего ключа остаётся левое значение.Сохраняются ключи; это не склейка двух списков.

Ни распаковка, ни эти обычные операции сами не сливают глубоко внутренние настройки: совпадение контейнера заменяет либо сохраняет контейнер целиком по правилу инструмента. Для вложенной политики отдельно выбирают рекурсивную операцию и проверяют её договор. Распаковывать можно также Traversable, например генератор; неподходящий тип ключа итератора приводит к TypeError. Результат материализуется в памяти, поэтому это не замена потоковому обходу.

Массив приходит аргументом и возвращается готовым новым набором

На два товара пришло по 5 упаковок. Требуется подготовить новые остатки, сохранив старые для сверки. Тот же расчёт понадобится другому складу, поэтому создадим функцию addDelivery. Предпосылки уже разобраны: аргумент → параметр → return и обычный foreach.

ЗаписьДоговор
array $stocksПервый аргумент — массив «артикул → остаток». В этом опыте каждый остаток заранее проверен как неотрицательное целое.
int $unitsВторой аргумент — сколько упаковок добавить к каждой позиции. Одна величина для всех товаров, не общий объём поставки.
: arrayВозвратит один новый массив с теми же артикулами и пересчитанными остатками.
$result[$sku] = ...Запишет результат именно под исходным артикулом, сохранив связь товара с числом.
addDelivery([TEA-01 → 3, COF-02 → 0], 5)
  1. stocks получает весь массив; units получает 5.
  2. Создаётся отдельный пустой result.
  3. Для TEA-01 считается 3 + 5 → result[TEA-01] = 8.
  4. Для COF-02 считается 0 + 5 → result[COF-02] = 5.
  5. return отдаёт [TEA-01 → 8, COF-02 → 5] в ожидающее присваивание updated.
  6. Внешний цикл сравнивает updated с исходным stocks и готовит ведомость.
Использовать возвращённый массив для сверки до и послеСамостоятельный пример
<?php
declare(strict_types=1);
function addDelivery(array $stocks, int $units): array
{
    $result = [];
    foreach ($stocks as $sku => $stock) {
        $result[$sku] = $stock + $units;
    }
    return $result;
}
$stocks = ['TEA-01' => 3, 'COF-02' => 0];
$updated = addDelivery($stocks, 5);
foreach ($updated as $sku => $stock) {
    echo $sku, ': было ', $stocks[$sku], ', стало ', $stock, ' уп.', "\n";
}
Ожидаемый результат
TEA-01: было 3, стало 8 уп.
COF-02: было 0, стало 5 уп.

updated получил один целый массив после окончания функции. Внешний foreach читает новое значение, а выражение stocks[sku] берёт прежнее для того же артикула. Это подготовленная ведомость текущего выполнения; реальная база склада от вызова не меняется.

Иногда нужен только один элемент результата. После вызова можно написать ещё одну пару []: сначала завершится функция и вернёт массив, затем выбранный ключ даст число. Такой приём не делает функцию ленивой: весь результат всё равно вычисляется.

Выбрать число непосредственно из возвращённого массиваСамостоятельный пример
<?php
declare(strict_types=1);
function addDelivery(array $stocks, int $units): array
{
    $result = [];
    foreach ($stocks as $sku => $stock) {
        $result[$sku] = $stock + $units;
    }
    return $result;
}
$stocks = ['TEA-01' => 3, 'COF-02' => 0];
$coffeeAfter = addDelivery($stocks, 2)['COF-02'];
echo 'Кофе после такой поставки: ', $coffeeAfter, ' уп.', "\n";
echo 'Кофе в исходных данных: ', $stocks['COF-02'], ' уп.', "\n";
Ожидаемый результат
Кофе после такой поставки: 2 уп.
Кофе в исходных данных: 0 уп.

Возврат был [TEA-01 → 5, COF-02 → 2]; обращение [COF-02] выбрало 2 и сохранило его в coffeeAfter. Исходный stocks не изменён. Если нужен весь массив несколько раз, сохраните его один раз, а не вызывайте расчёт заново для каждого поля.

Объявление array ограничивает только внешний тип, int — вид величины units. Они не проверяют каждый остаток, допустимость отрицательной поставки, наличие артикула и переполнение суммы. Пример работает на известных небольших целых. Внешние данные сначала проверяют по этим правилам; стратегия неудачи должна быть частью договора.

Ссылки внутри и объекты внутри: копия не всегда полностью независима

Этот раздел читается после ссылок и объектов. Он нужен, когда подготовка черновика неожиданно меняет исходную структуру.

Общая ссылка остаётся общей и после копирования контейнераСамостоятельный пример
<?php
$stock = 3;
$original = ['stock' => &$stock];
$copy = $original;
$copy['stock'] = 8;
echo $original['stock'], ' / ', $stock, "\n";
Ожидаемый результат
8 / 8

Мы явно положили в элемент ссылку. Копирование массива сохранило эту связь; оно не превратило элемент в независимый снимок. Это причина не использовать & «на всякий случай».

Два массива могут указывать на один объектСамостоятельный пример
<?php
$record = (object) ['stock' => 3];
$original = ['product' => $record];
$copy = $original;
$copy['product']->stock = 8;
echo $original['product']->stock, "\n";
Ожидаемый результат
8

(Object) здесь создаёт простой объект stdClass, а -> обращается к свойству. Объект не клонировался вместе с массивом. Как получить намеренно отдельный объект и как быть с вложенными объектами, объясняет клонирование. JSON-перекодирование не является универсальной безопасной глубокой копией произвольных PHP-значений.

Изменение через &: почему после цикла нужен unset

Ссылка на элемент в цикле
  1. &$stock связан с первым элементом
  2. Затем связан со следующим
  3. После последнего прохода связь остаётся
  4. unset($stock) разрывает связь имени
Обновить массив на месте и освободить переменную обходаСамостоятельный пример
<?php
$stocks = ['A17' => 3, 'B20' => 0];
foreach ($stocks as &$stock) {
    $stock += 5;
}
unset($stock);
$stock = 100;
echo $stocks['A17'], ' / ', $stocks['B20'], "\n";
Ожидаемый результат
8 / 5

Без unset следующее присваивание $stock = 100 изменило бы последний элемент массива. Ссылка не исчезает от закрывающей фигурной скобки. Если нужен только расчёт нового результата, пример с отдельным массивом выше проще. Foreach не изменяет внутренний указатель current/key; не смешивайте эти два механизма без причины.

Приведение к array: упаковка значения не равна проверке данных

Одна строка станет одним элементом, а null — пустым массивомСамостоятельный пример
<?php
$single = (array) 'A17';
$empty = (array) null;
echo $single[0], "\n";
echo count($empty), "\n";
$notDecoded = (array) '{"sku":"A17"}';
echo $notDecoded[0], "\n";
Ожидаемый результат
A17
0
{"sku":"A17"}

Число, строка, bool и ресурс при таком приведении дают один элемент с ключом 0. Json-текст остался текстом; (array) не разбирает JSON. Если API обязан вернуть массив, не скрывайте ошибочный тип приведением: проверьте его и обработайте нарушение договора.

Приведение объекта показывает также служебные ключи свойствСамостоятельный пример
<?php
class DeliveryDraft
{
    public string $city = 'Тула';
    protected int $attempts = 1;
    private string $token = 'demo';
    public string $notLoaded;
}

$fields = (array) new DeliveryDraft();
foreach ($fields as $key => $value) {
    echo str_replace("\0", '[NUL]', $key), "\n";
}
Ожидаемый результат
city
[NUL]*[NUL]attempts
[NUL]DeliveryDraft[NUL]token

Это поздний пример после свойств и областей видимости. Неинициализированное типизированное свойство пропущено, а protected/private получили специальные ключи с нулевыми байтами. Не отправляйте такой массив клиенту: приватность свойства не защищает данные от намеренного экспорта. Для API явно выбирайте разрешённые поля.

Список, словарь, стек, очередь и дерево: результат определяет структуру

PHP-массив подходит нескольким моделям. Выбирайте модель по операции, которую нужно выполнить: показать все значения по порядку, найти товар по артикулу, отменить последнее действие или взять самое раннее задание. Одно название array не объясняет ни форму входа, ни порядок обработки.

ЗадачаМодельРезультат нужной операции
Показать пункты меню по порядкуСписокПоследовательность строк для показа.
Узнать остаток TEA-01СловарьОдно значение по согласованному ключу.
Отменить последнее изменениеСтекПоследняя сохранённая версия.
Взять раннюю заявкуОчередьПервое ещё не обработанное задание.
Обойти разделы и подразделыДеревоНужные листья или узлы вместе с путями.

Для стека добавление $history[] = $draft сохраняет очередной снимок, а array_pop возвращает и удаляет последний элемент. Важны оба результата: функция меняет history и отдаёт снятую версию. Сохраняйте прежнюю версию до изменения, чтобы снятый снимок действительно мог восстановить данные.

На деле вернуть прежнюю цену карточкиСамостоятельный пример
<?php
$draft = ['title' => 'Чай', 'price' => 150];
$history = [];
$history[] = $draft;
$draft['title'] = 'Чай — акция';
$history[] = $draft;
$draft['price'] = 120;
echo 'До отмены: ', $draft['title'], ', ', $draft['price'], ' руб.', "\n";
if ($history !== []) {
    $draft = array_pop($history);
}
echo 'После отмены цены: ', $draft['title'], ', ', $draft['price'], ' руб.', "\n";
if ($history !== []) {
    $draft = array_pop($history);
}
echo 'После отмены названия: ', $draft['title'], ', ', $draft['price'], ' руб.', "\n";
Ожидаемый результат
До отмены: Чай — акция, 120 руб.
После отмены цены: Чай — акция, 150 руб.
После отмены названия: Чай, 150 руб.

Первое извлечение вернуло сохранённую карточку с новым названием, но ещё старой ценой. Присваивание draft восстановило её. Второе извлечение вернуло самую первую карточку. !== [] проверяет, что история непуста; без этого пустой стек вернул бы null.

Договор array_pop(array &$array): mixed: принять изменяемую переменную-массив, удалить последний элемент и вернуть его значение любого типа либо null для пустого массива. Передавать следует переменную, не временное выражение. Если null — допустимый элемент, один возвращённый null не отличает пустоту от такого значения: проверяйте состояние или договор содержимого. В примере снимки содержат только строки и числа, поэтому работают обычные правила копирования; объекты и ссылки потребуют другой политики снимков.

Для очереди array_shift возвращает и удаляет первый элемент. Числовые ключи после этого перенумеровываются. В следующем примере вывод подтверждает порядок выполнения заявок; сохранение результата в реальную систему здесь не моделируется.

Взять ранние заявки в порядке поступленияСамостоятельный пример
<?php
$queue = ['Заявка A', 'Заявка B'];
$first = array_shift($queue);
echo 'Начать обработку: ', $first, "\n";
$queue[] = 'Заявка C';
while ($queue !== []) {
    $request = array_shift($queue);
    echo 'Следующая в работе: ', $request, "\n";
}
echo "Ожидающих заявок не осталось\n";
Ожидаемый результат
Начать обработку: Заявка A
Следующая в работе: Заявка B
Следующая в работе: Заявка C
Ожидающих заявок не осталось

A удалена первой, C добавлена после оставшейся B. Перед каждым следующим извлечением while проверяет непустой массив, поэтому null пустой очереди не принимается за заявку. Программа показывает порядок, в котором обработчик получит значения.

Договор array_shift(array &$array): mixed тоже требует изменяемого массива и возвращает извлечённое значение либо null при пустом входе. Перенумеровываются числовые ключи с нуля; строковые ключи сохраняются. И array_shift, и array_pop сбрасывают внутренний указатель массива к началу. Обычный foreach работает независимо от этого указателя; средства current/key/next имеют собственный договор.

Многократный array_shift большой коллекции требует переиндексации; для подходящего объёма и договора рассмотрите SplQueue. Обычный массив живёт в текущем процессе: он не переживает падение автоматически и не координирует нескольких обработчиков. Постоянная очередь требует отдельного хранилища, подтверждения и политики повторов. Деревья продолжаются в рекурсии, а иной способ выдачи значений — в итераторах.

Практика: изменить корзину, сохранив понятный договор

Условие: известная локальная корзина содержит два артикула. Человек изменил количество одного товара. Требуется подготовить новую корзину, оставить исходную для сравнения и показать изменения. Здесь нет пользовательского HTTP-ввода: приём и проверка формы добавляются отдельным шагом.

Новый вариант корзины без изменения исходногоСамостоятельный пример
<?php
$original = ['A17' => 2, 'B20' => 1];
$updated = $original;
$updated['A17'] = 3;
$updated['C31'] = 1;
unset($updated['B20']);
foreach ($updated as $sku => $quantity) {
    echo $sku, ': ', $quantity, "\n";
}
echo 'В исходной A17: ', $original['A17'], "\n";
Ожидаемый результат
A17: 3
C31: 1
В исходной A17: 2

Ключи сохранили артикулы; array_values уничтожил бы эту связь. Копия позволила подготовить изменение отдельно. Для сохранения заказа в базе нужны ещё проверка товара, допустимого количества, цены на сервере и согласованная транзакция — эти действия не выполняются от одного создания массива.

Попробуйте сами. Удалите C31 только из новой корзины, добавьте D40 с количеством 0 и ответьте: существует ли поле D40? Какой проверкой отличить нулевое количество от отсутствия?

Разбор решения

Unset($updated['C31']); $updated['D40'] = 0;. Array_key_exists('D40', $updated) вернёт true; isset тоже true, поскольку значение не null. Empty вернёт true и потому не подходит как проверка отсутствия. Допустимо ли хранить нулевую позицию, определяет бизнес-правило корзины.

Что такое массив и какую задачу он решает

Наглядная модель

Массив — органайзер для данных

Весь органайзер — массив $organizer. Подпись ячейки — ключ. Число внутри — количество деталей, то есть значение.

  • саморезы

    50 шт.

  • дюбели

    20 шт.

  • шайбы

    80 шт.

  • гайки

    12 шт.

Прочитать одну ячейку по её ключу$organizer['саморезы'] → 50

Можно менять значение ячейки и добавлять новые. foreach перебирает ячейки. Модель условная: PHP-массив хранит не только числа, но и строки, другие массивы. Стрелка выше означает результат чтения.

У вас есть корзина, и в ней несколько товаров. Можно завести $firstProduct, $secondProduct, $thirdProduct, но заранее неизвестно, сколько их добавит покупатель. Массив позволяет хранить такую коллекцию в одной переменной и обращаться к её элементам по ключам. Ключ отвечает на вопрос «какое значение взять», а значение — это сами данные.

Список покупок: одна переменная вместо множества отдельныхСамостоятельный пример
<?php
declare(strict_types=1);

$products = ['Чай', 'Кофе'];
echo $products[0], "\n";
$products[] = 'Мёд';
echo 'Товаров: ', count($products), "\n";
foreach ($products as $product) {
    echo '- ', $product, "\n";
}
Ожидаемый результат
Чай
Товаров: 3
- Чай
- Кофе
- Мёд

Квадратные скобки при создании собирают значения в массив. В этом списке первый ключ — 0, второй — 1. $products[0] читает первое значение, $products[] добавляет новое в конец, count считает элементы. foreach выполняет одно действие с каждым товаром, поэтому не нужно заранее знать их количество.

Карточка товара: ключи объясняют значение полейСамостоятельный пример
<?php
declare(strict_types=1);

$product = ['sku' => 'TEA-01', 'title' => 'Чай', 'stock' => 0];
echo $product['title'], ': ', $product['stock'], "\n";
$product['stock'] = 5;
echo 'После поставки: ', $product['stock'], "\n";
echo array_key_exists('price', $product) ? "Цена есть\n" : "Цена не передана\n";
Ожидаемый результат
Чай: 0
После поставки: 5
Цена не передана

Здесь ключ title обозначает название, stock — остаток. Стрелка => связывает ключ со значением. Ноль — существующее показание, а price вообще отсутствует: это разные состояния. Проверка ключа нужна до чтения необязательного поля. Числовой список и карточка с именами полей используют один тип PHP — array.

Когда внутри корзины лежат не строки названий, а такие карточки товаров, получается вложенный массив: $cart[0]['title'] означает «первая карточка → её название». Такие структуры встречаются в ответах API, $_POST с повторяющимися полями и результатах запросов к базе. В примере с устройством и JSON видно, откуда они приходят и зачем нужны.

Один тип PHP — несколько моделей данных

Массив PHP — упорядоченное отображение ключей в значения. Из него получается и список позиций, и словарь «артикул → остаток». У списка ключи идут от 0 без пропусков; у словаря ключ выражает смысл. Список удобно перебирать, словарь — использовать для прямого доступа по известному ключу. Перед выбором функции сформулируйте, какая из этих моделей у вас сейчас и какая нужна после операции.

Ключами служат int и string, но некоторые строковые числовые ключи преобразуются в целые. Артикул "12" и число 12 могут столкнуться; "012" сохраняет другую семантику. Если источник не гарантирует уникальность ID, нельзя без проверки строить индекс через array_column: повторный ключ перезапишет предыдущий товар. У массива нет встроенной проверки схемы каждого элемента. PHPDoc помогает анализатору, а данные от клиента проверяет исполняемый код.

Построить индекс товаров и обнаружить дубликатСамостоятельный пример
<?php
declare(strict_types=1);

$products = [['sku' => 'A-1', 'stock' => 4], ['sku' => 'B-2', 'stock' => 0]];
$bySku = [];
foreach ($products as $product) {
    if (array_key_exists($product['sku'], $bySku)) {
        throw new UnexpectedValueException('Повтор артикула');
    }
    $bySku[$product['sku']] = $product;
}
echo $bySku['B-2']['stock'], "\n";
Ожидаемый результат
0

Индекс полезен, когда вы много раз сопоставляете позиции заказа с каталогом. Проверка дубликата не дает молча заменить один товар другим. Для внешнего источника до индексации нужны ограничения типа и длины SKU.

Фильтровать, преобразовать, свернуть

Список доступных артикулов для ответа APIСамостоятельный пример
<?php
declare(strict_types=1);

$products = [
    ['sku' => 'A', 'stock' => 2],
    ['sku' => 'B', 'stock' => 0],
    ['sku' => 'C', 'stock' => 5],
];
$available = array_filter($products, static fn (array $row): bool => $row['stock'] > 0);
$skus = array_values(array_column($available, 'sku'));
echo json_encode($skus, JSON_THROW_ON_ERROR), "\n";
Ожидаемый результат
["A","C"]

Фильтрация решает условие наличия, выбор колонки извлекает значения. Если непосредственно кодировать отфильтрованный список с пропусками числовых ключей, JSON может стать объектом. array_values явно возвращает модель списка.

array_map преобразует каждое значение; array_filter оставляет подходящие; array_reduce сворачивает коллекцию в одно значение. Для нескольких выходов, сложных веток и отчета об ошибках обычный foreach часто понятнее. Никто не получает дополнительную надежность только от функционального стиля. Значение 0 нельзя удалять стандартным array_filter, если это законный остаток или скидка; задайте конкретный callback.

Объединение не всегда означает одно и то же

Наложить настройки без неожиданной склейкиСамостоятельный пример
<?php
declare(strict_types=1);

$defaults = ['timeout' => 5, 'headers' => ['Accept' => 'application/json']];
$local = ['timeout' => 10];
$settings = array_replace($defaults, $local);
echo $settings['timeout'], "\n";
echo json_encode([10 => 'A'] + [10 => 'B', 20 => 'C'], JSON_THROW_ON_ERROR), "\n";
Ожидаемый результат
10
{"10":"A","20":"C"}

array_replace заменяет значение по ключу. Оператор+ сохраняет левое значение при совпадении ключей. array_merge для числовых ключей добавляет значения с перенумерацией. Вложенные настройки требуют отдельно выбранной политики: слепая рекурсивная склейка может превратить заголовок в массив.

Стоимость и критерии выбора

ЗадачаОперацияЧто проверить
Есть ли значениеin_array(..., true)Линейный поиск; для частых проверок подумать об индексе
Есть ли ключ даже с nullarray_key_existsНе спутать с isset
Получить порядковый списокarray_valuesСоздает новый массив
Сортировать записиusort / uasortТеряются ли ключи; компаратор возвращает int
Читать миллионы строкГенератор/потокНе собирать все данные только ради foreach

Многократный array_merge внутри большого цикла может снова и снова копировать растущий результат. Для добавления одной позиции используйте $result[] = $item. Если вы ищете каждый товар линейно для каждой позиции заказа, число сравнений растет как произведение размеров. Один индекс переносит работу в предварительный проход и требует дополнительной памяти. Измеряйте реальный объем и помните, что база уже умеет соединять таблицы эффективнее, чем ручной перенос миллионов строк в PHP.

Попробуйте сами. Поставщик прислал остатки ["A"=>0,"B"=>null,"C"=>3]. Нужно удалить только неизвестные. Какой фильтр?

Разбор решения

array_filter($stocks, static fn ($value) => $value !== null). Ключи SKU сохраняются; array_values здесь вреден, он уничтожит связь артикулов с остатками. Проверку int>=0 выполните отдельно.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

array_filterОставить только подходящие элементы; значения и исходные ключи сохраняются.

PHP 4.0.6+ · standard

array_filter(array $array, ?callable $callback = null, int $mode = 0): array

Задача и маршрут результата. Нужен список доступных остатков. Callback отвечает true/false для каждой величины; array_filter возвращает целый новый массив отобранных элементов, который далее перебирается. Сам callback не возвращает этот массив.

Оставить только подходящие элементы; значения и исходные ключи сохраняются.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayПроверяемый массив.
callbackВернуть true для сохранения элемента. null или отсутствие функции удаляет всё, что empty считает пустым: в том числе 0 и "0".
mode0: callback(value); ARRAY_FILTER_USE_KEY: callback(key); ARRAY_FILTER_USE_BOTH: callback(value, key).
ВозвратНовый массив с исходными ключами, возможно с пропусками.
Отказ и диагностикаPHP 8: E_WARNING, если callback требует аргумент по ссылке. Изменение состава исходного массива внутри callback имеет неопределённое поведение; ошибки callback распространяются наружу.

Запись function ($value) { return ...; } создаёт функцию, которую операция вызывает для каждого элемента; return передаёт ей результат.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Для этого примера: передача функции как значения.

Что происходит в первом опыте
  1. Остатки 0, 3, 0, 7
  2. Проверить value > 0
  3. Сохранить ключи 1 и 3
Использовать результат array_filter в задачеСамостоятельный пример
<?php
$stock = [0, 3, 0, 7];
$available = array_filter($stock, function ($value) {
    return $value > 0;
});
foreach ($available as $originalPosition => $quantity) {
    echo 'Исходная позиция ', $originalPosition, ': доступно ', $quantity, ' шт.', "\n";
}
$list = array_values($available);
echo 'Первая доступная величина нового списка: ', $list[0], ' шт.', "\n";
Ожидаемый результат
Исходная позиция 1: доступно 3 шт.
Исходная позиция 3: доступно 7 шт.
Первая доступная величина нового списка: 3 шт.

При значениях 0 и 0 callback возвращает false, при 3 и 7 — true. Array_filter сохраняет оригинальные ключи 1 и 3. Foreach выводит обе оставшиеся величины, после чего array_values создаёт отдельный список с индексами 0 и 1. Переиндексация уместна для технических позиций, но уничтожила бы смысл ключей, если бы ими были id товаров.

Не потерять полезный нольСамостоятельный пример
<?php
$measurements = [0, '0', null, false, '', 5];
echo json_encode(array_values(array_filter($measurements))), "
";
echo json_encode(array_values(array_filter($measurements,
    function ($value) { return $value !== null; }))), "
";
Ожидаемый результат
[5]
[0,"0",false,"",5]

Отсутствующее измерение и измеренный ноль — разные состояния; условие выбрано из договора данных.

Отобрать разрешённые поля по ключу и значениюСамостоятельный пример
<?php
$form = ['name' => 'Ada', 'role' => 'admin', 'note' => ''];
$allowed = array_filter($form, function ($key) {
    return in_array($key, ['name','note'], true);
}, ARRAY_FILTER_USE_KEY);
$filled = array_filter($allowed, function ($value, $key) {
    return $key === 'name' && $value !== '';
}, ARRAY_FILTER_USE_BOTH);
echo json_encode($filled), "
";
Ожидаемый результат
{"name":"Ada"}

Отбор полей не заменяет проверку типов, длины и прав. Порядок параметров режима BOTH: сначала значение, затем ключ.

Нюансы и выбор. Не используйте empty-фильтрацию как универсальную очистку формы. В callback не добавляйте и не удаляйте элементы исходного массива; для изменения формы данных используйте отдельный результат.

Сравните: array_values, array_map, array_find, array_any. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Возвращает новый массив, сохраняя ключи. Callback получает значение, либо ключ/оба аргумента при соответствующем mode. Без callback удаляются значения, считающиеся пустыми; это отличается от удаления только null.

Не потерять нулевой остатокСамостоятельный пример
<?php
declare(strict_types=1);

$stocks = ['A' => 0, 'B' => null, 'C' => 7];
$known = array_filter($stocks, static fn ($stock): bool => $stock !== null);
echo json_encode($known, JSON_THROW_ON_ERROR), "\n";
Ожидаемый результат
{"A":0,"C":7}

У поставщика null означает «не сообщил»,0 — «нет в наличии». Явный критерий сохраняет эту разницу и связь с артикулом.

Выбрать поля для публичного ответаСамостоятельный пример
<?php
declare(strict_types=1);

$profile = ['name' => 'Ann', 'email' => 'a@example.test', 'internalNote' => 'review'];
$public = array_filter($profile, static fn (string $key): bool => in_array($key, ['name', 'email'], true), ARRAY_FILTER_USE_KEY);
echo implode(',', array_keys($public)), "\n";
Ожидаемый результат
name,email

Фильтр по ключу реализует разрешенный список. Для сложного API явное построение ответа еще нагляднее; не публикуйте все поля по умолчанию.

Сравните. array_map: преобразование меняет значения; foreach удобнее для нескольких результатов и отчета об ошибках.

array_mapПолучить новое значение для каждого элемента или собрать параллельные списки по позициям.

PHP 4.0.6+ · standard

array_map(?callable $callback, array $array, array ...$arrays): array

Задача и маршрут результата. Нужны цены после добавления упаковки. Callback возвращает новую цену одной позиции; array_map собирает эти отдельные возвраты в новый массив. Исходный прайс остаётся пригодным для сравнения «было → стало».

Получить новое значение для каждого элемента или собрать параллельные списки по позициям.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
callbackФункция принимает значение из каждого массива. null собирает массивы в строки (zip); для одного входа null возвращает его без преобразования.
array, arraysОдин или несколько массивов; сопоставление по порядку, не по равенству ключей. Короткие входы дополняются null до длины самого длинного.
ВозвратНовый массив. При одном входном массиве ключи сохранены; при нескольких — 0, 1…
Отказ и диагностикаМеньше входных массивов, чем обязательных параметров callback: ArgumentCountError. Лишние входы не становятся обязательными параметрами обычной пользовательской функции. С PHP 8 callback с параметром по ссылке вызывает E_WARNING; недопустимый null для типизированного callback может дать TypeError.

Запись function ($value) { return ...; } создаёт функцию, которую операция вызывает для каждого элемента; return передаёт ей результат.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Для этого примера: передача функции как значения.

Что происходит в первом опыте
  1. Цены 100 и 250 копеек
  2. Для каждого значения прибавить 20
  3. Новый массив 120 и 270
Использовать результат array_map в задачеСамостоятельный пример
<?php
$prices = [40 => 100, 70 => 250];
$changed = array_map(function ($price) {
    return $price + 20;
}, $prices);
foreach ($changed as $id => $newPrice) {
    echo 'Товар ', $id, ': ', $prices[$id], ' → ', $newPrice, ' коп.', "\n";
}
Ожидаемый результат
Товар 40: 100 → 120 коп.
Товар 70: 250 → 270 коп.

Map передаёт callback сначала 100, затем 250. Return создаёт соответственно 120 и 270; map сохраняет ключи 40 и 70, потому что входной массив один. Changed получает новый прайс, а prices хранит старый и используется в отчёте. Callback не получает ключ автоматически; внешний foreach получает его из результата.

Проверить неполные параллельные колонкиСамостоятельный пример
<?php
$codes = ['p1','p2'];
$amounts = [4];
echo json_encode(array_map(null, $codes, $amounts)), "
";
if (count($codes) !== count($amounts)) {
    echo "Импорт отклонён: длины колонок различаются
";
}
Ожидаемый результат
[["p1",4],["p2",null]]
Импорт отклонён: длины колонок различаются

Функция не останавливается на коротком списке, как Python zip. Для строгого импорта сначала проверяйте длины.

Посчитать суммы строк по двум колонкамСамостоятельный пример
<?php
$totals = array_map(function ($price, $quantity) {
    return $price * $quantity;
}, [100, 250], [3, 2]);
echo json_encode($totals), "
";
echo json_encode(array_map(null, ['id' => 5])), "
";
Ожидаемый результат
[300,500]
{"id":5}

Первый аргумент callback берётся из первой колонки, второй — из второй. При нескольких входах их ключи не используются для соединения записей.

Нюансы и выбор. Для соединения по ID сначала постройте индекс, а не используйте позиционный map. Если нужны ключи, foreach часто понятнее; либо явно передайте array_keys как второй вход. Возврат callback обязателен для полезного результата: echo печатает, но возвращает null.

Сравните: array_filter, array_walk, array_combine, array_column. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Возвращает результаты callback. При одном входном массиве ключи сохраняются; при нескольких результирующие ключи числовые. Исходный массив не заменяется автоматически.

Нормализовать CSV-тегиСамостоятельный пример
<?php
declare(strict_types=1);

$tags = [' PHP ', ' API '];
$normalized = array_map('trim', $tags);
echo implode('|', $normalized), "\n";
Ожидаемый результат
PHP|API

Передаем имя функции как callback. Это подходит для уже проверенного списка строк; массив или null внутри списка требуют валидации на входе.

Построить карточки ответаСамостоятельный пример
<?php
declare(strict_types=1);

$rows = [['id' => 7, 'name' => 'Tea', 'secret' => 'x']];
$cards = array_map(static fn (array $row): array => ['id' => $row['id'], 'title' => $row['name']], $rows);
echo json_encode($cards, JSON_THROW_ON_ERROR), "\n";
Ожидаемый результат
[{"id":7,"title":"Tea"}]

Выбираем только публичные поля. Функция превращает внутренние записи в представление API и не тащит служебный secret в ответ.

Сравните. array_filter отбирает; array_reduce сворачивает. Один foreach уменьшает промежуточные массивы в большой обработке.

array_reduceСвести все элементы к одному итогу: числу, строке или накопленному массиву.

PHP 4.0.5+ · standard

array_reduce(array $array, callable $callback, mixed $initial = null): mixed

Задача и маршрут результата. Нужна одна сумма для оплаты. Callback принимает предыдущую сумму и очередную строку, возвращает следующую сумму. Последний return становится результатом всей array_reduce и участвует в расчёте с доставкой.

Свести все элементы к одному итогу: числу, строке или накопленному массиву.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayЗначения в текущем порядке; ключи callback не получает.
callbackcallback(carry, item): новый carry. На первом шаге carry равен initial; дальше — предыдущему возврату.
initialНачальное состояние и результат пустого массива. По умолчанию null.
ВозвратПоследнее возвращённое callback значение; для пустого входа initial, то есть null по умолчанию.
Отказ и диагностикаОшибки callback распространяются наружу. В PHP 8 требование параметра по ссылке вызывает E_WARNING. Неверный тип initial для типизированного carry приводит к TypeError при вызове callback.

Запись function ($value) { return ...; } создаёт функцию, которую операция вызывает для каждого элемента; return передаёт ей результат. Накопитель carry — обычный параметр, в который следующий вызов получит предыдущий return.

Для этого примера: передача функции как значения.

Что происходит в первом опыте
  1. Начальный итог 0
  2. Добавить 100, затем 250
  3. Вернуть 350
Увидеть накопительСамостоятельный пример
<?php
$sum = array_reduce([100, 250], function ($carry, $item) {
    echo $carry, '+', $item, "
";
    return $carry + $item;
}, 0);
echo 'Итого: ', $sum, "
";
var_dump(array_reduce([], function ($a, $b) { return $a + $b; }));
Ожидаемый результат
0+100
100+250
Итого: 350
NULL

Для простого сложения array_sum короче. Здесь виден договор каждого шага.

Использовать результат array_reduce в задачеСамостоятельный пример
<?php
$lines = [['price' => 150, 'quantity' => 2], ['price' => 80, 'quantity' => 3]];
$itemsTotalKopecks = array_reduce($lines, function ($sum, $line) {
    $lineTotal = $line['price'] * $line['quantity'];
    return $sum + $lineTotal;
}, 0);
$deliveryKopecks = 50;
$payableKopecks = $itemsTotalKopecks + $deliveryKopecks;
echo 'Товары: ', $itemsTotalKopecks, ' коп.', "\n";
echo 'Доставка: ', $deliveryKopecks, ' коп.', "\n";
echo 'К оплате: ', $payableKopecks, ' коп.', "\n";
$emptyTotal = array_reduce([], function ($sum, $line) { return $sum; }, 0);
echo 'Товары в пустой корзине: ', $emptyTotal, ' коп.', "\n";
Ожидаемый результат
Товары: 540 коп.
Доставка: 50 коп.
К оплате: 590 коп.
Товары в пустой корзине: 0 коп.

Initial 0 становится первым sum. Строка 150 × 2 даёт 300; return 300 поступает в sum следующего callback. Строка 80 × 3 даёт 240, следующий return равен 540. Array_reduce возвращает 540 в itemsTotalKopecks; обычный код прибавляет 50. Пустой вход не вызывает callback и возвращает initial 0. Все поля примера заранее проверены по форме и единицам.

Нюансы и выбор. Не забывайте return на каждом пути: иначе следующий carry станет null. Многократное слияние растущих массивов внутри reduce может много раз копировать накопленное; прямой foreach с добавлением часто яснее и экономнее.

Сравните: array_sum, array_product, array_map, array_walk. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Callback получает накопитель и очередное значение; его результат становится следующим накопителем. Явный initial задает тип и ответ для пустой коллекции.

Итог корзины в копейкахСамостоятельный пример
<?php
declare(strict_types=1);

$items = [['price' => 15000, 'qty' => 2], ['price' => 9900, 'qty' => 1]];
$total = array_reduce($items, static fn (int $sum, array $item): int => $sum + $item['price'] * $item['qty'], 0);
echo $total, "\n";
Ожидаемый результат
39900

Начальное 0 гарантирует числовой итог и для пустой корзины. Ограничения полей проверяются до агрегации; не используйте float для копеек.

Есть ли просроченное обязательствоСамостоятельный пример
<?php
declare(strict_types=1);

$tasks = [['overdue' => false], ['overdue' => true]];
$hasOverdue = array_reduce($tasks, static fn (bool $found, array $task): bool => $found || $task['overdue'], false);
echo $hasOverdue ? "Нужно внимание\n" : "Все вовремя\n";
Ожидаемый результат
Нужно внимание

Это показывает bool-накопитель. Reduce все равно вызовет callback для каждого элемента; если важно остановиться на первом совпадении, используйте foreach с break или array_any в PHP 8.4+.

Сравните. Для простой суммы чисел есть array_sum; для нескольких метрик — один foreach; замыкания объясняют callback.

array_columnДостать одно поле из строк отчёта или построить индекс записей по ID.

PHP 5.5+ · standard

array_column(array $array, int|string|null $column_key, int|string|null $index_key = null): array

Задача и маршрут результата. Нужно обратиться к остатку по артикулу. Column_key задаёт, что взять из строки, index_key — как подписать это значение в новом массиве. Передайте эти два ключа в соответствующем порядке.

Достать одно поле из строк отчёта или построить индекс записей по ID.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayМассив строк-массивов или строк-объектов. Исходные ключи внешнего массива не сохраняются.
column_keyИмя/индекс требуемого поля. null означает взять целую строку. Строка без нужного поля пропускается без диагностики.
index_keyПоле строки для ключа результата. Без него — последовательный список; при отсутствии поля в отдельной строке PHP добавляет числовой ключ. Повтор ключа заменяет прежнюю строку.
ВозвратНовый массив выбранных значений либо целых строк. Количество результата может быть меньше входа.
Отказ и диагностикаТипы аргументов должны соответствовать сигнатуре. Объект как значение index_key в PHP 8 даёт TypeError, даже если умеет __toString. Недоступное свойство с __isset, но без __get может вызвать Error.

В array_column($rows, "stock", "sku") stock задаёт содержимое, sku — подпись ячейки. Функция извлекает поля, но не проверяет обязательность каждого поля во всех строках.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Строки: A со stock=0 и B со stock=3.
  2. Из каждой берётся поле stock; ключ берётся из sku.
  3. Результат: A=0, B=3.
Использовать результат array_column в задачеСамостоятельный пример
<?php
$rows = [['sku' => 'A', 'stock' => 0], ['sku' => 'B', 'stock' => 3]];
$stocksBySku = array_column($rows, 'stock', 'sku');
foreach ($stocksBySku as $sku => $stock) {
    echo $sku, ': ', $stock, ' шт.', "\n";
}
echo 'Для проверки заказа товара B: ', $stocksBySku['B'], ' шт.', "\n";
$codes = array_column($rows, 'sku');
echo 'Первый артикул отдельного списка: ', $codes[0], "\n";
Ожидаемый результат
A: 0 шт.
B: 3 шт.
Для проверки заказа товара B: 3 шт.
Первый артикул отдельного списка: A

Stock задаёт содержимое новой ячейки, sku — её ключ: получаются A => 0 и B => 3. Ноль сохранён. Вызов без index_key возвращает обычный список артикулов с 0 и 1. Исходные строки остаются прежними. Если поле отсутствует или sku повторяется, результат может потерять строку: подробный следующий опыт показывает эти границы.

Пропущенное поле и повтор идентификатораСамостоятельный пример
<?php
$rows = [['sku' => 'A', 'stock' => 1], ['sku' => 'B'], ['sku' => 'A', 'stock' => 4]];
echo json_encode(array_column($rows, 'stock', 'sku')), "\n";
$indexed = array_column($rows, null, 'sku');
echo $indexed['A']['stock'], "\n";
Ожидаемый результат
{"A":4}
4

Строка B не попала в первый результат, а поздняя A затёрла раннюю. Индекс по ID подходит только при принятой политике уникальности.

Взять доступные свойства объектовСамостоятельный пример
<?php
$rows = [(object) ['id' => 41, 'stock' => 0], (object) ['id' => 72, 'stock' => 2]];
$stocks = array_column($rows, 'stock', 'id');
echo json_encode($stocks), "\n";
Ожидаемый результат
{"41":0,"72":2}

(object) создаёт простые объекты с публичными свойствами. Функция читает поля, но не вызывает произвольный метод getStock.

Нюансы и выбор. С PHP 7 поддерживаются строки-объекты; с 7.1 соблюдается видимость свойств. Для недоступного свойства требуются согласованные __isset и __get: без __isset строка может быть пропущена. Строки, не являющиеся массивом/объектом, тоже пропускаются. Для строгого импорта используйте явный обход с валидацией до извлечения: число полученных полей не заменяет проверку схемы.

Сравните: array_map, array_combine, array_key_exists. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

column_key задает извлекаемое поле; null оставляет всю запись. index_key выбирает поле для ключа результата. Типичная задача — список ID из строк базы.

Список ID для следующего шагаСамостоятельный пример
<?php
declare(strict_types=1);

$orders = [['id' => 21, 'status' => 'new'], ['id' => 22, 'status' => 'paid']];
echo implode(',', array_column($orders, 'id')), "\n";
Ожидаемый результат
21,22

ID можно передать в параметризованный SQL с отдельным плейсхолдером на каждый ID. Не вставляйте готовую строку из implode в сырой SQL.

Индекс загруженного каталогаСамостоятельный пример
<?php
declare(strict_types=1);

$products = [['sku' => 'A', 'stock' => 2], ['sku' => 'B', 'stock' => 0]];
$bySku = array_column($products, null, 'sku');
echo $bySku['B']['stock'], "\n";
Ожидаемый результат
0

Предполагается проверенная уникальность SKU. Индекс нужен при повторном сопоставлении позиций; это осознанный обмен памяти на отсутствие повторного линейного поиска.

Сравните. Для обязательных полей и диагностики дубликатов используйте foreach. array_map удобен, когда нужно вычислить значение, а не выбрать одно поле.

array_key_existsПроверить наличие поля, даже если его значение null.

PHP 4.0.7+ · standard

array_key_exists(string|int|float|bool|resource|null $key, array $array): bool

Проверить наличие поля, даже если его значение null.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
keyИскомый ключ. Для ясного договора используйте int или string; прочие допустимые типы преобразуются по правилам ключей. null как аргумент deprecated с PHP 8.5 — используйте пустую строку.
arrayМассив, проверяется только первый уровень. Объекты не поддерживаются с PHP 8.0; для свойства существует property_exists.
Возвратtrue, если ключ присутствует, включая значение null; false при отсутствии.
Отказ и диагностикаНеподдерживаемый тип ключа или аргумент не-массив вызывает TypeError. Преобразования некоторых ключей могут давать диагностику; не используйте ресурс как бизнес-ключ.
Что происходит в первом опыте
  1. В сохранённой записи есть текст комментария.
  2. Наличие ключа с null разрешает очистку.
  3. Пустой массив правки не меняет запись.
Различить очистку комментария и отсутствие измененияСамостоятельный пример
<?php
$saved = ['comment' => 'Позвонить заранее'];
$patch = ['comment' => null];
if (array_key_exists('comment', $patch)) {
    $saved['comment'] = $patch['comment'];
}
echo 'Комментарий: ', $saved['comment'] ?? 'не указан', "\n";
$patch = [];
if (array_key_exists('comment', $patch)) {
    $saved['comment'] = $patch['comment'];
}
echo 'После пустой правки: ', $saved['comment'] ?? 'не указан', "\n";
Ожидаемый результат
Комментарий: не указан
После пустой правки: не указан

По договору этой локальной правки null означает очистку. Проверка находит существующий ключ comment и возвращает true даже при null; присваивание действительно очищает значение. Во второй правке ключа нет: false пропускает присваивание. ?? выбирает текст для отсутствующего/null комментария. Isset дал бы false уже в первом случае и помешал бы очистить поле. Тип и разрешённые имена входных полей проверяют отдельно.

Вложенное поле требует перехода на нужный уровеньСамостоятельный пример
<?php
$payload = ['product' => ['stock' => 0]];
var_dump(array_key_exists('stock', $payload));
var_dump(array_key_exists('stock', $payload['product']));
echo $payload['product']['stock'], "\n";
Ожидаемый результат
bool(false)
bool(true)
0

Функция не ищет рекурсивно. Форма product здесь известна; для внешнего JSON сначала убедитесь, что product — массив.

Нюансы и выбор. Не заменяйте проверку наличия на empty: empty считает пустыми и 0, и "0", и false. Наличие поля не доказывает его тип и допустимость. В PHP 8.0 набор допустимых скалярных типов ключа расширен, но наиболее понятный контракт приложения — именованный строковый ключ или целочисленный индекс.

Сравните: key_exists, in_array, array_keys. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Проверяет наличие ключа только на текущем уровне массива. Значение null не отменяет присутствия. Для JSONPATCH это позволяет различить «не менять» и «очистить».

Очистить необязательный телефонСамостоятельный пример
<?php
declare(strict_types=1);

$patch = ['phone' => null];
echo array_key_exists('phone', $patch) ? "Обновить поле\n" : "Не менять\n";
echo isset($patch['phone']) ? "Есть значение\n" : "Значение отсутствует\n";
Ожидаемый результат
Обновить поле
Значение отсутствует

В PATCH отсутствие поля и явный null могут иметь разные действия. Политику очистки и право изменения проверяют отдельно.

Кешировать отрицательный результатСамостоятельный пример
<?php
declare(strict_types=1);

$cache = ['missing-product' => null];
$key = 'missing-product';
echo array_key_exists($key, $cache) ? "Уже искали\n" : "Нужен запрос\n";
Ожидаемый результат
Уже искали

Null может означать сохраненное«не найдено». isset заставил бы выполнять поиск повторно. Для долгоживущего кеша добавьте срок действия отрицательного результата.

Сравните. isset отвечает «есть и не null», а ?? выбирает резерв при null/отсутствии. Для поиска значения есть in_array.

array_valuesОставить значения в текущем порядке и заменить ключи на последовательные 0,1,2,…

PHP 4+ · standard

array_values(array $array): array

Оставить значения в текущем порядке и заменить ключи на последовательные 0,1,2,…

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayЛюбой массив; исходные ключи сознательно отбрасываются.
ВозвратНовый список значений. Порядок обхода сохраняется, вложенные ключи не меняются.
Отказ и диагностикаАргумент должен быть массивом; другой тип даёт TypeError.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Вход: 0→A, 2→C.
  2. Значения A,C читаются в текущем порядке.
  3. Результат получает новые ключи 0,1; исходный массив остаётся с 0,2.
Использовать результат array_values в задачеСамостоятельный пример
<?php
$items = [0 => 'A', 2 => 'C'];
$list = array_values($items);
foreach ($list as $position => $name) {
    echo 'Позиция ', $position, ': ', $name, "\n";
}
echo 'Во входе C по-прежнему доступно по ключу 2: ', $items[2], "\n";
Ожидаемый результат
Позиция 0: A
Позиция 1: C
Во входе C по-прежнему доступно по ключу 2: C

Values читает A и C в текущем порядке и присваивает новым ячейкам ключи 0 и 1. List получает новый массив; items сохраняет 0 и 2. Это переиндексация, не сортировка и не фильтр. Если важный id существовал только в ключе, он был бы потерян в list.

Подготовить массив строк для JSON-спискаСамостоятельный пример
<?php
$byId = [41 => ['id' => 41, 'status' => 'new'], 72 => ['id' => 72, 'status' => 'paid']];
$rows = array_values($byId);
echo json_encode($rows), "\n";
echo $rows[1]['id'], "\n";
Ожидаемый результат
[{"id":41,"status":"new"},{"id":72,"status":"paid"}]
72

Идентификаторы остались внутри каждой записи. Если ID были только ключами, это преобразование потеряло бы их.

Нюансы и выбор. Это не сортировка: порядок значений остаётся прежним. Новый массив требует памяти; при больших коллекциях учитывайте одновременное существование источника и результата. Не исправляет схему вложенных строк и не удаляет пустые значения.

Сравните: array_keys, array_is_list, array_filter, array_slice. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Возвращает новый массив без исходных ключей. Полезно после фильтрации списка, перед JSON и для передачи позиционных данных.

Вернуть JSON-массив после фильтраСамостоятельный пример
<?php
declare(strict_types=1);

$ids = [0 => 10, 2 => 30];
echo json_encode(array_values($ids), JSON_THROW_ON_ERROR), "\n";
Ожидаемый результат
[10,30]

Внешний контракт ожидает список, поэтому устраняем дырку индексов. Без этой операции такой массив кодировался бы объектом.

Передать позиции в табличный экспортСамостоятельный пример
<?php
declare(strict_types=1);

$row = ['sku' => 'A', 'stock' => 5];
$cells = array_values($row);
echo implode(';', $cells), "\n";
Ожидаемый результат
A;5

Порядок соответствует порядку элементов массива. В рабочем CSV лучше явно перечислить колонки, а затем fputcsv, чтобы изменение схемы не сдвинуло данные.

Сравните. array_keys извлекает ключи. foreach сохраняет полный контекст ключа и значения.

array_keysПолучить имена полей или все ключи, у которых найдено заданное значение.

PHP 4+ · standard

array_keys(array $array): array; array_keys(array $array, mixed $filter_value, bool $strict = false): array

Получить имена полей или все ключи, у которых найдено заданное значение.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayИсточник ключей; его порядок сохраняется в последовательности результата.
filter_valueЕсли аргумент передан, вернуть лишь ключи элементов с этим значением. Переданный null — фильтр по null, а не отсутствие фильтра.
stricttrue использует === и учитывает тип; по умолчанию нестрогое сравнение.
ВозвратНовый список ключей int/string, перенумерованный с 0. Исходный массив не меняется.
Отказ и диагностикаНеверный тип array вызывает TypeError. Пустой набор совпадений даёт [].

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. stocks хранит A=0, B=2, C=0.
  2. Без фильтра результат содержит A,B,C.
  3. С фильтром 0 и strict=true остаются A,C.
Использовать результат array_keys в задачеСамостоятельный пример
<?php
$stocks = ['A' => 0, 'B' => 2, 'C' => 0];
$allCodes = array_keys($stocks);
echo 'Первый известный артикул: ', $allCodes[0], "\n";
$soldOut = array_keys($stocks, 0, true);
foreach ($soldOut as $sku) {
    echo 'Нужно пополнить: ', $sku, "\n";
}
Ожидаемый результат
Первый известный артикул: A
Нужно пополнить: A
Нужно пополнить: C

Без второго аргумента keys возвращает все имена A,B,C новым списком. Со значением 0 и strict=true выбирает только ключи элементов int 0: A и C. Это все совпадения, тогда как array_search вернул бы один первый ключ. Функция не меняет сами остатки и не пополняет склад автоматически.

Не смешивать строковый и числовой нольСамостоятельный пример
<?php
$states = ['count' => 0, 'code' => '0', 'unknown' => null];
echo json_encode(array_keys($states, 0, true)), "\n";
echo json_encode(array_keys($states, null, true)), "\n";
Ожидаемый результат
["count"]
["unknown"]

Фильтр со строгим сравнением отражает реальный договор типа. Без третьего аргумента значения разных типов могут совпасть.

Нюансы и выбор. Создаётся отдельный массив всех выбранных ключей. Для одного первого ключа используйте array_key_first, для факта присутствия — array_key_exists. При нестабильной схеме полезно сравнить возвращённые имена с разрешённым списком, но сами значения всё равно требуют проверки.

Сравните: array_values, array_search, array_key_first, array_key_exists. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Без filter_value возвращает все ключи. С дополнительным значением отбирает ключи по совпадению, а strict включает проверку типа.

Увидеть неожиданные поля формыСамостоятельный пример
<?php
declare(strict_types=1);

$input = ['name' => 'Ann', 'role' => 'admin'];
$unexpected = array_diff(array_keys($input), ['name']);
echo implode(',', $unexpected), "\n";
Ожидаемый результат
role

Разрешенный список предотвращает массовое назначение привилегированного поля. Правильная реакция — игнорирование или ошибка согласно API, а не автоматическое присваивание.

Найти склады с ровно нулевым остаткомСамостоятельный пример
<?php
declare(strict_types=1);

$stocks = ['north' => 0, 'south' => '0', 'west' => 4];
echo implode(',', array_keys($stocks, 0, true)), "\n";
Ожидаемый результат
north

Strict показывает, что строка"0" не прошла нормализацию. Для реального каталога нормализуйте весь вход, а не пользуйтесь случайным мягким равенством.

Сравните. Для одной проверки наличия ключа array_key_exists не требует собирать все списком.

array_mergeСклеить списки и применить поздние значения строковых ключей.

PHP 4+ · standard

array_merge(array ...$arrays): array

Склеить списки и применить поздние значения строковых ключей.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arraysНоль или больше массивов; обрабатываются слева направо. Без аргументов [] с PHP 7.4.
Правило совпаденияСтроковый ключ: позднее значение заменяет раннее. Целочисленные ключи: все значения добавляются и перенумеровываются с нуля.
ВозвратНовый массив; исходные массивы не изменяются. Это не глубокое клонирование объектов внутри значений.
Отказ и диагностикаНемассивный аргумент — TypeError. Рекурсивные варианты применяйте к конечным деревьям; циклические ссылки не являются поддержкой произвольного графа.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Два списка содержат три документа, в обоих есть числовой ключ 10.
  2. Merge собирает три позиции 0,1,2; совпавший строковый mode заменяется.
  3. Вывести очередь и режим из новых массивов.
Соединить две очереди и прочитать новые позицииСамостоятельный пример
<?php
$morning = [10 => 'накладная A', 20 => 'накладная B'];
$evening = [10 => 'накладная C'];
$queue = array_merge($morning, $evening);
foreach ($queue as $index => $document) {
    echo 'Позиция ', $index, ': ', $document, "\n";
}
$settings = array_merge(['mode' => 'draft'], ['mode' => 'live']);
echo 'Режим: ', $settings['mode'], "\n";
echo 'Первый исходный ключ 10: ', $morning[10], "\n";
Ожидаемый результат
Позиция 0: накладная A
Позиция 1: накладная B
Позиция 2: накладная C
Режим: live
Первый исходный ключ 10: накладная A

Оба числовых ключа 10 обозначают здесь технические позиции: merge добавляет все три документа и назначает 0/1/2. Строковый ключ mode действует иначе: позднее live заменяет draft. Queue и settings — два новых массива, исходные morning/evening не меняются. Если 10 был бы ID одного документа, такая склейка потеряла бы смысл ключа; тогда выбирают replace или оператор + с нужным правилом победителя.

Получить один список из уже загруженных страницСамостоятельный пример
<?php
$pages = [['p1','p2'], ['p3']];
$result = [];
foreach ($pages as $page) { foreach ($page as $id) { $result[] = $id; } }
echo json_encode($result), "\n";
echo json_encode(array_merge(...$pages)), "\n";
Ожидаемый результат
["p1","p2","p3"]
["p1","p2","p3"]

... перед массивом разворачивает его в аргументы вызова. Для большого потока прямое добавление избегает повторного слияния растущего результата.

Нюансы и выбор. Для словаря с числовыми ID обычно нужна array_replace или оператор +. Оператор + сохраняет левое значение совпавшего ключа; это иной договор. Многократный merge внутри цикла копирует растущий результат.

Сравните: array_merge_recursive, array_replace, array_replace_recursive. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Числовые ключи перенумеровываются и значения добавляются. При одинаковых строковых ключах побеждает более позднее значение. Результат — новый массив.

Собрать ошибки двух независимых проверокСамостоятельный пример
<?php
declare(strict_types=1);

$addressErrors = ['Нет города'];
$contactErrors = ['Нет телефона'];
echo implode('; ', array_merge($addressErrors, $contactErrors)), "\n";
Ожидаемый результат
Нет города; Нет телефона

Оба источника — списки сообщений. Их порядок задан аргументами; пустой список естественно вносит ничего.

Добавить обязательный заголовок клиентаСамостоятельный пример
<?php
declare(strict_types=1);

$base = ['Accept' => 'application/json', 'X-Client' => 'manual'];
$request = ['X-Client' => 'importer'];
echo array_merge($base, $request)['X-Client'], "\n";
Ожидаемый результат
importer

Это обычный словарь конфигурации; реальные HTTP-заголовки регистр независимы, поэтому канонизируйте имена или используйте библиотеку, иначе accept и Accept станут разными ключами PHP.

Сравните. array_replace заменяет по всем ключам; оператор+ сохраняет левые значения; $list[] добавляет одно значение.

array_replaceОбновить значения по ключам, включая числовые идентификаторы.

PHP 5.3+ · standard

array_replace(array $array, array ...$replacements): array

Обновить значения по ключам, включая числовые идентификаторы.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayОбязательный первый массив.
replacementsНоль или больше массивов-патчей, слева направо.
Правило совпаденияДля любого ключа побеждает самый правый вход. Новые ключи добавляются. Вложенное значение заменяется целиком, без обхода его полей.
ВозвратНовый массив; исходные массивы не изменяются. Это не глубокое клонирование объектов внутри значений.
Отказ и диагностикаНемассивный аргумент — TypeError. Рекурсивные варианты применяйте к конечным деревьям; циклические ссылки не являются поддержкой произвольного графа.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Было 10→2 и 20→5; правка 10→3, 30→1.
  2. Заменить один остаток, сохранить другой, добавить третий.
  3. Показать новый словарь с исходными ID.
Обновить остатки по числовым IDСамостоятельный пример
<?php
$stock = [10 => 2, 20 => 5];
$correction = [10 => 3, 30 => 1];
$updated = array_replace($stock, $correction);
foreach ($updated as $id => $quantity) {
    echo 'ID ', $id, ': ', $quantity, ' шт.', "\n";
}
echo 'До правки ID 10: ', $stock[10], ' шт.', "\n";
Ожидаемый результат
ID 10: 3 шт.
ID 20: 5 шт.
ID 30: 1 шт.
До правки ID 10: 2 шт.

Replace видит тот же ключ 10 и берёт позднее значение 3, сохраняет отсутствующий в правке 20→5 и добавляет 30→1. Числовые ключи не перенумеровываются. Новый массив updated используется в отчёте, а stock хранит прежнее 2. Если значение ключа само массив, обычный replace заменяет его целиком: ниже это сравнивается с рекурсивной правкой.

Увидеть замену целой вложенной настройкиСамостоятельный пример
<?php
$base = ['mail' => ['host' => 'local', 'port' => 25]];
$patch = ['mail' => ['port' => 587]];
echo json_encode(array_replace($base, $patch)), "\n";
echo json_encode($base), "\n";
Ожидаемый результат
{"mail":{"port":587}}
{"mail":{"host":"local","port":25}}

host исчез из результата, потому что заменялось всё значение mail. Исходный массив не изменён.

Нюансы и выбор. Для частичного обновления вложенных полей нужен replace_recursive. Проверяйте разрешённые ключи патча до применения; замена массива сама по себе не проверяет права.

Сравните: array_merge, array_merge_recursive, array_replace_recursive. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Возвращает массив, где более поздние аргументы заменяют значения тех же ключей. Замена поверхностная: вложенный массив является одним значением.

Переопределить тайм-аут окруженияСамостоятельный пример
<?php
declare(strict_types=1);

$defaults = ['timeout' => 5, 'retries' => 2];
$environment = ['timeout' => 15];
echo json_encode(array_replace($defaults, $environment), JSON_THROW_ON_ERROR), "\n";
Ожидаемый результат
{"timeout":15,"retries":2}

Значение повторов остается по умолчанию. После объединения проверьте диапазон и тип: строка из переменной окружения не становится int автоматически.

Обновить значения по числовым IDСамостоятельный пример
<?php
declare(strict_types=1);

$stocks = [101 => 3, 205 => 1];
$patch = [205 => 0];
echo json_encode(array_replace($stocks, $patch), JSON_THROW_ON_ERROR), "\n";
Ожидаемый результат
{"101":3,"205":0}

Ключ 205 сохранен, нулевой остаток явно заменяет старый. В базе одновременные обновления требуют транзакционной политики; массив решает только локальное объединение.

Сравните. array_merge склеивает числовые списки; array_replace_recursive нужен только при явно согласованной вложенной политике.

array_chunkРазделить список на порции: например, отправлять по два задания в одном запросе.

PHP 4.2+ · standard

array_chunk(array $array, int $length, bool $preserve_keys = false): array

Разделить список на порции: например, отправлять по два задания в одном запросе.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayМассив в текущем порядке.
lengthМаксимум элементов в одной порции, строго больше нуля.
preserve_keysfalse: каждый внутренний массив получает ключи 0, 1…; true: исходные ключи сохраняются.
ВозвратВнешний список порций с ключами от нуля. Последняя порция может быть короче; пустой вход даёт [].
Отказ и диагностикаlength < 1: ValueError в PHP 8+; прежде E_WARNING и null.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Пять заданий A…E
  2. Отделять очередные два
  3. Порции [A,B], [C,D], [E]
Использовать результат array_chunk в задачеСамостоятельный пример
<?php
$jobs = ['A', 'B', 'C', 'D', 'E'];
$batches = array_chunk($jobs, 2);
foreach ($batches as $batchIndex => $batch) {
    echo 'Порция ', $batchIndex + 1, ':', "\n";
    foreach ($batch as $job) {
        echo '  Подготовить задание ', $job, "\n";
    }
}
Ожидаемый результат
Порция 1:
  Подготовить задание A
  Подготовить задание B
Порция 2:
  Подготовить задание C
  Подготовить задание D
Порция 3:
  Подготовить задание E

Chunk вернул три вложенных массива [A,B], [C,D], [E]. Внешний foreach берёт порцию; внутренний перебирает её задания. Максимум два элемента не требует дополнять последнюю порцию: там только E. Все порции созданы в памяти; функция не делает запросы, паузы или запуск заданий.

Сохранить идентификаторы при пакетной обработкеСамостоятельный пример
<?php
$jobs = [41 => 'mail', 58 => 'report', 90 => 'export'];
foreach (array_chunk($jobs, 2, true) as $batch) {
    echo implode(',', array_keys($batch)), "
";
}
try { array_chunk($jobs, 0); }
catch (ValueError $error) { echo "Размер должен быть положительным
"; }
Ожидаемый результат
41,58
90
Размер должен быть положительным

catch перехватывает отказ вызова; как работает исключение. Порции создаются в памяти: это не потоковая загрузка из БД.

Нюансы и выбор. array_chunk не делает пауз и не отправляет запросы. Для большого источника читайте его порциями сразу, вместо загрузки всего массива и последующего дробления.

Сравните: array_slice, array_keys, array_splice. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Возвращает массив частей длиной не более length. Последняя часть может быть короче. length должен быть положительным, иначе ValueError.

Партии ID по лимиту поставщикаСамостоятельный пример
<?php
declare(strict_types=1);

$ids = [1, 2, 3, 4, 5];
foreach (array_chunk($ids, 2) as $batch) {
    echo implode(',', $batch), "\n";
}
Ожидаемый результат
1,2
3,4
5

Это только формирование пакетов, без сетевых вызовов. У API могут быть разные лимиты на число ID в запросе и на число запросов в секунду.

Сохранить артикулы при разбиении отчетаСамостоятельный пример
<?php
declare(strict_types=1);

$stocks = ['A' => 2, 'B' => 0, 'C' => 7];
foreach (array_chunk($stocks, 2, true) as $part) {
    echo implode(',', array_keys($part)), "\n";
}
Ожидаемый результат
A,B
C

Preservekeys нужен, когда ключи — смысловые метки. Для списка JSON обычно оставляют false, чтобы каждая партия была списком.

Сравните. Для большого импорта используйте генератор; частота и повторы объясняет таймеры, очередь и backoff.

array_sliceВзять окно из массива без изменения оригинала.

PHP 4+ · standard

array_slice(array $array, int $offset, ?int $length = null, bool $preserve_keys = false): array

Взять окно из массива без изменения оригинала.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayМассив в текущем порядке.
offsetПозиция с нуля, не ключ; отрицательное число отсчитывается от конца.
lengthnull/отсутствие: до конца; положительное: не больше этого количества; отрицательное: не включать столько последних элементов; 0: [].
preserve_keysfalse перенумеровывает целочисленные ключи; true сохраняет. Строковые ключи сохранены всегда.
ВозвратНовый массив-срез; за пределами входа — [].
Отказ и диагностикаТипы аргументов проверяются; выход позиции за длину не является исключением.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Записи с ID 10, 50, 90
  2. Начать с позиции 1, взять одну
  3. Получить запись ID 50
Использовать результат array_slice в задачеСамостоятельный пример
<?php
$rows = [10 => 'A', 50 => 'B', 90 => 'C'];
$window = array_slice($rows, 1, 1, true);
foreach ($window as $id => $name) {
    echo 'Выбранная запись ID ', $id, ': ', $name, "\n";
}
$renumbered = array_slice($rows, 1, 1);
echo 'Та же запись под новым ключом 0: ', $renumbered[0], "\n";
echo 'Исходная первая запись сохранена: ', $rows[10], "\n";
Ожидаемый результат
Выбранная запись ID 50: B
Та же запись под новым ключом 0: B
Исходная первая запись сохранена: A

Offset 1 означает вторую позицию в порядке массива, а не ключ 1. Length 1 берёт один элемент. Preserve_keys=true удерживает id 50; без этого флага новый числовой ключ 0. Оба вызова вернули копию окна, ни один не удалил A или другую запись из rows.

Взять хвост журнала и убрать служебный конецСамостоятельный пример
<?php
$events = ['open','load','save','close'];
echo json_encode(array_slice($events, -2)), "
";
echo json_encode(array_slice($events, 1, -1)), "
";
echo json_encode(array_slice($events, 99)), "
";
Ожидаемый результат
["save","close"]
["load","save"]
[]

Отрицательная длина — число исключаемых элементов с конца, а не обратное направление обхода.

Нюансы и выбор. Для пагинации большой таблицы задайте ограничение в запросе БД. Slice уже загруженного массива не экономит стоимость получения всех строк.

Сравните: array_splice, array_chunk, array_reverse. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Offset — позиция в порядке массива, не значение ключа. Отрицательный offset отсчитывается с конца. Числовые ключи по умолчанию перенумеровываются.

Вторая страница небольшого локального спискаСамостоятельный пример
<?php
declare(strict_types=1);

$titles = ['A', 'B', 'C', 'D', 'E'];
$page = 2;
$perPage = 2;
echo implode(',', array_slice($titles, ($page - 1) * $perPage, $perPage)), "\n";
Ожидаемый результат
C,D

Подходит для небольшого заранее загруженного списка. Page следует валидировать как положительное целое; для большой таблицы используйте SQL пагинацию.

Последние события из уже ограниченного буфераСамостоятельный пример
<?php
declare(strict_types=1);

$events = [10 => 'created', 20 => 'paid', 30 => 'shipped'];
echo json_encode(array_slice($events, -2, null, true), JSON_THROW_ON_ERROR), "\n";
Ожидаемый результат
{"20":"paid","30":"shipped"}

Числовые идентификаторы событий сохраняются. Последние в массиве не обязательно последние повремени: порядок источника нужен в контракте.

Сравните. array_chunk дает все порции; slice — один участок. Для первых N элементов потока остановите цикл после N.

array_uniqueОставить первое из совпадающих значений, сохранив его ключ.

PHP 4.0.1+ · standard

array_unique(array $array, int $flags = SORT_STRING): array

Оставить первое из совпадающих значений, сохранив его ключ.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayОдномерные значения; это не универсальное удаление одинаковых вложенных записей.
flagsSORT_STRING по умолчанию: равенство строковых представлений. SORT_NUMERIC: числовое; SORT_REGULAR: обычное сравнение; SORT_LOCALE_STRING: строки с учётом текущей locale.
ВозвратНовый массив, ключи и первое значение каждой группы сохранены; числовые ключи могут иметь пропуски.
Отказ и диагностикаНеподходящие для выбранного сравнения значения могут вызывать диагностику преобразования. Для SORT_REGULAR смешение числовых и нечисловых строк может оставить дубликаты из-за внутреннего порядка сравнения; выбирайте однородные данные и подходящий режим.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Значения red, blue, red
  2. Первое red сохранить, повтор отбросить
  3. Остаются исходные ключи 0 и 1
Удалить повтор меткиСамостоятельный пример
<?php
$tags = ['red','blue','red'];
echo json_encode(array_unique($tags)), "
";
echo json_encode(array_unique([1, '1', '01'])), "
";
echo json_encode(array_unique([1, '1', '01'], SORT_NUMERIC)), "
";
Ожидаемый результат
["red","blue"]
{"0":1,"2":"01"}
[1]

По умолчанию число 1 и строка "1" равны как строки, а "01" отличается. Числовой режим объединяет все три.

Подготовить один запрос на каждый встретившийся товарСамостоятельный пример
<?php
$rows = [['id' => 41], ['id' => 72], ['id' => 41]];
$ids = array_column($rows, 'id');
$uniqueIds = array_unique($ids, SORT_NUMERIC);
foreach ($uniqueIds as $sourcePosition => $id) {
    echo 'Подготовить товар ID ', $id, ' (первая позиция ', $sourcePosition, ')', "\n";
}
$list = array_values($uniqueIds);
echo 'Первый ID нового списка: ', $list[0], "\n";
Ожидаемый результат
Подготовить товар ID 41 (первая позиция 0)
Подготовить товар ID 72 (первая позиция 1)
Первый ID нового списка: 41

Column извлекает [41,72,41] из уже проверенных строк. Unique с числовым правилом возвращает первые 41 и 72 с ключами 0 и 1, повтор 41 не вызывает вторую подготовку. Здесь все ID — int; смешанные текстовые коды требуют другого правила равенства. Values — отдельный шаг для технических позиций нового списка. Ни unique, ни этот пример не выполняют запрос к базе: они готовят его набор ID.

Нюансы и выбор. Для уникальности записей по составному ключу постройте явный индекс с договором о первом/последнем совпадении. PHP 7.2 изменил построение результата SORT_STRING: последующий автоматический индекс при добавлении может отличаться от старой реализации, хотя сохранённые пары те же.

Сравните: array_count_values, array_column, array_values, array_diff. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Сохраняет первый встретившийся элемент и его ключ. Режим сравнения задается flags; по умолчанию используется строковое сравнение.

Уникальные артикулы из повторных позицийСамостоятельный пример
<?php
declare(strict_types=1);

$skus = ['A', 'B', 'A'];
echo json_encode(array_values(array_unique($skus)), JSON_THROW_ON_ERROR), "\n";
Ожидаемый результат
["A","B"]

Подходит для проверенных строк SKU. Если повторы означают несколько заказанных единиц, нельзя удалять их без агрегации количества.

Список адресатов после канонизацииСамостоятельный пример
<?php
declare(strict_types=1);

$addresses = ['ops@example.test', 'sales@example.test', 'ops@example.test'];
echo count(array_unique($addresses, SORT_STRING)), "\n";
Ожидаемый результат
2

Это учебный список уже проверенных канонических адресов. Разная политика нормализации email должна решаться раньше; array_unique ее не выбирает.

Сравните. Для записей стройте проверенный индекс по уникальному ключу; array_values возвращает плотный список.

array_diffРазность массивов: оставить пары первого входа, для которых совпадение не найдено ни в одном из остальных массивов. Только значения; ключи остальных массивов не участвуют.

PHP 4.0.1+ · standard

array_diff(array $array, array ...$arrays): array

Разность массивов: оставить пары первого входа, для которых совпадение не найдено ни в одном из остальных массивов. Только значения; ключи остальных массивов не участвуют.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayПервый массив — источник возвращаемых ключей, значений и порядка.
arraysОстальные массивы для сравнения. Без них допустим вызов с PHP 8: возвращается исходное содержимое.
Что совпадаетТолько значения; ключи остальных массивов не участвуют. Значения равны, когда их строковые представления совпадают: 10 и "10" равны, 10 и "010" различаются. Это не строгое сравнение исходных типов.
ВозвратНовый массив с исходными ключами/значениями/порядком первого аргумента; повторы подходящих значений не убираются.
Отказ и диагностикаВложенные массивы здесь не сравниваются рекурсивно: приведение массива к строке вызывает warning, объект без строкового преобразования — Error. Немассивные входы — TypeError.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Первый массив задаёт кандидатов
  2. Сверить значения с остальными
  3. Сохранить пару, если совпадение не найдено ни в одном из остальных массивов
Увидеть, что именно сравниваетсяСамостоятельный пример
<?php
$a = [4 => 10, 7 => 20, 9 => 10];
$b = ["x" => "10"];
echo json_encode(array_diff($a, $b)), "\n";
Ожидаемый результат
{"7":20}

Только значения; ключи остальных массивов не участвуют. Ключи результата взяты из $a.

Теперь применим правило первого опыта к данным задачи. Первый массив задаёт, какие значения и ключи можно вернуть. Следующие массивы служат только для сравнения; они не дополняют результат новыми записями.

Составить список оставшейся работыСамостоятельный пример
<?php
$requested = ['mail', 'export', 'report', 'mail'];
$groupA = ['mail', 'report'];
$groupB = ['mail'];
$selected = array_diff($requested, $groupA, $groupB);
foreach ($selected as $position => $task) {
    echo "Ещё не назначено: ", $task, " (позиция ", $position, ")\n";
}
Ожидаемый результат
Ещё не назначено: export (позиция 1)

Diff исключает всё, что встречается хотя бы в одной группе: mail и report заняты, остаётся export. Selected содержит исходные ключи requested; foreach использует их для связи с исходной заявкой. Ключи групп не сравниваются. Выход — план, реальная отправка писем/экспорт в этом примере не выполняются.

Нюансы и выбор. Проверяется один уровень. Для вложенной записи выберите конкретное поле или собственный компаратор; не подменяйте структуру строкой "Array". Если нужна JSON-последовательность, явный array_values допустим только когда исходные ключи не несут смысла.

Сравните: array_diff_assoc, array_diff_key, array_udiff, array_unique. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Сравнивает значения на одном уровне и сохраняет ключи первого массива. Стандартное сравнение основано на строковом представлении значений.

Новые подписки относительно сохраненныхСамостоятельный пример
<?php
declare(strict_types=1);

$requested = ['news', 'offers', 'events'];
$existing = ['news'];
echo implode(',', array_diff($requested, $existing)), "\n";
Ожидаемый результат
offers,events

Это разность разрешенных строковых кодов аргумента. Проверку прав и набор допустимых кодовых значений выполните отдельно.

Неизвестные колонки импортаСамостоятельный пример
<?php
declare(strict_types=1);

$receivedColumns = ['sku', 'stock', 'admin'];
$allowedColumns = ['sku', 'stock'];
echo implode(',', array_diff($receivedColumns, $allowedColumns)), "\n";
Ожидаемый результат
admin

Проверка колонок позволяет отклонить неожиданную схему. Чтобы найти обязательные отсутствующие поля, поменяйте массивы местами.

Сравните. array_diff_key сравнивает ключи; array_intersect выбирает общие значения.

array_intersectПересечение массивов: оставить пары первого входа, для которых совпадение найдено в каждом из остальных массивов. Только значения; ключи остальных массивов не участвуют.

PHP 4.0.1+ · standard

array_intersect(array $array, array ...$arrays): array

Пересечение массивов: оставить пары первого входа, для которых совпадение найдено в каждом из остальных массивов. Только значения; ключи остальных массивов не участвуют.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayПервый массив — источник возвращаемых ключей, значений и порядка.
arraysОстальные массивы для сравнения. Без них допустим вызов с PHP 8: возвращается исходное содержимое.
Что совпадаетТолько значения; ключи остальных массивов не участвуют. Значения равны, когда их строковые представления совпадают: 10 и "10" равны, 10 и "010" различаются. Это не строгое сравнение исходных типов.
ВозвратНовый массив с исходными ключами/значениями/порядком первого аргумента; повторы подходящих значений не убираются.
Отказ и диагностикаВложенные массивы здесь не сравниваются рекурсивно: приведение массива к строке вызывает warning, объект без строкового преобразования — Error. Немассивные входы — TypeError.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Первый массив задаёт кандидатов
  2. Сверить значения с остальными
  3. Сохранить пару, если совпадение найдено в каждом из остальных массивов
Увидеть, что именно сравниваетсяСамостоятельный пример
<?php
$a = [4 => 10, 7 => 20, 9 => 10];
$b = ["x" => "10"];
echo json_encode(array_intersect($a, $b)), "\n";
Ожидаемый результат
{"4":10,"9":10}

Только значения; ключи остальных массивов не участвуют. Ключи результата взяты из $a.

Теперь применим правило первого опыта к данным задачи. Первый массив задаёт, какие значения и ключи можно вернуть. Следующие массивы служат только для сравнения; они не дополняют результат новыми записями.

Выбрать возможности для совместной работы системСамостоятельный пример
<?php
$requested = ['mail', 'export', 'report', 'mail'];
$groupA = ['mail', 'report'];
$groupB = ['mail'];
$selected = array_intersect($requested, $groupA, $groupB);
foreach ($selected as $position => $task) {
    echo "Поддерживается обеими системами: ", $task, " (позиция ", $position, ")\n";
}
Ожидаемый результат
Поддерживается обеими системами: mail (позиция 0)
Поддерживается обеими системами: mail (позиция 3)

Intersect требует наличия значения в каждой последующей группе: report есть только у A, поэтому остаётся mail. Оба вхождения mail сохраняются: операция не удаляет повторы. Selected содержит исходные ключи requested; foreach использует их для связи с исходной заявкой. Ключи групп не сравниваются. Выход — план, реальная отправка писем/экспорт в этом примере не выполняются.

Нюансы и выбор. Проверяется один уровень. Для вложенной записи выберите конкретное поле или собственный компаратор; не подменяйте структуру строкой "Array". Если нужна JSON-последовательность, явный array_values допустим только когда исходные ключи не несут смысла.

Сравните: array_intersect_assoc, array_intersect_key, array_uintersect, array_unique. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Возвращает элементы первого массива, совпадающие со значениями остальных. Сохраняет ключи и возможные повторы первого массива.

Оставить только поддерживаемые форматы отчетаСамостоятельный пример
<?php
declare(strict_types=1);

$requested = ['csv', 'pdf', 'exe'];
$supported = ['csv', 'pdf'];
echo implode(',', array_intersect($requested, $supported)), "\n";
Ожидаемый результат
csv,pdf

Пересечение применяется к явному разрешенному набору. Результат может быть пустым — сообщите это, а не выбирайте случайный формат.

Общие теги двух карточекСамостоятельный пример
<?php
declare(strict_types=1);

$first = ['php', 'api', 'shop'];
$second = ['api', 'security'];
echo implode(',', array_values(array_intersect($first, $second))), "\n";
Ожидаемый результат
api

При ранжировании похожих статей общие теги — лишь признак. Если коллекции большие, измерьте стоимость и подумайте об индексе/поиске хранилища.

Сравните. array_intersect_key работает по ключам; in_array со strict проверяет одно значение.

in_arrayПроверить присутствие конкретного значения, например кода разрешённого статуса.

PHP 4+ · standard

in_array(mixed $needle, array $haystack, bool $strict = false): bool

Проверить присутствие конкретного значения, например кода разрешённого статуса.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
needleИскомое значение. Строки сравниваются с учётом регистра.
haystackМассив значений для поиска; ключи не проверяются.
stricttrue требует ===; по умолчанию PHP допускает нестрогое равенство разных типов.
Возвратtrue при наличии совпадения, иначе false. Исходный массив не меняется.
Отказ и диагностикаНеверный тип haystack даёт TypeError. Само отсутствие значения не является ошибкой.
Что происходит в первом опыте
  1. Разрешены строки new и paid.
  2. Проверяем строку paid со strict=true.
  3. Тип и значение совпали → true; ключи массива не участвуют.
Использовать результат in_array в задачеСамостоятельный пример
<?php
$allowed = ['new', 'paid'];
$status = 'paid';
if (in_array($status, $allowed, true)) {
    echo 'Фильтр списка заказов принят: ', $status, "\n";
}
$status = 'PAID';
if (!in_array($status, $allowed, true)) {
    echo 'Фильтр отклонён: неизвестный код ', $status, "\n";
}
Ожидаемый результат
Фильтр списка заказов принят: paid
Фильтр отклонён: неизвестный код PAID

Needle — status, haystack — allowed, strict=true запрещает совпадение с другим типом. Возврат bool прямо выбирает сообщение. Ищутся значения new/paid, не их ключи 0/1. Регистр различается; если протокол допускает нормализацию, это отдельное согласованное действие до проверки.

Различить строковый код и числоСамостоятельный пример
<?php
var_dump(in_array('0', [0], false));
var_dump(in_array('0', [0], true));
var_dump(in_array(true, ['approved'], false));
Ожидаемый результат
bool(true)
bool(false)
bool(true)

Нестрогое равенство может принять bool true за непустую строку. Исправление сравнения числа с нечисловой строкой в PHP 8 не отменило остальные преобразования.

Нюансы и выбор. Поиск обычно просматривает элементы до совпадения; для множества повторных проверок имеет смысл заранее построить индекс, если допустимы его преобразования ключей. Нестрогое сравнение не заменяет валидацию типов. Для объектов strict означает тот же экземпляр, а не просто похожие поля. До PHP 8 число 0 могло нестрого совпадать с нечисловой строкой; на такое старое поведение рассчитывать нельзя.

Сравните: array_search, array_key_exists, array_fill_keys, array_any. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Возвращает bool. Установите strict=true, если тип является частью контракта; поиск просматривает значения, а ключи не участвуют.

Разрешенный способ сортировкиСамостоятельный пример
<?php
declare(strict_types=1);

$sort = 'price';
$allowed = ['price', 'name', 'created'];
echo in_array($sort, $allowed, true) ? "Разрешено\n" : "Неизвестное поле\n";
Ожидаемый результат
Разрешено

Для SQL после сверки используют таблицу соответствия разрешенных полей. Нельзя параметризовать имя колонки тем же способом, что значение.

Различить текстовый и целочисленный IDСамостоятельный пример
<?php
declare(strict_types=1);

$allowedIds = [10, 20];
echo in_array('10', $allowedIds, true) ? "Есть\n" : "Нужна нормализация\n";
Ожидаемый результат
Нужна нормализация

Внешний ввод сначала проверяют и нормализуют, после этого строгое сравнение не создает неожиданных совпадений.

Сравните. array_search возвращает ключ; array_key_exists проверяет словарь по ключу.

usortЗадать своё правило порядка значений, получив новые числовые ключи.

PHP 4+ · standard

usort(array &$array, callable $callback): true

Задать своё правило порядка значений, получив новые числовые ключи.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayИзменяемый массив; все старые ключи заменены индексами с нуля.
callbackcallback(a,b): int сравнивает значения. Допустим любой отрицательный/нулевой/положительный int, не только -1/0/1.
Возвратtrue; тип уточнён с bool в PHP 8.2. Массив находится в первом аргументе.
Отказ и диагностикаС PHP 8 параметры callback по ссылке вызывают E_WARNING. Float приводится к int, например 0.6 превращается в 0. Булевый компаратор не задаёт три исхода; возврат bool устарел с PHP 8. Ошибки callback распространяются наружу.

Все равные по правилу сравнения элементы сохраняют взаимный порядок с PHP 8.0; раньше он не гарантировался. Указатель массива сбрасывается на первый элемент. Массив меняется на месте: true — статус, не отсортированные данные. В сигнатуре & означает передачу изменяемой переменной; в вызове знак & не ставится. Компаратор — функция сравнения двух элементов. Она возвращает целое число: отрицательное — первый раньше, 0 — равны по выбранному признаку, положительное — первый позже. $a <=> $b даёт -1, 0 или 1. Это не проверка true/false; разность дробей нельзя возвращать вместо целого результата. function ($a, $b) { return ...; } передаёт правило операции, а не вызывает его немедленно.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Для этого примера: передача функции как значения; оператор <=> и условный выбор.

Что происходит в первом опыте
  1. Есть массы 2.7,2.1,2.4.
  2. Callback сравнивает пару масс и возвращает знак целым числом.
  3. В weights получен порядок 2.1,2.4,2.7; его показывает foreach.
Упорядочить массы собственным правилом сравненияСамостоятельный пример
<?php
$weights = ['large' => 2.7, 'small' => 2.1, 'middle' => 2.4];
usort($weights, function ($left, $right) {
    return $left <=> $right;
});
foreach ($weights as $key => $weight) {
    echo 'Позиция ', $key, ': ', $weight, " кг\n";
}
Ожидаемый результат
Позиция 0: 2.1 кг
Позиция 1: 2.4 кг
Позиция 2: 2.7 кг

Первый аргумент — изменяемый массив, второй — функция как значение. Алгоритм сам многократно передаёт в left/right две массы; конкретный порядок вызовов не обещан. Оператор <=> возвращает int: например, для 2.1 и 2.7 это -1, для обратной пары 1, для одинаковых значений 0. Этот return поступает в алгоритм сортировки, а не в weights. После завершения внешний foreach читает массы в новом порядке. Имена large/small/middle заменены позициями 0/1/2; для сохранения имён нужна uasort. Дробную разность left-right возвращать нельзя: приведение к int превратило бы 0.6 в 0.

Сортировка заказов с явным вторым критериемСамостоятельный пример
<?php
$orders = ["c" => ["priority" => 2, "id" => 30], "a" => ["priority" => 1, "id" => 20], "b" => ["priority" => 1, "id" => 10]];
usort($orders, function ($a, $b) {
    $byPriority = $a["priority"] <=> $b["priority"];
    return $byPriority !== 0 ? $byPriority : $a["id"] <=> $b["id"];
});
echo json_encode(array_column($orders, "id")), "\n";
echo json_encode(array_keys($orders)), "\n";
Ожидаемый результат
[10,20,30]
[0,1,2]

Сначала сравниваем приоритет, при равенстве — ID. ?: выбирает один результат; <=> вычисляет целое сравнение даже для float.

Нюансы и выбор. Компаратор должен задавать согласованный порядок, а не решать «эти похожи»: нарушенная транзитивность делает результат ненадёжным. Не выполняйте внутри каждого сравнения сетевой запрос или дорогое преобразование; предварительно вычислите сортировочные поля. Два поля с равным критерием сохраняют исходный порядок в PHP 8, но порядок исходного ответа БД сам по себе может быть не определён без ORDER BY.

Сравните: uasort, uksort, sort, array_multisort. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Меняет массив на месте и перенумеровывает ключи. Компаратор возвращает int меньше, равный или больше нуля; не нужно знать точную величину.

Дешевые товары раньше, при равенстве — по SKUСамостоятельный пример
<?php
declare(strict_types=1);

$rows = [['sku' => 'B', 'price' => 100], ['sku' => 'A', 'price' => 100], ['sku' => 'C', 'price' => 50]];
usort($rows, static fn ($a, $b): int => ($a['price'] <=> $b['price']) ?: ($a['sku'] <=> $b['sku']));
echo implode(',', array_column($rows, 'sku')), "\n";
Ожидаемый результат
C,A,B

Вторичный ключ делает порядок предсказуемым. Здесь цены уже одной валюты и единицы; сортировка разных валют без конвертации бессмысленна.

Сначала более срочные задачиСамостоятельный пример
<?php
declare(strict_types=1);

$tasks = [['id' => 1, 'priority' => 1], ['id' => 2, 'priority' => 5]];
usort($tasks, static fn ($a, $b): int => $b['priority'] <=> $a['priority']);
echo implode(',', array_column($tasks, 'id')), "\n";
Ожидаемый результат
2,1

Обратный порядок аргументов дает убывание. Для настоящей очереди нужны еще справедливость, блокировки и защита от вечного голодания низкого приоритета.

Сравните. uasort сохраняет ключи, sort сортирует простые значения, ksort сортирует ключи.

ksortУпорядочить ключи по возрастанию, сохранив связь ключей и значений.

PHP 4+ · standard

ksort(array &$array, int $flags = SORT_REGULAR): true

Упорядочить ключи по возрастанию, сохранив связь ключей и значений.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayИзменяемая переменная-массив; ключи не теряются.
flagsSORT_REGULAR — обычные правила PHP; SORT_NUMERIC — числовые; SORT_STRING — строковые; SORT_LOCALE_STRING — строки в текущей locale; SORT_NATURAL — естественный порядок числовых частей строк. SORT_FLAG_CASE добавляют через | только к SORT_STRING/SORT_NATURAL для нечувствительности к ASCII-регистру (с PHP 8.2 только ASCII).
Возвратtrue; тип объявлен как true с PHP 8.2, ранее bool. Сам результат сортировки находится в первом аргументе.
Отказ и диагностикаНеподходящий тип array — TypeError. Преобразование значений, несовместимых с режимом, может вызвать диагностику. SORT_REGULAR на смешанных типах часто не соответствует предметному правилу.

Все равные по правилу сравнения элементы сохраняют взаимный порядок с PHP 8.0; раньше он не гарантировался. Указатель массива сбрасывается на первый элемент. Массив меняется на месте: true — статус, не отсортированные данные. В сигнатуре & означает передачу изменяемой переменной; в вызове знак & не ставится. В PHP 8.2 сравнение числовых строковых ключей SORT_REGULAR приведено к стандартным правилам PHP 8.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Исходный порядок ID 30,10,20.
  2. Сравнить только ключи по возрастанию.
  3. Пройти изменённый invoices и показать накладные по ID.
Получить понятный упорядоченный отчётСамостоятельный пример
<?php
$invoices = [30 => 'накладная C', 10 => 'накладная A', 20 => 'накладная B'];
$status = ksort($invoices, SORT_NUMERIC);
if ($status) { echo "Накладные упорядочены\n"; }
foreach ($invoices as $id => $title) { echo "ID ", $id, ": ", $title, "\n"; }
Ожидаемый результат
Накладные упорядочены
ID 10: накладная A
ID 20: накладная B
ID 30: накладная C

Сравниваются числовые ключи 30/10/20; текст накладной в сравнении не участвует. SORT_NUMERIC явно выбирает числовой порядок. Значение перемещается вместе со своим ID. Возврат status — true; результат сортировки находится в изменившемся invoices и сразу используется для отчёта.

Упорядочить нумерованные части отчётаСамостоятельный пример
<?php
$files = ["part10" => "ten", "part2" => "two", "Part1" => "one"];
ksort($files, SORT_NATURAL | SORT_FLAG_CASE);
echo json_encode($files), "\n";
Ожидаемый результат
{"Part1":"one","part2":"two","part10":"ten"}

Часть 2 сравнивается как число и идёт перед частью 10 при возрастании. Флаг CASE убирает различие ASCII-регистра, а не меняет сами строки.

Нюансы и выбор. Для русских названий и языковых правил нужна подходящая locale/Collator, а не обещание универсального «алфавита». Не полагайтесь на конкретный алгоритм, число сравнений или скорость на любом входе. До сортировки нормализуйте сравниваемый тип; чтение БД уже в нужном порядке может быть выгоднее сортировки огромного PHP-массива.

Сравните: sort, asort, usort, array_multisort. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Сортирует на месте по ключам, сохраняя их связь со значениями. Для строковых кодов выберите SORT_STRING, чтобы намерение было явным.

Стабильный порядок строк отчета по SKUСамостоятельный пример
<?php
declare(strict_types=1);

$stocks = ['B' => 2, 'A' => 0];
ksort($stocks, SORT_STRING);
echo json_encode($stocks, JSON_THROW_ON_ERROR), "\n";
Ожидаемый результат
{"A":0,"B":2}

Так отчет легче сравнивать в diff. Это оформление, а не изменение остатков или порядка клиентского заказа.

Числовые дни месяцаСамостоятельный пример
<?php
declare(strict_types=1);

$sales = [12 => 300, 2 => 100];
ksort($sales, SORT_NUMERIC);
echo implode(',', array_keys($sales)), "\n";
Ожидаемый результат
2,12

Режим числового сравнения объясняет порядок. Для полных дат ISO в формате Y-m-d строковый порядок обычно соответствует календарному при едином формате.

Сравните. krsort —обратный порядок, usort —сортировка по значениям с правилом.

countПолучить число элементов или количество, определённое объектом Countable.

PHP 4+ · standard

count(Countable|array $value, int $mode = COUNT_NORMAL): int

Получить число элементов или количество, определённое объектом Countable.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
valueМассив либо объект, реализующий Countable::count(). Не любая переменная или объект является countable.
modeCOUNT_NORMAL (0) считает верхний уровень. COUNT_RECURSIVE (1) дополнительно считает элементы вложенных массивов; вложенные контейнеры тоже учитываются.
ВозвратЦелое количество элементов. Для Countable вызывается его собственный метод count; это не обязательно число публичных свойств.
Отказ и диагностикаВ PHP 8 неподходящий тип даёт TypeError. В PHP 7.2 появилась диагностика для неподходящих типов; ранее не-null скаляр давал 1, null — 0. Рекурсивная ссылка при COUNT_RECURSIVE вызывает E_WARNING и не является корректным счётом листьев.
Что происходит в первом опыте
  1. Есть две строки: A с 3 упаковками и B с 1.
  2. Посчитать строки отдельно от суммы количеств.
  3. Показать 2 позиции и 4 упаковки.
Отличить число строк заказа от числа упаковокСамостоятельный пример
<?php
$cart = ['A' => 3, 'B' => 1];
$lineCount = count($cart);
$packageCount = array_sum($cart);
echo 'Разных позиций: ', $lineCount, "\n";
echo 'Упаковок к сборке: ', $packageCount, "\n";
if ($lineCount === 0) { echo "Корзина пуста\n"; }
Ожидаемый результат
Разных позиций: 2
Упаковок к сборке: 4

count возвращает 2, потому что на верхнем уровне две пары: A→3 и B→1. Этот результат отвечает за число разных строк; array_sum отдельно возвращает 4 упаковки. Наличие вложенных полей у строки не увеличило бы обычный count. Рекурсивный режим ниже считает также элементы контейнеров и не заменяет сумму количеств.

Вложенные контейнеры тоже входят в счётСамостоятельный пример
<?php
$groups = [['A', 'B'], ['C']];
echo count($groups), "\n";
echo count($groups, COUNT_RECURSIVE), "\n";
try {
    count(null);
} catch (TypeError) {
    echo "Ожидался массив или Countable\n";
}
Ожидаемый результат
2
5
Ожидался массив или Countable

Рекурсивно получилось 2 контейнера + 3 значения. Это не число товаров-листьев. На границе API null сначала обрабатывают по договору, а не автоматически превращают в пустой список.

Нюансы и выбор. Обычный размер массива хранится в его структуре; не объявляйте count линейным обходом каждого элемента. Рекурсивный режим действительно обходит вложенные массивы, а пользовательский Countable может выполнять произвольную работу. Последний числовой ключ не равен количеству. Ссылки на собственный массив не превращают COUNT_RECURSIVE в безопасный универсальный обход графа.

Сравните: sizeof, array_sum, array_key_last. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Возвращает число элементов верхнего уровня. COUNT_RECURSIVE подсчитывает вложенные массивы со собой семантикой, это не «число товаров» без знания схемы.

Пустая корзина до оформленияСамостоятельный пример
<?php
declare(strict_types=1);

$items = [];
echo count($items) === 0 ? "Добавьте товары\n" : "Можно оформить\n";
Ожидаемый результат
Добавьте товары

Число позиций не равно сумме количеств: три товара в одной строке — одна ячейка массива.

Проверить число колонок CSVСамостоятельный пример
<?php
declare(strict_types=1);

$row = ['A', 'Tea', '2'];
echo count($row) === 3 ? "Форма строки верна\n" : "Лишние колонки\n";
Ожидаемый результат
Форма строки верна

Это проверка ширины записи, а не валидности содержимого. Затем проверьте SKU, название и числовой остаток.

Сравните. strlen считает байты; SQLCOUNT считает строки без загрузки всех данных в PHP.

array_is_listПроверить, что массив действительно является последовательностью с ключами 0,1,2,… в этом порядке.

PHP 8.1+ · standard

array_is_list(array $array): bool

Проверить, что массив действительно является последовательностью с ключами 0,1,2,… в этом порядке.

Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.

Вход или результатДоговор
arrayПроверяемый массив; его значения не обязаны иметь один тип.
Возвратtrue для последовательных ключей от 0 до count−1 в текущем порядке; пустой массив тоже является списком.
Отказ и диагностикаДругой тип аргумента вызывает TypeError. Функция только проверяет и не исправляет ключи.

В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.

Что происходит в первом опыте
  1. Удалить Кофе: останутся ключи 0 и 2.
  2. Проверка списка обнаружит пропуск.
  3. Получить новые позиции 0/1 и показать 1/2 человеку.
Восстановить позиции списка после удаленияСамостоятельный пример
<?php
$items = ['Чай', 'Кофе', 'Вода'];
unset($items[1]);
if (!array_is_list($items)) {
    echo "После удаления позиции нужно перенумеровать\n";
    $items = array_values($items);
}
foreach ($items as $index => $name) {
    echo 'Позиция ', $index + 1, ': ', $name, "\n";
}
Ожидаемый результат
После удаления позиции нужно перенумеровать
Позиция 1: Чай
Позиция 2: Вода

После unset остаются ключи 0 и 2. Array_is_list возвращает false; знак ! обращает его в true, поэтому выполняется ветка восстановления. Array_values возвращает список с ключами 0 и 1, который сохраняется обратно в items. На экране номера для человека увеличены на 1. Это позиции, а не ID товаров; словарь с ID нельзя так перенумеровывать.

Вернуть список товаров после фильтраСамостоятельный пример
<?php
$items = [0 => 'A', 2 => 'C'];
$response = array_values($items);
var_dump(array_is_list($response));
echo json_encode($response), "\n";
var_dump(array_is_list([1 => 'B', 0 => 'A']));
Ожидаемый результат
bool(true)
["A","C"]
bool(false)

array_values уместен, потому что исходные индексы здесь технические. Даже набор ключей 0 и 1 в обратном порядке не является списком.

Нюансы и выбор. Наличие всех ключей без правильного порядка недостаточно. Числовые строковые ключи могли уже преобразоваться при создании массива; функция видит фактические ключи. Для словаря ID→запись перенумерация уничтожает идентификаторы: не применяйте array_values просто ради прохождения проверки.

Сравните: array_values, array_keys, array_filter. Выбрать операцию по задаче.

Продолжение: ранее разобранная практическая задача.

Возвращает true для пустого массива или массива с целыми последовательными ключами, начинающими с 0. Значения могут быть любого типа.

Проверить поле items в JSON-контрактеСамостоятельный пример
<?php
declare(strict_types=1);

$items = [['sku' => 'A'], ['sku' => 'B']];
echo array_is_list($items) ? "Список\n" : "Нужен список\n";
Ожидаемый результат
Список

После этого проверяйте каждую запись внутри. Список скаляров также прошел бы array_is_list, хотя контракт заказа он нарушает.

Обнаружить дырку после удаленияСамостоятельный пример
<?php
declare(strict_types=1);

$ids = [0 => 10, 2 => 30];
echo array_is_list($ids) ? "list\n" : "map\n";
echo array_is_list(array_values($ids)) ? "list\n" : "map\n";
Ожидаемый результат
map
list

Это объясняет разный вид JSON после unset. Восстанавливайте плотные индексы только для коллекции, где позиционные ключи не имеют смысла.

Сравните. array_values принудительно создает список, но сначала решите, можно ли отбросить ключи.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.