• Время чтения ~10 мин
  • 28.10.2022

С момента своего выпуска в конце 2020 года PHP 8 изменил правила игры. В этом уроке я расскажу обо всех последних функциях с реальными примерами того, когда я могу их использовать.

Я влюбился в язык PHP в начале своей карьеры, и с тех пор я выступаю за него как за язык при каждом удобном случае. Однако, начиная с релизов 8.*, мне не пришлось преувеличивать ни одной вещи. Вместо этого я мог полагаться исключительно на факты с языком. Давайте прогуляемся по некоторым из выдающихся функций из выпуска PHP 8.0.

Продвижение недвижимости

конструктора Это должно быть одной из моих самых используемых функций 8.0 и сэкономило мне много нажатий клавиш. Давайте разберем его:

// Before PHP 8.0
class Client
{
    private string $url;
 
    public function __construct(string $url)
    {
        $this->url = $url;
    }
}
// PHP 8.0
class Client
{
    public function __construct(
        private string $url,
    ) {}
}

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

Еще

Another fantastic feature that was released is Еще. This is where a type hinted variable or a return type can be one or more types. This has helped with static analysis, where you might have conditional returns within a method. Let's look at an example.

// Before PHP 8.0
class PostService
{
    public function all(): mixed
    {
        if (! Auth::check()) {
            return [];
        }
 
        return Post::query()->get();
    }
}
// PHP 8.0
class PostService
{
    public function all(): array|Collection
    {
        if (! Auth::check()) {
            return [];
        }
 
        return Post::query()->get();
    }
}

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

Названные аргументы

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

// Before PHP 8.0
class ProcessImage
{
    public static function handle(string $path, int $height, int $width, string $type, int $quality, int $compression): void
    {
        // logic for handling image processing
    }
}
 
ProcessImage::handle('/path/to/image.jpg', 500, 300, 'jpg', 100, 5);
// PHP 8.0
class ProcessImage
{
    public static function handle(string $path, int $height, int $width, string $type, int $quality, int $compression): void
    {
        // logic for handling image processing
    }
}
 
ProcessImage::handle(
    path: '/path/to/image.jpg',
    height: 500,
    width: 300,
    type: 'jpg',
    quality: 100,
    compression: 5,
);

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

Match Expressions

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

// Before PHP 8.0
switch (string $method) {
    case 'GET':
        $method = 'GET';
        break;
    case 'POST':
        $method = 'POST';
        break;
    default:
        throw new Exception("$method is not supported yet.");
}
// PHP 8.0
match (string $method) {
    'GET' => $method = 'GET',
    'POST' => $method = 'POST',
    default => throw new Exception(
        message: "$method is not supported yet.",
    ),
};

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

Использование ::class для объектов

В прошлом, когда вы хотели передать строку класса методу, вам приходилось использовать что-то вроде get_class, что всегда казалось немного бессмысленным. Система уже знает о классе в то время, так как вы уже автоматически загрузили его или создали новый экземпляр. Давайте посмотрим на пример /

// Before PHP 8.0
$commandBus->dispatch(get_class($event), $payload);
// PHP 8.0
$commandBus->dispatch(
    event: $event::class,
    payload: $payload,
);

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

Нет захвата блоков catch

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

// Before PHP 8.0
try {
    $response = $this->sendRequest();
} catch (RequestException $exception) {
    Log::error('API request failed to send.');
}
// PHP 8.0
try {
    $response = $this->sendRequest();
} catch (RequestException) {
    Log::error('API request failed to send.');
}

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

Мы все можем согласиться с тем, что PHP 8.0 был фантастическим релизом, которого мы все ждали. Так как насчет PHP 8.1? Что это нам принесло? Конечно, он не может стать лучше, верно? Если бы вы думали об этом, как я, вы были бы неправы. И вот почему.

Enums

Lovely Enums, спаситель бессмысленных таблиц баз данных и плавающих констант в кодовых базах мира. Enums быстро стали одной из моих любимых функций PHP 8.1 - теперь я могу перемещать свои роли в Enums вместо того, чтобы хранить их в таблице, которая никогда не меняется. Я могу задать для http-методов значение Enums вместо констант или общедоступных статических свойств класса, который я никогда не хотел использовать. Давайте посмотрим.

