Три точки (spread) в PHP: полный обзор
Три точки в PHP: синтаксис и применение
Оператор spread ( ... ) в PHP позволяет распаковывать итерируемые значения (массивы, объекты, реализующие Traversable) в места, где требуется несколько элементов. В PHP 5.6 появилась возможность распаковки при вызове функций, в PHP 7.4 - распаковка в массивах, а с PHP 8.1 добавлена поддержка строковых ключей в массивах (ограниченно). Рассмотрим основные способы использования и типичные ошибки.
Распаковка массива в новый массив (PHP 7.4+)
Основное назначение spread - слияние массивов или копирование с добавлением элементов. В отличие от array_merge, spread работает быстрее и может применяться к любому выражению, возвращающему массив.
$arr1 = [1, 2, 3];
$arr2 = [...$arr1, 4, 5]; // [1,2,3,4,5]
точка в php в строке (точка (конкатенация) в строке php)
Комбинация нескольких массивов и литеральных значений:
$a = ['x', 'y'];
$b = [1, ...$a, 2]; // [1, 'x', 'y', 2]
Php три точки (три точки в php (spread operator))
При совпадении целочисленных ключей последнее значение перезаписывает предыдущие, как и в array_merge. Строковые ключи до PHP 8.1 вызывают ошибку.
Ошибка: распаковка ассоциативного массива с нечисловыми ключами в версиях ниже 8.1 приводит к фатальной ошибке "Cannot unpack array with string keys". В PHP 8.1+ такая распаковка разрешена, но все ключи должны быть либо строками, либо целыми числами (смешанные типы допускаются).
Как передать элементы массива как отдельные аргументы функции?
Синтаксис ... при вызове функции (с PHP 5.6) распаковывает массив или Traversable в список аргументов:
function sum($a, $b, $c) {
return $a + $b + $c;
}
$numbers = [1, 2, 3];
echo sum(...$numbers); // 6
Это удобно для функций, ожидающих переменное число параметров, например array_merge или sprintf.
Проблема: количество элементов в массиве должно соответствовать количеству объявленных параметров. При меньшем числе возникнет ошибка о недостающих аргументах; при большем - лишние элементы игнорируются, но если параметры типизированы, это может вызвать TypeError.
Как объявить функцию, принимающую произвольное число аргументов (variadic)?
В определении функции ... собирает все переданные аргументы начиная с этой позиции в массив:
function logMessages(...$messages) {
foreach ($messages as $msg) {
echo $msg . "\n";
}
}
logMessages('Hello', 'World', 'Test');
// Hello
// World
// Test
Вариативный параметр должен быть последним в списке параметров.
Ошибка: если вариативный параметр стоит не последним, возникает синтаксическая ошибка. Также невозможно использовать передачу по ссылке с ... в сигнатуре (до PHP 8.0 это запрещено, в PHP 8.0+ разрешено с осторожностью).
Как объединить несколько массивов без array_merge?
Spread внутри массива позволяет объединять массивы с числовыми ключами. Результат аналогичен array_merge, но не поддерживает строковые ключи до версии 8.1.
$arr1 = [1 => 'a', 2 => 'b'];
$arr2 = [1 => 'c', 3 => 'd'];
$result = [...$arr1, ...$arr2];
// Результат: [1=>'c', 2=>'b', 3=>'d']
// Ключ 1 перезаписан значением 'c'
Для сохранения всех значений при дублировании ключей следует использовать оператор + или array_merge с дополнительной логикой.
Особенность: spread не предупреждает о перезаписи ключей - последнее значение побеждает. Это важно учитывать при объединении неупорядоченных массивов.
Как развернуть итератор или генератор в массив?
Spread работает с любым Traversable, включая генераторы, позволяя получить все элементы без вызова iterator_to_array:
function generator() {
yield 10;
yield 20;
}
$array = [...generator()]; // [10, 20]
Это удобно для цепочек вызовов, где нужно немедленно получить массив.
Замечание: если генератор возвращает ключи, они будут сохранены (только числовые). Для полного контроля используйте iterator_to_array с флагом.
Можно ли использовать spread со строками?
Spread не применим к строкам как к итерируемому типу (строки не являются Traversable). Для разбивки строки на массив символов применяется str_split:
$str = 'abc';
// $chars = [...$str]; // Ошибка: Cannot unpack string
$chars = str_split($str); // ['a','b','c']
Проблема: попытка распаковать строку приводит к фатальной ошибке. Для символов Unicode используйте mb_str_split.
Расширенные примеры использования spread
Пример 1. Слияние массивов с переиндексацией
$list1 = [0 => 'a', 3 => 'b'];
$list2 = [1 => 'c', 2 => 'd'];
$merged = [...$list1, ...$list2];
print_r($merged);
Array
(
[0] => a
[1] => b
[2] => c
[3] => d
)
Ключи сбрасываются: 0,1,2,3. Порядок определяется исходными индексами (сначала все элементы первого массива, затем второго).
Пример 2. Variadic функция с форматированием
function buildString($format, ...$params) {
return vsprintf($format, $params);
}
echo buildString('Hello %s, you have %d new messages.', 'User', 5);
Hello User, you have 5 new messages.
Вариативный параметр собирает все аргументы после первого, что удобно для обёрток над vsprintf.
Пример 3. Распаковка генератора в массив
function fib($n) {
$a = 0; $b = 1;
for ($i = 0; $i < $n; $i++) {
yield $a;
[$a, $b] = [$b, $a + $b];
}
}
$firstTen = [...fib(10)];
print_r($firstTen);
Array
(
[0] => 0
[1] => 1
[2] => 1
[3] => 2
[4] => 3
[5] => 5
[6] => 8
[7] => 13
[8] => 21
[9] => 34
)
Пример 4. Использование spread для передачи массива в функции с типизированными параметрами
function concat(string $a, string $b, string $c): string {
return $a . $b . $c;
}
$parts = ['PHP', ' ', '8.1'];
echo concat(...$parts);
PHP 8.1
Типы параметров проверяются автоматически. Ошибка возникает, если элемент массива не соответствует объявленному типу.
Пример 5. Ошибка распаковки ассоциативного массива в PHP 7.4–8.0
$assoc = ['name' => 'Alice', 'age' => 30];
try {
$flat = [...$assoc];
} catch (\Error $e) {
echo $e->getMessage();
}
Cannot unpack array with string keys
В PHP 8.1+ этот код выполнится, но ключи будут сохранены. Для совместимости рекомендуется избегать распаковки ассоциативных массивов.
Пример 6. Комбинация spread и array_values для сброса ключей
$data = [3 => 'x', 1 => 'y', 2 => 'z'];
$data = [...$data]; // переиндексирует ключи
print_r($data);
Array
(
[0] => y
[1] => z
[2] => x
)
Порядок сохраняется, но ключи становятся последовательными, начиная с 0. Это быстрый способ переиндексации без вызова array_values.
Пример 7. Spread с вложенными массивами (только первый уровень)
$matrix = [[1, 2], [3, 4]];
$flat = [...$matrix[0], ...$matrix[1]];
print_r($flat);
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
Spread не проникает вглубь структуры - он разворачивает только один уровень.