PHP 10798 ~ 5 мин.

PHP 8: Именованные аргументы

PHP 8: Именованные аргументы

Именованные аргументы, также называемые именованными параметрами, получат поддержку в PHP 8. В этом посте я расскажу об их плюсах и минусах.

Но позвольте сначала спросить бывали ли вы когда-нибудь в ситуации, когда вы видели функцию и ее параметры и задавались вопросом, что это за параметры? Я почти уверен, что это было и не раз, например, возьмем следующее:


array_slice($array, $offset, $length, true);

Первые три параметра, переданные в array_slice , кажутся очевидными из-за информативных имен переменных, но как насчет четвертого параметра? Это просто true. Но что здесь означает это true? Что ж, чтобы это узнать, вам нужно перейти к определению функции или обратиться к документации. В случае array_sliceс определением будет:


function array_slice(
    array $array,
    $offset,
    $length = null,
    $preserve_keys = false
) { }

Итак, четвертый параметр - $preserve_keys это то, что вы можете определить, только взглянув на определение. И больше у вас ничего нет, что может подсказать, просто взглянув на объявление функции.

А что если мы перепишем данный пример вот так:


array_slice($array, $offset, $length, preserve_keys: true);

Как видите, код теперь довольно самодокументируется по сравнению с предыдущим примером. Теперь мы знаем, для чего предназначен четвертый параметр. Или возьмем встроенную функции PHP по созданию кук:


setcookie(
    name: 'week',
    expires: time() + 60 * 60 * 24 * 14,
);

А это, DTO, использующий продвигаемые свойства , а также именованные аргументы:


class CustomerData
{
    public function __construct(
        public string $name,
        public string $email,
        public int $age,
    ) {}
}

$data = new CustomerData(
    name: $input['name'],
    email: $input['email'],
    age: $input['age'],
);

Именованные аргументы также поддерживают распаковку массива:


$data = new CustomerData(...$customerRequest->validated());

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

RFC был первоначально предложен Никитой Поповым, который работает над редактором PHP в компании по разработке инструментов JetBrains. Как он выразился в недавнем подкасте: «Если у вас есть метод, например, с тремя логическими аргументами, это действительно выглядит ужасно, потому что вы называете его так: «true »,«true»,«false», например, что это означает? Если вы указали параметры и у вас есть те же три логических аргумента, тогда это больше не проблема».

Почему именованные аргументы?

Эта функция была очень обсуждаемой, и было несколько контраргументов против ее добавления. Тем не менее, я бы сказал, что их преимущества намного перевешивают страх перед проблемами обратной совместимости или раздутыми API. На мой взгляд, они позволят нам писать более чистый и гибкий код.

Во-первых, именованные аргументы позволяют пропускать значения по умолчанию. Взглянем еще раз на пример c созданием cookie:


setcookie(
    name: 'week',
    expires: time() + 60 * 60 * 24 *14,
);

Сигнатура его метода на самом деле достаточно большая и выглядит так:


setcookie ( 
    string $name, 
    string $value = "", 
    int $expires = 0, 
    string $path = "", 
    string $domain = "", 
    bool $secure = false, 
    bool $httponly = false,
) : bool

В показанном мной примере нам не нужно было устанавливать значение $value, но нам обязательно нужно установить время истечения куки. Именованные аргументы сделали вызов этого метода более лаконичным, итак вот это:


setcookie(
    'week',
    '',
    time() + 60 * 60 * 24 *14,
);

против


setcookie(
    name: 'week',
expires: time() + 60 * 60 * 24 *14,
);

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

Подробнее об именованных аргументов

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

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

Посмотрим на наш предыдущий пример DTO:


class CustomerData
{
    public function __construct(
        public string $name,
        public string $email,
        public int $age,
    ) {}
}

Вы можете построить его так:


$data = new CustomerData(
    $input['name'],
    age: $input['age'],
    email: $input['email'],
);

