Що нового в PHP 8.3

Що нового в PHP 8.3
Переклад 12 хв. читання
18 серпня 2023
PHP 8.3 буде випущено 23 листопада 2023 року; у ньому покращено доступні тільки для читання класи, нову функцію json_validate(), доповнення до нещодавно доданого класу Randomizer, виявлено переповнення стеку та багато іншого.

У цій статті ми розглянемо всі можливості, покращення продуктивності, зміни та застарілі версії один за одним.

Поправки readonly властивостей

У цьому RFC було запропоновано дві зміни, з яких прийнято лише одну: можливість повторної ініціалізації властивостей, доступних лише для читання, під час клонування. Це може здатися великим кроком, але цей RFC стосується лише дуже специфічного (але важливого) граничного випадку: перезапису значень властивостей у __clone(), щоб дозволити глибоке клонування властивостей, доступних лише для читання.

readonly class Post
{
    public function __construct(
        public DateTime $createdAt,
    ) {}
    
    public function __clone()
    {
        $this->createdAt = new DateTime(); 
        // This is allowed,
        // even though `createdAt` is a readonly property.
    }
}

Типізовані константи класу

Тепер ви можете типізувати константи класу:

class Foo
{
    const string BAR = 'baz'; 
} 

Атрибут #[Override]

Новий атрибут #[Override] використовується, щоб показати наміри програміста. По суті, він говорить: "Я знаю, що цей метод перевизначає батьківський метод. Якщо це коли-небудь зміниться, будь ласка, дайте мені знати".

Ось приклад:

abstract class Parent
{
    public function methodWithDefaultImplementation(): int
    {
        return 1;
    }
}

final class Child extends Parent
{
    #[Override]
    public function methodWithDefaultImplementation(): void
    {
        return 2; // The overridden method
    }
} 

Тепер уявімо, що в якийсь момент батьківський метод змінює ім'я свого методу:

abstract class Parent
{
    public function methodWithNewImplementation(): int
    {
        return 1;
    }
}

Завдяки атрибуту #[Override] PHP зможе виявити, що Child::methodWithDefaultImplementation() більше нічого не перевизначає, і видасть помилку.

Від'ємні індекси в масивах

Якщо у вас порожній масив, додайте елемент з від'ємним індексом, а потім додайте ще один елемент, цей другий елемент завжди буде починатися з індексом 0:

$array = [];

$array[-5] = 'a';
$array[] = 'b';

var_export($array);

//array (
//  -5 => 'a',
//  0 => 'b',
//)

Починаючи з PHP 8.3, наступний елемент буде додано з індексом -4:

//array (
//  -5 => 'a',
//  -4 => 'b',
//)

Анонімні класи тільки для читання

Раніше ви не могли позначити анонімні класи як readonly. Це виправлено у PHP 8.3:

$class = new readonly class {
    public function __construct(
        public string $foo = 'bar',
    ) {}
};

Нова функція json_validate()

Раніше єдиним способом перевірити, чи є рядок коректним JSON, було декодувати його і перевірити, чи не було згенеровано жодних помилок. Ця нова функція json_validate() є корисною, якщо вам потрібно лише дізнатися, чи є вхідний рядок коректним JSON, оскільки вона використовує менше пам'яті порівняно з декодуванням рядка.

json_validate(string $json, int $depth = 512, int $flags = 0): bool

Доповнення класу Randomizer

У PHP 8.2 додано новий клас Randomizer. Це оновлення приносить кілька незначних доповнень:

Randomizer::getBytesFromString(string $string, int $length): string

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

Randomizer::getFloat(
    float $min,
    float $max,
    IntervalBoundary $boundary = IntervalBoundary::ClosedOpen
): float

getFloat() повертає float зі значенням між $min і $max. Ви можете визначити, чи потрібно включати значення $min і $max, за допомогою IntervalBoundary enum. Закрите значення означає, що значення включено, а відкрите - виключено.

Randomizer::nextFloat(): float {}

Нарешті, nextFloat() - це скорочення від getFloat(0, 1, IntervalBoundary::ClosedOpen), іншими словами: ця функція повертає випадкове число з проміжком від 0 до 1, де 1 не включається.

