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

Источник: «Named Arguments in PHP»
Узнайте, как использовать именованные аргументы в PHP, чтобы улучшить читаемость кода. Кроме того, поговорим о некоторых проблемах, о которых следует помнить.

Введение

Именованные аргументы (или именованные параметры) — одна из моих любимых функций, добавленных в PHP ещё в версии 8.0. Кажется, что они действительно помогают улучшить читабельность кода и облегчают понимание происходящего.

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

Именованные аргументы в двух словах

Проще говоря, именованные аргументы позволяют передавать аргументы в функцию или метод, указывая имя параметра, за которым следует двоеточие :, а затем значение. Допустим, есть функция greet, принимающая два параметра:

function greet(string $name, string $greeting): string
{
return "{$greeting}, {$name}!";
}

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

echo greet('Ash', 'Hello');

Но с именованными аргументами эту функцию можно вызвать следующим образом:

echo greet(name: 'Ash', greeting: 'Hello');

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

Какую проблему решают именованные аргументы

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

Допустим, есть сервисный класс (рассмотрим его в ближайшее время), позволяющий создать нового пользователя. Метод createUser в сервисном классе можно вызвать следующим образом:

use App\Services\UserService;

$userService = new UserService();

$user = $userService->createUser(
'Ash',
'mail@ashallendesign.co.uk',
true,
);

Посмотрев на приведённый выше код, вы, вероятно, догадаетесь, что представляют собой первый и второй параметры (имя и адрес электронной почты). Но что представляет собой третий параметр?

Если этот код попался в проекте, то, возможно, придётся переходить в интегрированную среду разработки (IDE), чтобы узнать, что представляет собой третий параметр. Необходимость делать это может добавить трения в процесс разработки и замедлить его, поскольку не сразу понятно, что это за параметр.

Здесь на помощь приходят именованные аргументы.

Давайте посмотрим на сигнатуру метода createUser:

//app/Services/UserService.php

namespace App\Services;

use App\Models\User;

class UserService
{
public function createUser(
string $name,
string $email,
bool $sendWelcomeEmail,
): User {
// Создание пользователя
}
}

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

use App\Services\UserService;

$userService = new UserService();

$user = $userService->createUser(
name: 'Ash',
email: 'mail@ashallendesign.co.uk',
sendWelcomeEmail: true,
);

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

Самое замечательное в именованных аргументах то, что можно использовать смесь именованных и позиционных аргументов (при условии, что именованные аргументы идут после позиционных). Поэтому, если хотим использовать именованные аргументы только для третьего параметра, можно обновить вызов функции следующим образом:

use App\Services\UserService;

$userService = new UserService();

$user = $userService->createUser(
'Ash',
'mail@ashallendesign.co.uk',
sendWelcomeEmail: true,
);

Преимущества именованных аргументов

Использование именованных аргументов в коде может дать несколько преимуществ:

Улучшенная читабельность

Как уже говорилось, самое полезное, что я обнаружил при использовании именованных аргументов, — это улучшение читабельности. Используя именованные аргументы, можно сделать код более понятным и лёгким для восприятия с первого взгляда.

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

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

Пропуск необязательных аргументов

Ещё одно преимущество именованных параметров заключается в том, что с их помощью можно сократить количество необязательных аргументов, передаваемых в метод, если они соответствуют значениям по умолчанию.

Представим, что есть следующий метод createUser:

//app/Services/UserService.php

namespace App\Services;

use App\Models\User;

class UserService
{
public function createUser(
string $name,
string $email,
bool $sendWelcomeEmail = true,
bool $isVerified = false,
string $role = 'user',
string $locale = 'en',
): User {
// Создание пользователя
}
}

В методе фактически используются только первые два параметра ($name и $email). Остальные параметры имеют значения по умолчанию, поэтому они необязательны.

Представим, что требуется создать пользователя со всеми значениями по умолчанию, кроме локали. Используя позиционные аргументы, необходимо передать значения по умолчанию для параметров sendWelcomeEmail, isVerified и role:

use App\Services\UserService;

$userService = new UserService();

$user = $userService->createUser(
'Ash',
'mail@ashallendesign.co.uk',
true,
false,
'user',
'es',
);

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

use App\Services\UserService;

$userService = new UserService();

$user = $userService->createUser(
name: 'Ash',
email: 'mail@ashallendesign.co.uk',
locale: 'es',
);

Круто, правда?

О чём следует помнить

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

Например, возьмём метод createUser из предыдущих примеров. Представьте, что параметр sendWelcomeEmail был переименован в sendWelcomeNotification следующим образом:

//app/Services/UserService.php

namespace App\Services;

use App\Models\User;

class UserService
{
public function createUser(
string $name,
string $email,
bool $sendWelcomeNotification,
): User {
// Создание пользователя
}
}

Если не обновить код, приведённый выше, это приведёт к ошибке:

Unknown named parameter $sendWelcomeEmail

Вместо этого необходимо обновить вызов функции, чтобы он соответствовал новой сигнатуре метода:

use App\Services\UserService;

$userService = new UserService();

$user = $userService->createUser(
name: 'Ash',
email: 'mail@ashallendesign.co.uk',
sendWelcomeNotification: true,
);

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

Ещё один отличный способ выявить эти ошибки — иметь хороший набор тестов или использовать инструмент статического анализа, например PHPStan. Поэтому после обновления зависимостей Composer будет полезно запустить набор тестов, чтобы выявить изменения.

Заключение

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

Дополнительные материалы

Предыдущая Статья

Базовое руководство по GitHub Actions

Следующая Статья

Введение в Popover API