Работа с массивом аргументов в функциях PHP

Раздел: Основы PHP -> Функции

В PHP функция может принимать переменное количество аргументов, которые внутри тела функции часто удобно представить в виде массива. Это позволяет обрабатывать параметры единообразно, передавать их другим функциям или динамически изменять их состав. Рассмотрим основные способы работы с массивом аргументов, их цели, типичные ошибки и рекомендации.

Основные подходы к работе с массивом аргументов

Современный и эффективный способ: variadic arguments (оператор ...)

Начиная с PHP 5.6, для объявления функции, которая принимает переменное количество аргументов, используется оператор spread (...) перед именем последнего параметра. Все переданные значения автоматически собираются в массив. Этот подход является наиболее эффективным, так как не требует дополнительных вызовов функций и хорошо интегрируется с контролем типов.

Пример:

function sumAll(...$numbers) {
    return array_sum($numbers);
}
echo sumAll(1, 2, 3, 4); // 10

Здесь $numbers - массив [1, 2, 3, 4]. Можно указать тип элементов, добавив объявление типа перед ...:

function sumInts(int ...$ints) {
    return array_sum($ints);
}
sumInts(1, 2, '3'); // Ошибка, '3' не int

Типичные проблемы и ошибки:

  • Нельзя объявить несколько variadic параметров в одной функции.
  • Variadic параметр должен быть последним. Если поставить его раньше обязательных параметров, возникнет фатальная ошибка.
  • При использовании строгой типизации (declare(strict_types=1)) автоматическое приведение типов не происходит, что может вызвать неожиданные исключения.

Цели и случаи использования:

  • Функции-обёртки, логирования, события, где количество аргументов заранее неизвестно.
  • Реализация шаблонов проектирования, требующих передачи переменного числа параметров (например, строитель).
  • Создание полиморфных функций с одинаковым поведением для разного количества аргументов.

Как передать массив в функцию, если он уже существует?

Если у вас уже есть массив значений, его можно передать как один аргумент (например, $arr), а внутри функции работать с ним напрямую. Это простейший случай, когда функция ожидает именно массив.

function processArray(array $data) {
    foreach ($data as $value) {
        echo $value . ' ';
    }
}
processArray([10, 20, 30]); // 10 20 30

Ошибка: если попытаться передать не массив (например, число), произойдет TypeError.

Как получить все переданные аргументы функции без использования variadic?

Для обратной совместимости или при работе с кодом более старых версий PHP используют функцию func_get_args(). Она возвращает массив всех аргументов, переданных текущей функции.

function oldSum() {
    $args = func_get_args();
    return array_sum($args);
}
echo oldSum(5, 6, 7); // 18

Особенности:

  • Нельзя использовать с typed parameters (например, int $a, string $b), так как при объявлении типов аргументов PHP может выдать предупреждение, если количество переданных аргументов не совпадает с объявленными параметрами.
  • Функция func_get_args() работает медленнее, чем variadic, и менее читаема.

Типичная ошибка:

Использование func_get_args() внутри вложенной функции или замыкания, когда ожидаются аргументы внешней функции. func_get_args() всегда относится к текущей функции.

Как использовать именованные аргументы (PHP 8) для передачи массива?

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

function displayInfo($name, $age, $city) {
    echo "Name: $name, Age: $age, City: $city";
}
$params = ['age' => 30, 'name' => 'Alice', 'city' => 'Moscow'];
displayInfo(...$params); // Распаковка ассоциативного массива в именованные аргументы

Важно:

  • Ключи массива должны соответствовать именам параметров функции.
  • Лишние ключи игнорируются, отсутствующие - вызовут ошибку (если параметр обязательный).

Ошибка: при использовании числового индексированного массива с spread-оператором внутри вызова функции аргументы будут переданы по порядку (как при позиционных параметрах). Если функция ожидает именованные аргументы, это приведёт к ошибке.

Как комбинировать обязательные параметры с вариативными?

Variadic параметр можно добавить после одного или нескольких обязательных параметров. Все лишние аргументы соберутся в массив.

function logMessage($level, ...$messages) {
    echo "[$level] " . implode(', ', $messages);
}
logMessage('INFO', 'Connection established', 'User logged in');

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

Ошибка: если после variadic параметра объявить ещё один параметр, будет фатальная ошибка.

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

Иногда требуется, чтобы функция могла изменять содержимое переданного массива. Это делается с помощью передачи по ссылке (&).