Динамічий доступ до констант класу

PHP 8.3 дозволяє отримувати доступ до констант за допомогою більш динамічного синтаксису:

class Foo 
{
    const BAR = 'bar';
}

$name = 'BAR';
 
// Instead of this:
constant(Foo::class . '::' . $name);

// You can now do this:
Foo::{$name};

Винятки щодо дати/часу, які є доцільнішими

У багатьох випадках PHP просто згенерує виключення або помилку; або видасть попередження або помилку, коли щось піде не так у роботі з датами та часом. У цьому документі RFC розглядаються всі ці крайні випадки та додаються відповідні, спеціальні Exception для них.

Тепер у нас є такі Exception, як DateMalformedIntervalStringException, DateInvalidOperationException і DateRangeError.

Загалом, ці доповнення не зламають жодного коду, оскільки ці нові винятки та помилки є підкласами загальних класів Exception та Error. Однак, є три невеликі зміни, які призводять до порушень, що вносяться з цим RFC:

  • Якщо Епоха не вписується в ціле число PHP, тепер повертає нову DateRangeError замість типової ValueError, яка не є його підкласом. Це проблема тільки для 32-бітних платформ.
  • Єдині неспеціальні специфікації відносного часу підтримуються для попередження про віднімання за допомогою DateTime::sub(), а date_sub() стає новим DateInvalidOperationException.
  • Невідомий або неправильний формат (%s) у позиції %d (%c): %s і String %s містить не релятивні елементи попередження, які створюються при розборі неправильних/пошкоджених DateInterval рядків, тепер генеруватимуть нове DateMalformedIntervalStringException при використанні з інтерфейсом OO, замість того, щоб показувати попередження і повертати false.

Покращено обробку помилок у unserialize()

unserialize() тепер завжди видаватиме E_WARNING при виникненні проблем замість E_NOTICE у деяких випадках.

У цьому RFC також пропонувалося додати більше винятків при виконанні unserialize(), але цю частину не було прийнято.

Зміни у функції range()

Зі списку змін:

  • Тепер генерується помилка типу TypeError у разі передачі об'єктів, ресурсів або масивів як граничних входів
  • Більш зрозуміле повідомлення про помилку ValueError з'являється у разі передачі 0 для $step
  • Тепер генерується ValueError при використанні від'ємного $step для збільшення діапазонів
  • Якщо $step є числом з плаваючою комою тепер це може бути інтерпретовано як int
  • Тепер генерується ValueError, якщо будь-який аргумент дорівнює нескінченності або NAN
  • Тепер видається E_WARNING, якщо $start або $end є порожнім рядком. Значення продовжує приводитися до значення 0.
  • E_WARNING тепер видається, якщо $start або $end містить більше одного байта, тільки якщо це нечисловий рядок.
  • E_WARNING тепер видається, якщо $start або $end приводиться до цілого числа, тому що інший граничний вхід є числом. (наприклад, range(5, 'z');)
  • Тепер видається E_WARNING, якщо $step є float при спробі згенерувати діапазон символів, за винятком випадків, коли обидва граничні входи є числовими рядками (наприклад, range('5', '9', 0.5); не видає попередження)
  • range() тепер генерує список символів, якщо один з граничних входів є рядковою цифрою, замість приведення іншого входу до типу int (наприклад, range('5', 'z');)

Трейти та статичні властивості

Важливо зазначити, що це означає внесення змін з порушенням цілісності:

Використання трейтів зі статичними властивостями тепер перевизначатиме статичні властивості, успадковані від батьківського класу. Це створить окреме сховище статичних властивостей для поточного класу. Це аналогічно додаванню статичної властивості безпосередньо до класу без використання трейтів.

Виявлення переповнення стеку

У PHP 8.3 додано дві нові ini-директиви zend.max_allowed_stack_size та zend.reserved_stack_size. Програми, які близькі до переповнення стеку викликів, тепер можуть згенерувати помилку при використанні більше, ніж різниця між zend.max_allowed_stack_size і zend.reserved_stack_size.

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

