Циклы и массивы (включая ассоциативные)

Массивы в PHP - как карман: туда можно положить всё. Главное потом не искать ключи в стиральной машине.

Обычные массивы

<?php
$numbers = [10, 20, 30];
echo $numbers[0]; // 10
echo count($numbers); // 3

Добавить элемент:

<?php
$numbers[] = 40; // добавить в конец
array_push($numbers, 50); // то же самое, но длиннее

Удалить элемент:

<?php
unset($numbers[1]); // удалит элемент с индексом 1
// Осторожно: индексы НЕ пересчитываются!
// Чтобы пересчитать: $numbers = array_values($numbers);

Ассоциативные массивы

Ассоциативный массив - это словарь: ключ → значение. Именно так обычно представляют JSON-объекты после json_decode (см. урок про JSON) и строки из БД через PDO.

<?php
$user = [
    'id' => 1,
    'name' => 'Иван',
    'role' => 'student',
];

echo $user['name']; // Иван

Проверить, есть ли ключ:

<?php
if (array_key_exists('email', $user)) {
    echo $user['email'];
} else {
    echo 'Email не указан';
}

// Короче (но не различает null и «нет ключа»):
echo $user['email'] ?? 'Email не указан';
`isset($arr['key'])` вернёт `false`, если значение `null`. `array_key_exists('key', $arr)` вернёт `true`, даже если значение `null`. Если нужно отличить «ключ есть, но null» от «ключа нет» - используй `array_key_exists`.

foreach - лучший друг массива

<?php
$nums = [1, 2, 3];
foreach ($nums as $n) {
    echo $n . "\n";
}

// С ключами:
foreach ($user as $key => $value) {
    echo "$key = $value\n";
}

Изменить элемент прямо в цикле - через ссылку &:

<?php
$prices = [100, 200, 300];
foreach ($prices as &$price) {
    $price *= 1.1; // +10% к каждой цене
}
unset($price); // обязательно: убираем ссылку после цикла
Если забудешь `unset($price)` после цикла с `&`, последний элемент массива останется «привязан» к переменной `$price`. Следующее присваивание `$price = ...` изменит массив. Это классический баг.

Циклы for / while

<?php
for ($i = 0; $i < 3; $i++) {
    echo $i; // 012
}

while - когда количество итераций заранее неизвестно:

<?php
$line = '';
while (($line = fgets(STDIN)) !== false) {
    echo "Прочитано: $line";
}

Сортировка

<?php
$nums = [3, 1, 4, 1, 5];
sort($nums);      // [1, 1, 3, 4, 5] - по возрастанию
rsort($nums);     // [5, 4, 3, 1, 1] - по убыванию

Для ассоциативных массивов - asort / ksort (сохраняют ключи):

<?php
$ages = ['Иван' => 25, 'Анна' => 22, 'Пётр' => 30];
asort($ages);  // сортировка по значению: Анна, Иван, Пётр
ksort($ages);  // сортировка по ключу: Анна, Иван, Пётр

Кастомная сортировка - usort:

<?php
$users = [
    ['name' => 'Иван', 'age' => 25],
    ['name' => 'Анна', 'age' => 22],
];

usort($users, fn($a, $b) => $a['age'] - $b['age']);
// Теперь: Анна (22), Иван (25)
// fn() => - стрелочная функция, подробнее в [уроке про функции](./05-functions.md)

Полезные функции

<?php
$items = [1, 2, 3, 4, 5];

// array_map - преобразовать каждый элемент
$doubled = array_map(fn($n) => $n * 2, $items);
// [2, 4, 6, 8, 10]

// array_filter - оставить только подходящие
$even = array_filter($items, fn($n) => $n % 2 === 0);
// [2, 4]

// array_reduce - свернуть в одно значение
$sum = array_reduce($items, fn($carry, $n) => $carry + $n, 0);
// 15

Объединение массивов

<?php
$a = [1, 2];
$b = [3, 4];

// array_merge - объединить
$c = array_merge($a, $b); // [1, 2, 3, 4]

