Глава 17 · С нуля → практика
Массивы: списки, словари и выбор операции
От органайзера с ключами и значениями до корзины, импорта и ответа API: запись, чтение, изменение, обход, копирование и выбор операции.
Обычный массив: один список покупок вместо отдельных переменных
Покупатель выбрал чай и кофе. Нужно сохранить список, прочитать нужный элемент, затем добавить какао. Массив — упорядоченный набор пар «ключ → значение». У простого списка ключи PHP назначит сам: 0 и 1. Сначала оба значения — обычные строки; вложенных массивов здесь ещё нет.
- Ключ 0 → строка Чай.
- Ключ 1 → строка Кофе.
- $shopping[0] выбирает ячейку с ключом 0 → Чай.
- 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 стоит за циклом и выполняется один раз.
| Состояние | Что происходит |
|---|---|
| До foreach | total = 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(), запятые и пустой набор
Для списка доставки нужно хранить несколько названий. Сначала просто создадим набор и прочитаем один элемент. Здесь ещё нет ни цикла, ни функции для обработки данных.
- ["Чай", "Кофе"] → два элемента
- Первому назначен ключ 0, второму — 1
- $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 и пустой массив [] — разные значения. Массив может содержать числа, строки, логические значения и вложенные массивы; одинаковый тип элементов сам собой не проверяется.
Именованные поля: ключ — это адрес, а значение — содержимое
- Ключ name → строка «Чай»
- Ключ stock → число 12
- Ключ 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]; фигурные скобки вокруг подстановки в строке выполняют другую роль.
Внутри одного массива допустимы и строковые, и целочисленные ключи. На практике договор о структуре обычно проще: либо список карточек, либо одна карточка с полями, либо словарь по артикулу. Читателю и следующему участку программы важно понимать, какая модель используется. Когда ключ действительно задан константой разобрано отдельно.
Изменить, добавить, удалить: три разных действия
- В stock лежит 12
- Присвоить stock = 9 → заменить значение
- Присвоить reserved = 3 → добавить поле
- 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; пустая строка не заменяется. |
- Проверить ключ stock: он есть.
- Получить stock = 0 и показать фактический остаток.
- Проверить isset(delivery): false, потому что поле равно null.
- Показать осмысленную подпись «Срок доставки уточняется».
<?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" и 12 | int 12 | Обращаются к одной ячейке; позднее значение заменяет раннее. |
| "012" и "+12" | Строковые ключи | Не превращаются в int 12. |
| true и false | int 1 и int 0 | Могут совпасть с уже существующими числовыми ключами. |
| null | Пустая строка | Совпадает с ключом "". |
| float | int с отброшенной дробной частью | Неявная потеря дроби вызывает 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.
- Начальные ключи: 0 → A, 1 → B, 2 → C.
- unset удаляет B и C; остаётся ключ 0.
- Добавление [] создаёт ключ 3 → D.
- 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
$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 выбирают только для списка: у словаря переиндексация уничтожит значимые артикулы.
Массив внутри массива: читаем путь по одному шагу
- $order → вся запись заказа
- ["items"] → список позиций
- [0] → первая карточка
- ["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: прочитать каждую пару без ручного подсчёта индексов
- A17 → 3: присвоить $sku и $stock, выполнить тело
- B20 → 0: присвоить следующую пару, выполнить тело
- Пары закончились → продолжить после цикла
<?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] = ... | Запишет результат именно под исходным артикулом, сохранив связь товара с числом. |
- stocks получает весь массив; units получает 5.
- Создаётся отдельный пустой result.
- Для TEA-01 считается 3 + 5 → result[TEA-01] = 8.
- Для COF-02 считается 0 + 5 → result[COF-02] = 5.
- return отдаёт [TEA-01 → 8, COF-02 → 5] в ожидающее присваивание updated.
- Внешний цикл сравнивает 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
- &$stock связан с первым элементом
- Затем связан со следующим
- После последнего прохода связь остаётся
- 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: упаковка значения не равна проверке данных
<?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.
Фильтровать, преобразовать, свернуть
<?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) | Линейный поиск; для частых проверок подумать об индексе |
| Есть ли ключ даже с null | array_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Оставить только подходящие элементы; значения и исходные ключи сохраняются.
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". |
| mode | 0: 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 только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
Для этого примера: передача функции как значения.
- Остатки 0, 3, 0, 7
- Проверить value > 0
- Сохранить ключи 1 и 3
<?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Получить новое значение для каждого элемента или собрать параллельные списки по позициям.
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 только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
Для этого примера: передача функции как значения.
- Цены 100 и 250 копеек
- Для каждого значения прибавить 20
- Новый массив 120 и 270
<?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. При одном входном массиве ключи сохраняются; при нескольких результирующие ключи числовые. Исходный массив не заменяется автоматически.
<?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Свести все элементы к одному итогу: числу, строке или накопленному массиву.
array_reduce(array $array, callable $callback, mixed $initial = null): mixedЗадача и маршрут результата. Нужна одна сумма для оплаты. Callback принимает предыдущую сумму и очередную строку, возвращает следующую сумму. Последний return становится результатом всей array_reduce и участвует в расчёте с доставкой.
Свести все элементы к одному итогу: числу, строке или накопленному массиву.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| array | Значения в текущем порядке; ключи callback не получает. |
| callback | callback(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.
Для этого примера: передача функции как значения.
- Начальный итог 0
- Добавить 100, затем 250
- Вернуть 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 короче. Здесь виден договор каждого шага.
<?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.
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 только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Строки: A со stock=0 и B со stock=3.
- Из каждой берётся поле stock; ключ берётся из sku.
- Результат: A=0, B=3.
<?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 из строк базы.
<?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.
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. Преобразования некоторых ключей могут давать диагностику; не используйте ресурс как бизнес-ключ. |
- В сохранённой записи есть текст комментария.
- Наличие ключа с null разрешает очистку.
- Пустой массив правки не меняет запись.
<?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,…
array_values(array $array): arrayОставить значения в текущем порядке и заменить ключи на последовательные 0,1,2,…
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| array | Любой массив; исходные ключи сознательно отбрасываются. |
| Возврат | Новый список значений. Порядок обхода сохраняется, вложенные ключи не меняются. |
| Отказ и диагностика | Аргумент должен быть массивом; другой тип даёт TypeError. |
В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Вход: 0→A, 2→C.
- Значения A,C читаются в текущем порядке.
- Результат получает новые ключи 0,1; исходный массив остаётся с 0,2.
<?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.
<?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 и для передачи позиционных данных.
<?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Получить имена полей или все ключи, у которых найдено заданное значение.
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, а не отсутствие фильтра. |
| strict | true использует === и учитывает тип; по умолчанию нестрогое сравнение. |
| Возврат | Новый список ключей int/string, перенумерованный с 0. Исходный массив не меняется. |
| Отказ и диагностика | Неверный тип array вызывает TypeError. Пустой набор совпадений даёт []. |
В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- stocks хранит A=0, B=2, C=0.
- Без фильтра результат содержит A,B,C.
- С фильтром 0 и strict=true остаются A,C.
<?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Склеить списки и применить поздние значения строковых ключей.
array_merge(array ...$arrays): arrayСклеить списки и применить поздние значения строковых ключей.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| arrays | Ноль или больше массивов; обрабатываются слева направо. Без аргументов [] с PHP 7.4. |
| Правило совпадения | Строковый ключ: позднее значение заменяет раннее. Целочисленные ключи: все значения добавляются и перенумеровываются с нуля. |
| Возврат | Новый массив; исходные массивы не изменяются. Это не глубокое клонирование объектов внутри значений. |
| Отказ и диагностика | Немассивный аргумент — TypeError. Рекурсивные варианты применяйте к конечным деревьям; циклические ссылки не являются поддержкой произвольного графа. |
В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Два списка содержат три документа, в обоих есть числовой ключ 10.
- Merge собирает три позиции 0,1,2; совпавший строковый mode заменяется.
- Вывести очередь и режим из новых массивов.
<?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Обновить значения по ключам, включая числовые идентификаторы.
array_replace(array $array, array ...$replacements): arrayОбновить значения по ключам, включая числовые идентификаторы.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| array | Обязательный первый массив. |
| replacements | Ноль или больше массивов-патчей, слева направо. |
| Правило совпадения | Для любого ключа побеждает самый правый вход. Новые ключи добавляются. Вложенное значение заменяется целиком, без обхода его полей. |
| Возврат | Новый массив; исходные массивы не изменяются. Это не глубокое клонирование объектов внутри значений. |
| Отказ и диагностика | Немассивный аргумент — TypeError. Рекурсивные варианты применяйте к конечным деревьям; циклические ссылки не являются поддержкой произвольного графа. |
В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Было 10→2 и 20→5; правка 10→3, 30→1.
- Заменить один остаток, сохранить другой, добавить третий.
- Показать новый словарь с исходными 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 автоматически.
<?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Разделить список на порции: например, отправлять по два задания в одном запросе.
array_chunk(array $array, int $length, bool $preserve_keys = false): arrayРазделить список на порции: например, отправлять по два задания в одном запросе.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| array | Массив в текущем порядке. |
| length | Максимум элементов в одной порции, строго больше нуля. |
| preserve_keys | false: каждый внутренний массив получает ключи 0, 1…; true: исходные ключи сохраняются. |
| Возврат | Внешний список порций с ключами от нуля. Последняя порция может быть короче; пустой вход даёт []. |
| Отказ и диагностика | length < 1: ValueError в PHP 8+; прежде E_WARNING и null. |
В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Пять заданий A…E
- Отделять очередные два
- Порции [A,B], [C,D], [E]
<?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.
<?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Взять окно из массива без изменения оригинала.
array_slice(array $array, int $offset, ?int $length = null, bool $preserve_keys = false): arrayВзять окно из массива без изменения оригинала.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| array | Массив в текущем порядке. |
| offset | Позиция с нуля, не ключ; отрицательное число отсчитывается от конца. |
| length | null/отсутствие: до конца; положительное: не больше этого количества; отрицательное: не включать столько последних элементов; 0: []. |
| preserve_keys | false перенумеровывает целочисленные ключи; true сохраняет. Строковые ключи сохранены всегда. |
| Возврат | Новый массив-срез; за пределами входа — []. |
| Отказ и диагностика | Типы аргументов проверяются; выход позиции за длину не является исключением. |
В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Записи с ID 10, 50, 90
- Начать с позиции 1, взять одну
- Получить запись ID 50
<?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Оставить первое из совпадающих значений, сохранив его ключ.
array_unique(array $array, int $flags = SORT_STRING): arrayОставить первое из совпадающих значений, сохранив его ключ.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| array | Одномерные значения; это не универсальное удаление одинаковых вложенных записей. |
| flags | SORT_STRING по умолчанию: равенство строковых представлений. SORT_NUMERIC: числовое; SORT_REGULAR: обычное сравнение; SORT_LOCALE_STRING: строки с учётом текущей locale. |
| Возврат | Новый массив, ключи и первое значение каждой группы сохранены; числовые ключи могут иметь пропуски. |
| Отказ и диагностика | Неподходящие для выбранного сравнения значения могут вызывать диагностику преобразования. Для SORT_REGULAR смешение числовых и нечисловых строк может оставить дубликаты из-за внутреннего порядка сравнения; выбирайте однородные данные и подходящий режим. |
В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Значения red, blue, red
- Первое red сохранить, повтор отбросить
- Остаются исходные ключи 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Разность массивов: оставить пары первого входа, для которых совпадение не найдено ни в одном из остальных массивов. Только значения; ключи остальных массивов не участвуют.
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 только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Первый массив задаёт кандидатов
- Сверить значения с остальными
- Сохранить пару, если совпадение не найдено ни в одном из остальных массивов
<?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Пересечение массивов: оставить пары первого входа, для которых совпадение найдено в каждом из остальных массивов. Только значения; ключи остальных массивов не участвуют.
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 только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Первый массив задаёт кандидатов
- Сверить значения с остальными
- Сохранить пару, если совпадение найдено в каждом из остальных массивов
<?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Проверить присутствие конкретного значения, например кода разрешённого статуса.
in_array(mixed $needle, array $haystack, bool $strict = false): boolПроверить присутствие конкретного значения, например кода разрешённого статуса.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| needle | Искомое значение. Строки сравниваются с учётом регистра. |
| haystack | Массив значений для поиска; ключи не проверяются. |
| strict | true требует ===; по умолчанию PHP допускает нестрогое равенство разных типов. |
| Возврат | true при наличии совпадения, иначе false. Исходный массив не меняется. |
| Отказ и диагностика | Неверный тип haystack даёт TypeError. Само отсутствие значения не является ошибкой. |
- Разрешены строки new и paid.
- Проверяем строку paid со strict=true.
- Тип и значение совпали → true; ключи массива не участвуют.
<?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 после сверки используют таблицу соответствия разрешенных полей. Нельзя параметризовать имя колонки тем же способом, что значение.
<?php
declare(strict_types=1);
$allowedIds = [10, 20];
echo in_array('10', $allowedIds, true) ? "Есть\n" : "Нужна нормализация\n";
Ожидаемый результат
Нужна нормализация
Внешний ввод сначала проверяют и нормализуют, после этого строгое сравнение не создает неожиданных совпадений.
Сравните. array_search возвращает ключ; array_key_exists проверяет словарь по ключу.
array_searchПолучить ключ первого точного совпадения, чтобы найти его место или удалить конкретную запись.
array_search(mixed $needle, array $haystack, bool $strict = false): int|string|falseПолучить ключ первого точного совпадения, чтобы найти его место или удалить конкретную запись.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| needle | Искомое значение. Строки сравниваются с учётом регистра. |
| haystack | Массив в текущем порядке; поиск возвращает первый подходящий ключ. |
| strict | true включает ===; для объектов это идентичность экземпляра. |
| Возврат | Ключ int/string или false, если совпадение отсутствует. Ключ 0 и пустая строка — успешные результаты. |
| Отказ и диагностика | Haystack должен быть массивом. Проверка результата через if($key) ошибочно отвергает некоторые успешные ключи. |
В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- items содержит A по ключу 0 и B по ключу 1.
- array_search находит A и возвращает 0.
- Условие !== false распознаёт успех и разрешает удалить именно ключ 0.
<?php
$items = ['A', 'B'];
$key = array_search('A', $items, true);
if ($key !== false) {
echo 'Удаляем найденный товар ', $items[$key], ' по ключу ', $key, "\n";
unset($items[$key]);
}
foreach ($items as $remainingKey => $item) {
echo 'Остался товар ', $item, ' по ключу ', $remainingKey, "\n";
}
$missingKey = array_search('C', $items, true);
if ($missingKey === false) {
echo "Товара C нет: ничего не удаляем\n";
}
Ожидаемый результат
Удаляем найденный товар A по ключу 0
Остался товар B по ключу 1
Товара C нет: ничего не удаляем
Search возвращает успешный ключ 0, поэтому проверка !== false обязательна: if(key) ошибочно отвергла бы ноль. После чтения найденного значения удаляется именно этот элемент. Ключ B остался 1. Отсутствующий C даёт false, и unset не вызывается: непроверенный false мог бы быть преобразован в ключ 0.
<?php
$statuses = ['A' => 'paid', 'B' => 'new', 'C' => 'paid'];
echo array_search('paid', $statuses, true), "\n";
echo json_encode(array_keys($statuses, 'paid', true)), "\n";
var_dump(array_search('missing', $statuses, true));
Ожидаемый результат
A
["A","C"]
bool(false)
Первый результат — ключ, не значение paid. Если нужны все совпадения, array_keys с фильтром выражает задачу прямо.
Нюансы и выбор. По умолчанию сравнение нестрогое; в наборах кодов обычно нужен третий аргумент true. Для поиска по условию строки, а не по её целому значению, удобнее array_find_key. После unset технические индексы не перенумеровываются сами; array_values используйте только если они не являются идентификаторами.
Сравните: in_array, array_keys, array_find_key, array_values. Выбрать операцию по задаче.
Продолжение: ранее разобранная практическая задача.
Возвращает первый ключ либо false. Ключ 0 является успешным результатом, поэтому используйте!==false.
<?php
declare(strict_types=1);
$filters = ['sale', 'new'];
$key = array_search('sale', $filters, true);
if ($key !== false) {
unset($filters[$key]);
}
echo implode(',', array_values($filters)), "\n";
Ожидаемый результат
new
Sale стоит под ключом 0. Строгая проверка предотвращает пропуск первого пункта; array_values восстанавливает список для интерфейса.
<?php
declare(strict_types=1);
$labels = ['new' => 'Новый', 'paid' => 'Оплачен'];
$code = array_search('Оплачен', $labels, true);
echo $code === false ? "Не найдено\n" : $code . "\n";
Ожидаемый результат
paid
Для редкого поискав маленьком справочнике этого достаточно. Для локализованных подписей не гарантируется уникальность — лучше передавать код из формы, а подпись только показывать.
Сравните. in_array выбирайте, когда нужен только булев ответ; array_keys с filtervalue находит все ключи.
usortЗадать своё правило порядка значений, получив новые числовые ключи.
usort(array &$array, callable $callback): trueЗадать своё правило порядка значений, получив новые числовые ключи.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| array | Изменяемый массив; все старые ключи заменены индексами с нуля. |
| callback | callback(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 только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
Для этого примера: передача функции как значения; оператор <=> и условный выбор.
- Есть массы 2.7,2.1,2.4.
- Callback сравнивает пару масс и возвращает знак целым числом.
- В 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 меньше, равный или больше нуля; не нужно знать точную величину.
<?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Упорядочить ключи по возрастанию, сохранив связь ключей и значений.
ksort(array &$array, int $flags = SORT_REGULAR): trueУпорядочить ключи по возрастанию, сохранив связь ключей и значений.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| array | Изменяемая переменная-массив; ключи не теряются. |
| flags | SORT_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 только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Исходный порядок ID 30,10,20.
- Сравнить только ключи по возрастанию.
- Пройти изменённый 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, чтобы намерение было явным.
<?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.
count(Countable|array $value, int $mode = COUNT_NORMAL): intПолучить число элементов или количество, определённое объектом Countable.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| value | Массив либо объект, реализующий Countable::count(). Не любая переменная или объект является countable. |
| mode | COUNT_NORMAL (0) считает верхний уровень. COUNT_RECURSIVE (1) дополнительно считает элементы вложенных массивов; вложенные контейнеры тоже учитываются. |
| Возврат | Целое количество элементов. Для Countable вызывается его собственный метод count; это не обязательно число публичных свойств. |
| Отказ и диагностика | В PHP 8 неподходящий тип даёт TypeError. В PHP 7.2 появилась диагностика для неподходящих типов; ранее не-null скаляр давал 1, null — 0. Рекурсивная ссылка при COUNT_RECURSIVE вызывает E_WARNING и не является корректным счётом листьев. |
- Есть две строки: A с 3 упаковками и B с 1.
- Посчитать строки отдельно от суммы количеств.
- Показать 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";
Ожидаемый результат
Добавьте товары
Число позиций не равно сумме количеств: три товара в одной строке — одна ячейка массива.
<?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,… в этом порядке.
array_is_list(array $array): boolПроверить, что массив действительно является последовательностью с ключами 0,1,2,… в этом порядке.
Обозначения перед первым опытом: ключ, значение и чтение через [ ]; вызов, параметры и результат. var_dump показывает тип и значение; echo выводит текст, а "\n" переносит строку. try/catch в опытах с отказом отделяют успешный результат от исключения: разбор обработки ошибок.
| Вход или результат | Договор |
|---|---|
| array | Проверяемый массив; его значения не обязаны иметь один тип. |
| Возврат | true для последовательных ключей от 0 до count−1 в текущем порядке; пустой массив тоже является списком. |
| Отказ и диагностика | Другой тип аргумента вызывает TypeError. Функция только проверяет и не исправляет ключи. |
В выводе примеров json_encode только показывает форму результата: [...] — список, {...} — именованные поля. Это отдельное преобразование в текст; как читать такой вывод.
- Удалить Кофе: останутся ключи 0 и 2.
- Проверка списка обнаружит пропуск.
- Получить новые позиции 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. Значения могут быть любого типа.
<?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 принудительно создает список, но сначала решите, можно ли отбросить ключи.
Источники и обсуждения
- PHP Manual: language.types.array
- array_filter() — официальный справочник
- array_merge() — официальный справочник
- array_replace() — официальный справочник
- Разбор реальной проблемы на Stack Overflow — обсуждение проблемы
- PHP Manual: control-structures.foreach
- PHP Manual: function.array
- PHP Manual: function.list
- PHP Manual: function.array-is-list
- PHP Manual: function.array-key-exists
- PHP Manual: function.array-values
- PHP Manual: function.unset
- PHP Manual: function.array-pop
- PHP Manual: function.array-shift
- PHP Manual: language.references.whatdo
- PHP Manual: function.sort
- PHP Manual: language.variables.basics
- PHP Manual: language.variables.scope
- PHP Manual: language.types.boolean
- PHP Manual: language.types.declarations
- PHP Manual: language.operators.arithmetic
- PHP Manual: language.operators.comparison
- PHP Manual: control-structures.if
- PHP Manual: functions.user-defined
- PHP Manual: functions.arguments
- PHP Manual: functions.returning-values
- PHP Manual: function.echo
- PHP Manual: function.return
- PHP Manual: function.is-array
- PHP Manual: function.array-fill
- PHP Manual: function.array-replace-recursive
- PHP Manual: function.array-multisort
- PHP Manual: function.array-udiff