function modifyArray(&$arr) {
    $arr[] = 'new element';
}
$data = ['a', 'b'];
modifyArray($data);
print_r($data); // ['a', 'b', 'new element']

Примечание:

С variadic аргументами (...&$args) работа по ссылке также возможна (PHP 8.1+), но требует осторожности.

Дополнительные примеры работы с массивом аргументов

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

Пример 1: variadic с ключами через ассоциативный массив

Хотя variadic не поддерживает именованные ключи, можно передать один ассоциативный массив, а внутри функции извлечь его.

Пример
function setOptions(array $options) {
    $host = $options['host'] ?? 'localhost';
    $port = $options['port'] ?? 8080;
    echo "Connecting to $host:$port";
}
setOptions(['host' => 'example.com', 'port' => 443]);
Connecting to example.com:443

Пример 2: Использование variadic с типом mixed (PHP 8)

Пример
function log(...$data): void {
    foreach ($data as $item) {
        if (is_string($item) || is_numeric($item)) {
            echo $item . "\n";
        } else {
            var_dump($item);
        }
    }
}
log('Error code:', 42, ['details' => 'timeout']);
Error code:
42
array(1) {
  ["details"]=>
  string(7) "timeout"
}

Пример 3: Распаковка массива при вызове функции (spread оператор)

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

Пример
function add($a, $b, $c) {
    return $a + $b + $c;
}
$numbers = [1, 2, 3];
echo add(...$numbers);
6

Если количество элементов массива не совпадает с количеством ожидаемых параметров, возникнет ошибка (слишком много или слишком мало аргументов).

Пример 4: Декоратор с переменным числом аргументов

Пример
function timingDecorator(callable $callback, ...$args) {
    $start = microtime(true);
    $result = $callback(...$args);
    $elapsed = microtime(true) - $start;
    echo "Elapsed: $elapsed sec\n";
    return $result;
}
$sum = function($a, $b) { return $a + $b; };
echo timingDecorator($sum, 5, 7);
Elapsed: 0.000001 sec
12

Пример 5: Variadic в конструкторе класса

Пример
class Logger {
    private array $entries;
    public function __construct(string ...$entries) {
        $this->entries = $entries;
    }
    public function getEntries(): array {
        return $this->entries;
    }
}
$log = new Logger('start', 'process', 'end');
print_r($log->getEntries());
Array
(
    [0] => start
    [1] => process
    [2] => end
)

Пример 6: Использование func_get_args() с typed parameters (не рекомендуется)

Пример
function typedFunc(int $a, string $b) {
    $args = func_get_args();
    var_dump($args);
}
typedFunc(1, 'test');
typedFunc(1, 'test', 3.14); // лишний аргумент: ignore? зависит от настройки
array(2) {
  [0]=>
  int(1)
  [1]=>
  string(4) "test"
}
array(3) {
  [0]=>
  int(1)
  [1]=>
  string(4) "test"
  [2]=>
  float(3.14)
}

Замечание:

При наличии typed parameters, PHP всё равно собирает все переданные аргументы через func_get_args(), даже если их больше, чем объявлено. Это может привести к несоответствию типов, если лишние аргументы не соответствуют ожидаемым. Рекомендуется избегать такой практики.

Пример 7: Передача variadic аргументов в анонимную функцию (замыкание)

Пример
$callback = function(...$values) {
    return array_map(function($v) { return $v ** 2; }, $values);
};
$result = $callback(1, 2, 3, 4);
print_r($result);
Array
(
    [0] => 1
    [1] => 4
    [2] => 9
    [3] => 16
)

Пример 8: Комбинация spread-оператора с именованными аргументами (PHP 8)

Пример
function createUser($name, $email, $age = 18) {
    echo "Created user $name ($email), age $age";
}
$data = ['email' => 'user@example.com', 'name' => 'John'];
createUser(...$data); // age не указан – использует значение по умолчанию
Created user John (user@example.com), age 18

Пример 9: Проверка количества аргументов с помощью func_num_args()

Пример
function flexible() {
    $count = func_num_args();
    echo "Arguments count: $count\n";
    if ($count >= 2) {
        $first = func_get_arg(0);
        $second = func_get_arg(1);
        echo "First: $first, Second: $second";
    }
}
flexible('hello', 'world', 'extra');
Arguments count: 3
First: hello, Second: world

При использовании func_get_arg() с несуществующим индексом возникает предупреждение и возвращается false.

Массив аргументов функции в PHP - comments

En
Php arguments array (php)