За замовчуванням zend.max_allowed_stack_size дорівнює 0, тобто PHP автоматично визначить значення. Ви також можете вказати -1, щоб вказати відсутність обмеження або конкретну кількість байт. Директива zend.reserved_stack_size використовується для визначення "буферної зони", щоб PHP міг видавати помилку замість того, щоб фактично вичерпувати пам'ять. Значення тут повинно бути вказано у байтах, але PHP визначить для вас розумне значення за замовчуванням, тому вам не обов'язково встановлювати його, якщо тільки ви не стикаєтесь з граничними випадками для конкретних програм.

Наостанок, для волокон, наявна директива fiber.stack_size використовується як максимально дозволений розмір стека.

zend.max_allowed_stack_size=128K

Нова функція mb_str_pad

З RFC:

У PHP різні функції для роботи з рядками доступні у двох варіантах: для байтових рядків і для багатобайтових рядків. Однак, серед багатобайтових функцій помітна відсутність mbstring-еквівалента функції str_pad(). Функції str_pad() бракує підтримки багатобайтових символів, що спричиняє проблеми під час роботи з мовами, які використовують багатобайтові кодування, наприклад, UTF-8. Цей RFC пропонує додати таку функцію до PHP, яку ми назвемо mb_str_pad().

The function looks like this:

function mb_str_pad(
    string $string, 
    int $length, 
    string $pad_string = " ", 
    int $pad_type = STR_PAD_RIGHT, 
    ?string $encoding = null,
): string {}

Магічні замкнення методів та іменовані аргументи

Припустимо, у вас є клас, який підтримує магічні методи

class Test {
    public function __call($name, $args) 
    {
        var_dump($name, $args);
    }
    
    public static function __callStatic($name, $args) {
        var_dump($name, $args);
    }
}

PHP 8.3 дозволяє створювати замикання з цих методів, а потім передавати іменовані аргументи цим замиканням. Раніше це було неможливо.

$test = new Test();

$closure = $test->magic(...);

$closure(a: 'hello', b: 'world'); 

Інваріантна постійна видимість

Раніше при реалізації інтерфейсу видимість констант не перевірялася. У PHP 8.3 цю помилку виправлено, але в деяких місцях це може призвести до порушення цілісності коду, якщо ви не знали про таку поведінку.

interface I {
    public const FOO = 'foo';
}

class C implements I {
    private const FOO = 'foo';
}

Трохи застарілих функцій з RFC

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

  • Вилучено передачу від'ємних $widths у mb_strimwidth()
  • Вилучено константу NumberFormatter::TYPE_CURRENCY
  • Застарілу та вилучено несправну реалізацію до PHP 7.1 Mt19937 (MT_RAND_PHP)
  • Вилучено виклик ldap_connect() з 2 параметрами $host та $port
  • Вилучено залишки коду, що обчислює рядки

Невеликі, але помітні зміни

Не кожна зміна в PHP проходить процес створення RFC. Більшість змін пов'язані з обслуговуванням і виправленням помилок і не вимагають створення RFC. Всі ці зміни перераховані в документі UPGRADING. Я перерахую деякі з них, але ви обов'язково повинні прочитати весь список, якщо хочете дізнатися про найдрібніші деталі.

  • При використанні FFI функції C, які повертають тип void, тепер повертають null замість FFI\CData:void
  • posix_getrlimit() тепер приймає необов'язковий параметр $res, щоб дозволити отримати єдиний ліміт ресурсів.
  • gc_status() має чотири нових поля: running, protected, full та buffer_size.
  • class_alias() тепер підтримує створення псевдоніму внутрішнього класу.
  • mysqli_poll() тепер генерує ValueError, коли передаються аргументи read або error.
  • array_pad() тепер обмежена лише максимальною кількістю елементів, які може мати масив. Раніше за один раз можна було додати не більше 1048576 елементів.
  • Нові posix-функції: posix_sysconf(), posix_pathconf(), posix_fpathconf() та posix_eaccess()
  • Багаторазове виконання proc_get_status() тепер завжди повертатиме правильне значення на posix-системах.
  • Видалено директиву opcache.consistency_checks ini
  • Покращено array_sum() та array_product()

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

Джерело: What's new in PHP 8.3
Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Коментарі (0)

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

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

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