// Spread-оператор (PHP 7.4+)
$c = [...$a, ...$b]; // [1, 2, 3, 4]

Для ассоциативных массивов array_merge перезаписывает одинаковые ключи (последний побеждает):

<?php
$defaults = ['color' => 'blue', 'size' => 'M'];
$custom = ['color' => 'red'];

$result = array_merge($defaults, $custom);
// ['color' => 'red', 'size' => 'M']

Деструктуризация (list)

<?php
$coords = [55.75, 37.62];
[$lat, $lon] = $coords;
echo "Широта: $lat, Долгота: $lon";

// С ассоциативным массивом:
$user = ['name' => 'Иван', 'age' => 25];
['name' => $name, 'age' => $age] = $user;
echo "$name, $age лет";

Многомерные массивы

<?php
$matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
];

echo $matrix[1][2]; // 6

// Массив объектов (массивов):
$orders = [
    ['id' => 1, 'items' => ['Молоко', 'Хлеб']],
    ['id' => 2, 'items' => ['Кофе']],
];

foreach ($orders as $order) {
    echo "Заказ #{$order['id']}: " . implode(', ', $order['items']) . "\n";
}

in_array и array_search

<?php
$roles = ['admin', 'editor', 'viewer'];

if (in_array('admin', $roles, true)) {
    echo 'Есть админ';
}

$index = array_search('editor', $roles, true);
// $index === 1
Всегда передавай `true` третьим аргументом в `in_array` и `array_search`. Без него PHP сравнивает через `==`, и `in_array(0, ["строка"])` вернёт `true`. С `true` - строгое сравнение `===`.

Типичные ошибки

  • in_array без strict=true. in_array(0, ['admin']) вернёт true из-за юглинга типов: строка кастуется к 0. Всегда передавай третий аргумент true, чтобы получить сравнение через ===.
  • array_merge для числовых ключей. array_merge([1, 2], [3, 4]) объединяет как [1, 2, 3, 4], а оператор + оставит только первый массив: [1, 2] + [3, 4] === [1, 2]. Для ассоциативных наоборот: + сохраняет левые ключи, array_merge перезаписывает их правыми.
  • isset против array_key_exists. isset($arr['key']) вернёт false, если значение null - и ты не отличишь «ключа нет» от «ключ есть, но null». Если эта разница важна (например, в конфиге) - используй array_key_exists.
  • unset рушит порядок индексов. После unset($arr[1]) массив [10, 20, 30] превращается в [0 => 10, 2 => 30]. Следующий foreach отработает, но $arr[1] уже не существует. Если нужны последовательные индексы - array_values($arr).
  • Изменение массива через & без unset после цикла. foreach ($items as &$item) оставляет $item привязанным к последнему элементу. Любое присваивание $item = ... ниже по коду молча перезапишет последний элемент массива. Всегда делай unset($item) сразу после такого цикла.

Best practices

  • Используй array_key_exists для проверок наличия ключа в данных, где null - валидное значение (конфиги, JSON-ответы).
  • Для слияния списков по индексам - array_merge или spread [...$a, ...$b]. Оператор + оставляй для defaults: $options = $userOptions + $defaults.
  • Передавай большие массивы в функции, только если не собираешься их мутировать. Copy-on-write бесплатен на чтение, но первая же мутация скопирует весь массив.
  • Сортируй ассоциативные массивы через asort/ksort/uasort - обычный sort сбросит ключи в 0, 1, 2, ... и потеряет связь «ключ → значение».
  • Типизируй параметры как array в сигнатуре функции, а внутри сразу валидируй структуру (array_key_exists, is_string) - PHP не проверит «массив строк» за тебя.

Мини-задание

  • Создай массив товаров (name, price), выведи список
  • Посчитай сумму цен через array_reduce
  • С помощью array_filter выбери товары дороже 100
  • Отсортируй товары по цене (по возрастанию) через usort
  • Объедини два массива товаров через spread-оператор

Зарегистрируйтесь бесплатно, чтобы пройти квиз, решить задание с автопроверкой и вести прогресс.