• Час читання ~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();
    }
}

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

Названі аргументи

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

// 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

Покращення, яке всім подобається, з яким я говорив, значне покращення. У минулому ми використовували велику заяву про перемикання з кількома випадками, і давайте будемо чесними - це було не найприємніше, на що можна було дивитися або мати справу. Давайте розглянемо приклад.

// 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.",
    ),
};

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

Використання ::class на об'єктах

У минулому, коли ви хотіли передати рядок класу методу, вам доводилося використовувати щось на зразок get_class, що завжди здавалося трохи безглуздим. Система вже знає про клас на той момент, так як ви вже автозавантажили його або створили новий екземпляр. Давайте розглянемо приклад/

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

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

Жодне захоплення блоків улову

Іноді під час створення програми вам не потрібен доступ до винятку, який може бути кинутий. Однак для мене це трапляється рідко. Однак ваш пробіг може відрізнятися. Давайте розглянемо приклад.

// 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? Що це нам принесло? Звичайно, краще не може бути, правда? Якби ви думали про це, як я, ви помилялися б. Ось чому.

Lovely , the savior of pointless database tables and floating constants across the codebases of the world. have quickly become one of my favorite features of PHP 8.1 - I can now push my roles into instead of keeping them in a table that never changes. I can set HTTP methods to instead of constants or public static properties of a class I never really wanted to use. Let's have a look.

// 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 на те, з чим красномовний впорається за мене. Давайте розглянемо приклад.

// 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 - і просто дозволити класу впоратися з цим. Наведений вище приклад трохи спрощений. Чесно кажучи, це може бути пов'язано з тим, що у мене насправді раніше не було цих проблем. Однак я знаю, що багато хто з вас зробили б це і, сподіваюся, можуть побачити переваги використання цієї нової функції.

Властивості лише для читання

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

// 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