Однако наличие упорядоченного аргумента после именованного приведет к ошибке:


$data = new CustomerData(
    age: $input['age'],
    $input['name'],
    email: $input['email'],
);

Затем можно использовать распакову массива в сочетании с именованными аргументами:


$input = [
    'age' => 33,
    'name' => 'Sergey',
'email' => 'sinbadxiii@gmail.com',
]; $data = new CustomerData(...$input);

Однако, если отсутствуют необходимые записи в массиве, или если есть ключ, который не указан в качестве именованного аргумента, будет сгенерировано сообщение об ошибке:


$input = [
    'age' => 33,
    'name' => 'Sergey',
    'email' => 'sinbadxiii@gmail.com',
    'unknownProperty' => 'This is not allowed',
];

$data = new CustomerData(...$input);

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


$input = [
    'Sergey',
'age' => 33, 'email' => 'sinbadxiii@gmail.com',
]; $data = new CustomerData(...$input);

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


class CustomerData
{
    public static function new(...$args): self
    {
        return new self(...$args);
    }

    public function __construct(
        public string $name,
        public string $email,
        public int $age,
    ) {}
}

$data = CustomerData::new(
    email: 'sinbadxiii@gmail.com',
age: 33, name: 'Sergey', );

В этом случае массив $args будет содержать следующие данные: CustomerData::new


[
    'age' => 33,
    'email' => 'sinbadxiii@gmail.com',
'name' => 'Sergey', ]

Атрибуты, также известные как аннотации, тоже поддерживают именованные аргументы:


class ProductSubscriber
{
    @@ListensTo(event: ProductCreated::class)
    public function onProductCreated(ProductCreated $event) { /* … */ }
}

Невозможно использовать переменную в качестве имени аргумента:


$field = 'age';

$data = CustomerData::new(
    $field: 33,
);

И, наконец, именованные аргументы прагматично будут иметь дело с изменениями имени во время наследования. Вот пример:


interface EventListener {
    public function on($event, $handler);
}

class MyListener implements EventListener
{
    public function on($myEvent, $myHandler)
    {
        // …
    }
}

PHP автоматически разрешит изменение имени $event на $myEvent и $handler на $myHandler; но если вы решите использовать именованные аргументы, используя имя родителя, это приведет к ошибке выполнения:


public function register(EventListener $listener)
{
    $listener->on(
        event: $this->event,
        handler: $this->handler, 
    );
}

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

Ну что же, PHP присоединился к длинному списку языков, в которых уже есть эта функция, включая C#, Kotlin, PowerShell, Python, Ruby, Swift и Visual Basic. Однако те же Java и JavaScript пока его не поддерживают.


Что думаешь?

Категории
  • PHP 66
  • Заметки 15
  • Безопасность 3
  • Флуд 2
  • Nginx 2
  • ИТ новости 2
  • Видео 1
  • Docker 1
  • Roadmap 1
  • Архитектура 0

Хочешь поддержать сайт?

Делаем из мухи слона

sergeymukhin.com

персональный блог о веб-разработке от Сергея Мухина. Блог был основан в 2018 году, и собирался уделять основное внимание последним тенденциям, учебным пособиям, а также советам и рекомендациям, позволяющим начинающим девелоперам встать быстрее на правильную дорогу веб разработки, но что-то пошло не так 😃

Релизы PHP 8.4

Дата Релиз
8 Июня 2024 Альфа 1
20 Июня 2024 Альфа 2
04 Июля 2024 Альфа 3
16 Июля 2024 Feature freeze
18 Июля 2024 Бета 1
01 Августа 2024 Бета 2
15 Августа 2024 Бета 3
29 Августа 2024 RC 1
12 Сентября 2024 RC 2
26 Сентября 2024 RC 3
10 Октября 2024 RC 4
24 Октября 2024 RC 5
07 Ноября 2024 RC 6
21 Ноября 2024 GA

Что нового?