// Before PHP 8.1
class Method
{
    public const GET = 'GET';
    public const POST = 'POST';
    public const PUT = 'PUT';
    public const PATCH = 'PATCH';
    public const DELETE = 'DELETE';
}
// PHP 8.1
enum Method: string
{
    case GET = 'GET';
    case POST = 'POST';
    case PUT = 'PUT';
    case PATCH = 'PATCH';
    case DELETE = 'DELETE';
}

Приведенный выше пример подчеркивает синтаксические различия, которые улучшены, но как насчет фактического использования? Давайте возьмем краткий пример черты, которую я обычно использую в интеграции API.

// Before PHP 8.1
trait SendsRequests
{
    public function send(string $method, string $uri, array $options = []): Response
    {
        if (! in_array($method, ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'])) {
            throw new InvalidArgumentException(
                message: "Method [$method] is not supported.",
            );
        }
 
        return $this->buildRequest()->send(
            method: $method,
            uri: $uri,
            options: $options,
        );
    }
}
// PHP 8.1
trait SendsRequests
{
    public function send(Method $method, string $uri, array $options = []): Response
    {
        return $this->buildRequest()->send(
            method: $method->value,
            uri: $uri,
            options: $options,
        );
    }
}

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

Распаковка массивов

Эта функция — это то, что я не был уверен, что буду использовать, пока не сделал это. Раньше нам всегда приходилось реплицировать вещи или объединять массивы, чтобы получить то, что нам нужно. Теперь мы можем просто распаковать массив, и поведение будет таким же. Я часто использую DTO в своем коде, и у всех них есть метод под названием toArray, который является для меня простым способом преобразования DTO во что-то, с чем Eloquent справится за меня. Давайте рассмотрим пример.

// Before PHP 8.1
final class CreateNewClient implements CreateNewClientContract
{
    public function handle(DataObjectContract $client, int $account): Model|Client
    {
        return Client::query()->create(
            attributes: array_merge(
                $client->toArray(),
                [
                    'account_id' => $account,
                ],
            ),
        );
    }
}
// PHP 8.1
final class CreateNewClient implements CreateNewClientContract
{
    public function handle(DataObjectContract $client, int $account): Model|Client
    {
        return Client::query()->create(
            attributes: [
                ...$client->toArray(),
                'account_id' => $account,
            ],
        );
    }
}

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

Новое в конструкторах

Что я могу сказать о новых конструкторах, которые вы еще не представляете? Немного, но я попробую. До PHP 8.1 иногда вы могли не передавать новый экземпляр класса конструктору по разным причинам, а иногда и делали. Это создало ситуацию, когда вы никогда не были уверены, нужно ли вам передавать экземпляр или нет. Имея этот момент - я просто передам ноль и посмотрю, что произойдет - надежда на лучший момент. Спасибо, PHP 8.1, за предоставление нам некоторых гарантий от крайних сроков и поспешных решений. Я прав? Давайте рассмотрим пример.

// Before PHP 8.1
class BuyerWorkflow
{
    public function __construct(
        private null|WorkflowStepContract $step = null
    ) {
        $this->step = new InitialBuyerStep();
    }
}
// PHP 8.1
class BuyerWorkflow
{
    public function __construct(
        private WorkflowStepContract $step = new InitialBuyerStep(),
    ) {}
}

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

Только для чтения Свойства

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

// Before PHP 8.1
class Post
{
    public function __construct() {
        protected string $title,
        protected string $content,
    }
 
    public function getTitle(): string
    {
        return $this->title;
    }
 
    public function getContent(): string
    {
        return $this->content;
    }
}
// PHP 8.1
class Post
{
    public function __construct() {
        public readonly string $title,
        public readonly string $content,
    }
}

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

Это, конечно, не исчерпывающий список - это всего лишь несколько ключевых вещей, которые выделяют релизы. Есть еще много вещей, которые были добавлены в PHP 8.0 и 8.1, которые я не упомянул здесь. Если вы хотите более подробно рассказать обо всех добавленных вещах, я настоятельно рекомендую проверить Stitcher Брента Руза, который старателен в своих обновлениях вокруг обновлений языка.

Я не буду углубляться в PHP 8.2 в этой статье, так как он еще не выпущен, поэтому я еще не сформулировал никаких мнений о новых функциях - но следите за этим пространством, как оно будет приходить. Не говоря уже об улучшениях в планировании PHP 8.3!

Какая ваша любимая современная функция PHP? Что бы вы хотели видеть добавленным в будущих релизах? Дайте нам знать в Твиттере!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297