PHP 8: код «До» та «Після» (порівняння з PHP 7.4)

PHP 8: код «До» та «Після» (порівняння з PHP 7.4)
9 хв. читання
20 серпня 2020

Залишилося всього кілька місяців до виходу PHP 8, і в цій версії дійсно є багато хорошого. В цій статті я хочу поділитися як зміниться код з приходом PHP 8.

Підписники подій з атрибутами

Я стараюся не зловживати атрибутами, але у випадку з налаштуванням підписників подій, наприклад, вони дуже до речі.

Останнім часом я працював над системами, де таких установок було дуже багато. Візьмемо приклад:

// До
class CartsProjector implements Projector
{
    use ProjectsEvents;

    protected array $handlesEvents = [
        CartStartedEvent::class => 'onCartStarted',
        CartItemAddedEvent::class => 'onCartItemAdded',
        CartItemRemovedEvent::class => 'onCartItemRemoved',
        CartExpiredEvent::class => 'onCartExpired',
        CartCheckedOutEvent::class => 'onCartCheckedOut',
        CouponAddedToCartItemEvent::class => 'onCouponAddedToCartItem',
    ];

    public function onCartStarted(CartStartedEvent $event): void
    { /* … */ }

    public function onCartItemAdded(CartItemAddedEvent $event): void
    { /* … */ }

    public function onCartItemRemoved(CartItemRemovedEvent $event): void
    { /* … */ }

    public function onCartCheckedOut(CartCheckedOutEvent $event): void
    { /* … */ }

    public function onCartExpired(CartExpiredEvent $event): void
    { /* … */ }

    public function onCouponAddedToCartItem(CouponAddedToCartItemEvent $event): void
    { /* … */ }
}

PHP 7

У атрибутів є дві переваги:

  • Код для налаштування підписників подій і обробників зібрано в одному місці, і мені не потрібно прокручувати файл до початку, щоб дізнатися, чи правильно налаштований підписник.
  • Мені більше не потрібно турбуватися про написання та управлінні іменами методів у вигляді рядків (коли IDE не може їх автоматично заповнити, немає статичного аналізу помилок і не працює перейменування методів).

Але, на щастя PHP 8 розв'язує ці проблеми

class CartsProjector implements Projector
{
    use ProjectsEvents;

    @@SubscribesTo(CartStartedEvent::class)
    public function onCartStarted(CartStartedEvent $event): void
    { /* … */ }

    @@SubscribesTo(CartItemAddedEvent::class)
    public function onCartItemAdded(CartItemAddedEvent $event): void
    { /* … */ }

    @@SubscribesTo(CartItemRemovedEvent::class)
    public function onCartItemRemoved(CartItemRemovedEvent $event): void
    { /* … */ }

    @@SubscribesTo(CartCheckedOutEvent::class)
    public function onCartCheckedOut(CartCheckedOutEvent $event): void
    { /* … */ }

    @@SubscribesTo(CartExpiredEvent::class)
    public function onCartExpired(CartExpiredEvent $event): void
    { /* … */ }

    @@SubscribesTo(CouponAddedToCartItemEvent::class)
    public function onCouponAddedToCartItem(CouponAddedToCartItemEvent $event): void
    { /* … */ }
}

PHP 8

Static замість doc-блоків

Це не така важливе зміна, але я стикаюся з цим кожен день. Я часто бачу, що мені все ще потрібні doc-блоки, коли треба вказати, що функція має зворотний тип static.

Якщо в PHP 7.4 мені потрібно було писати:

/**
 * @return static
 */
public static function new()
{
    return new static();
}

PHP 7.4

То тепер достатньо:

public static function new(): static
{
    return new static();
}

PHP 8

DTO, передача властивостей і іменованих аргументів

Я досить багато писав про використання системи типів PHP і патерну DTO (data transfer objects). Природно, я часто використовую DTO у своєму власному коді, тому можете уявити, наскільки я щасливий, що тепер маю можливість переписати це:

class CustomerData extends DataTransferObject
{
    public string $name;

    public string $email;

    public int $age;
    
    public static function fromRequest(
        CustomerRequest $request
    ): self {
        return new self([
            'name' => $request->get('name'),
            'email' => $request->get('email'),
            'age' => $request->get('age'),
        ]);
    }
}

$data = CustomerData::fromRequest($customerRequest);

PHP 7.4

Ось так краще:

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

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

PHP 8

Зверніть увагу на використання передачі властивостей конструктора іменованих параметрів. Так, їх можна передавати за допомогою іменованих масивів і оператора Spread.

Перерахування і match

Ви використовуєте перерахування з деякими методами, які повертають результат в залежності від конкретного значення з перерахування?

/**
 * @method static self PENDING()
 * @method static self PAID()
 */
class InvoiceState extends Enum
{
    private const PENDING = 'pending';
    private const PAID = 'paid';

    public function getColour(): string
    {
        return [
            self::PENDING => 'orange',
            self::PAID => 'green',
        ][$this->value] ?? 'gray';   
    }
}

PHP 7.4

Я б сказав, що для складніших умов вам краще використовувати патерн Стан, але є випадки, коли досить перерахування. Цей дивний синтаксис масиву вже є скороченням для більш громіздкого умовного виразу:

/**
 * @method static self PENDING()
 * @method static self PAID()
 */
class InvoiceState extends Enum
{
    private const PENDING = 'pending';
    private const PAID = 'paid';

    public function getColour(): string
    {
        if ($this->value === self::PENDING) {
            return 'orange';
        }
    
        if ($this->value === self::PAID) {
            return 'green'
        }

        return 'gray';
    }
}

PHP 7.4 — альтернативний варіант

Але в PHP 8 замість цього ми можемо використовувати match.

/**
 * @method static self PENDING()
 * @method static self PAID()
 */
class InvoiceState extends Enum
{
    private const PENDING = 'pending';
    private const PAID = 'paid';

    public function getColour(): string
    {
        return match ($this->value) {
            self::PENDING => 'orange',
            self::PAID => 'green',
            default => 'gray',
        };
}

PHP 8

Об'єднання замість doc-блоків

Працює це аналогічно тому як було описано раніше типу static що повертається функцією.

/**
 * @param string|int $input
 *
 * @return string 
 */
public function sanitize($input): string;

PHP 7.4

public function sanitize(string|int $input): string;

PHP 8

Генерація винятків

Раніше ви не могли використовувати throw у виразі, а це означало, що вам доводилося писати, наприклад, ось такі перевірки:

public function (array $input): void
{
    if (! isset($input['bar'])) {
        throw BarIsMissing::new();
    }
    
    $bar = $input['bar'];

    // …
}

PHP 7.4

В PHP 8 throw став виразом, що означає, що ви можете використовувати його ось так:

public function (array $input): void
{
    $bar = $input['bar'] ?? throw BarIsMissing::new();

    // …
}

PHP 8

Оператор nullsafe

Якщо ви знайомі з оператором null coalescing, вам відомі його недоліки: він не працює з викликами методів. Тому мені часто були потрібні проміжні перевірки або відповідні для цієї мети функції фреймворків:

$startDate = $booking->getStartDate();
$dateAsString = $startDate ? $startDate->asDateTimeString() : null;

PHP 7.4

З появою оператора nullsafe я можу розв'язувати цю задачу набагато простіше.

$dateAsString = $booking->getStartDate()?->asDateTimeString();

PHP 8

А які нововведення в PHP 8 вважаєте важливими ви?

Джерело ENG: stitcher.io

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація