<? phpukraine ЕКОСИСТЕМА
⌕Пошук по платформі
PHP · ЩО НОВОГО

Що нового в PHP 8.3

  1. 01Типізовані константи класу: const string VERSION, і нащадок не підмінить тип.
  2. 02#[\Override] зупиняє компіляцію, якщо батьківського методу вже немає.
  3. 03json_validate(), Foo::{$name}, readonly у __clone() і окремі винятки для дат.
СТАТУС Лише безпека
Реліз
23 листопада 2023
Останній патч
8.3.35
Активна підтримка до
31.12.2025
Безпека до
31.12.2027
НОВІ МОЖЛИВОСТІ · 4

Нові можливості

1.1 · RFC

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

Константи класів, інтерфейсів, трейтів і enum можуть мати тип. Значення не того типу або нащадок, що перевизначає константу з несумісним типом, зупиняють компіляцію. Раніше тип константи жив лише в PHPDoc, і рушій його не перевіряв.

+ PHP 8.3
interface HasVersion
{
    const string VERSION = '1.0';
}
− PHP 8.2
interface HasVersion
{
    /** @var string */
    const VERSION = '1.0';
}
1.2 · RFC

#[\Override]

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

+ PHP 8.3
class UserRepository extends Repository
{
    #[\Override]
    public function findAll(): array { /* … */ }
}
− PHP 8.2
class UserRepository extends Repository
{
    // у Repository метод перейменували, а тут ніхто не помітив
    public function findAll(): array { /* … */ }
}
1.3 · RFC

readonly у __clone()

Під час клонування readonly-властивість можна перезаписати в __clone(). Раніше спроба кидала Error, тож копія ділила вкладений обʼєкт з оригіналом, і заради глибокої копії readonly доводилось прибирати.

+ PHP 8.3
public readonly ArrayObject $lines;

public function __clone()
{
    $this->lines = clone $this->lines;
}
− PHP 8.2
// readonly прибрали, щоб __clone() міг замінити обʼєкт
public ArrayObject $lines;
1.4 · RFC

Окремі винятки для дат

Розширення date отримало власні винятки: DateMalformedStringException для рядка, який не вдалось розібрати, DateInvalidTimeZoneException для невідомого поясу, DateMalformedIntervalStringException для інтервалу. Усі вони успадковують DateException, а той Exception, тож старі catch (Exception) працюють як раніше.

+ PHP 8.3
try {
    $at = new DateTimeImmutable($input);
} catch (DateMalformedStringException $e) {
    // саме некоректний рядок дати
}
− PHP 8.2
try {
    $at = new DateTimeImmutable($input);
} catch (Exception $e) {
    // будь-яка помилка, не лише формат
}
СИНТАКСИС · 3

Синтаксис

2.1 · RFC

Foo::{$name}

Імʼя константи або кейсу enum тепер можна підставити виразом у фігурних дужках. Раніше для цього був лише constant() з рядком 'Клас::ІМʼЯ'. Запис узгоджений з $object->{$name} для властивостей.

+ PHP 8.3
$limit = Limits::{$plan};
− PHP 8.2
$limit = constant(Limits::class . '::' . $plan);
2.2 · НОВЕ

Анонімні readonly-класи

Модифікатор readonly, який класи отримали у 8.2, тепер працює і з анонімними класами. Усі їхні властивості стають readonly, тож одноразовий незмінний обʼєкт, наприклад DTO в тесті, не потребує окремого іменованого класу.

+ PHP 8.3
$point = new readonly class(10, 20) {
    public function __construct(public int $x, public int $y) {}
};
− PHP 8.2
$point = new class(10, 20) {
    public function __construct(
        public readonly int $x,
        public readonly int $y,
    ) {}
};
2.3 · RFC

Будь-який вираз у static-змінних

Початкове значення статичної змінної у функції тепер може бути будь-яким виразом, зокрема викликом функції. Він виконується один раз, при першому виклику. Раніше дозволялись лише константні вирази, і кеш доводилось заводити через null і ??=.

+ PHP 8.3
static $config = loadConfig();
− PHP 8.2
static $config = null;
$config ??= loadConfig();
ФУНКЦІЇ · 4

Нові функції

3.1 · RFC

json_validate()

Функція перевіряє синтаксис і не будує масивів чи обʼєктів, тож на великих payload економить памʼять. Причину помилки, як і після json_decode(), покаже json_last_error_msg().

+ PHP 8.3
$isValid = json_validate($payload);
− PHP 8.2
json_decode($payload);
$isValid = json_last_error() === JSON_ERROR_NONE;
3.2 · RFC

Нові методи Randomizer

Random\Randomizer збирає рядок із заданого алфавіту: коди підтвердження, промокоди, короткі ідентифікатори без схожих символів. getFloat() повертає рівномірне дробове число в межах і дає вибрати, чи включати краї, а nextFloat() число від 0 до 1. За замовчуванням працює криптостійкий engine Secure.

+ PHP 8.3
$code = (new Random\Randomizer)->getBytesFromString($alphabet, 6);
− PHP 8.2
$code = '';
for ($i = 0; $i < 6; $i++) {
    $code .= $alphabet[random_int(0, strlen($alphabet) - 1)];
}
3.3 · RFC

str_increment() і str_decrement()

++ на рядках давно вміє перетворювати 'Az' на 'Ba', але поводиться неочевидно, а -- на нечислових рядках узагалі нічого не робить. Тепер для цього є явні функції, які працюють в обидва боки і кидають ValueError на порожньому рядку чи символах поза латиницею й цифрами.

+ PHP 8.3
$column = str_increment('Az'); // 'Ba'
$back = str_decrement($column); // 'Az'
− PHP 8.2
$column = 'Az';
$column++; // 'Ba'
3.4 · RFC

mb_str_pad()

str_pad() рахує байти, тож кирилиця вирівнюється криво: «Київ» займає 8 байтів, а не 4 символи. mb_str_pad() рахує символи і приймає кодування, тож колонки в консолі та рядки фіксованої ширини виходять рівними.

+ PHP 8.3
mb_str_pad('Київ', 10, '.'); // 'Київ......'
− PHP 8.2
str_pad('Київ', 10, '.'); // 'Київ..'
ПРОДУКТИВНІСТЬ · 2

Продуктивність

4.1 · НОВЕ

Час роботи GC у gc_status()

gc_status() тепер показує, чи збирач працює просто зараз, наскільки заповнений буфер коренів і скільки секунд пішло на збирання, деструктори та звільнення памʼяті. Для довгих воркерів це швидкий спосіб перевірити, чи не GC гальмує обробку.

+ PHP 8.3
gc_status()['collector_time'];
// плюс running, protected, full, buffer_size і чотири лічильники часу
− PHP 8.2
gc_status();
// runs, collected, threshold, roots
4.2 · ДВИГУН

Контроль глибини нативного стеку

Глибока рекурсія через внутрішні функції чи магічні методи могла вичерпати нативний стек, і процес падав без жодної діагностики. Тепер рушій кидає Error раніше, ніж це станеться. Межу задає zend.max_allowed_stack_size, за замовчуванням 0, тобто визначити автоматично.

+ PHP 8.3
zend.max_allowed_stack_size=0
zend.reserved_stack_size=0
; Error: Maximum call stack size of … reached. Infinite recursion?
− PHP 8.2
; рекурсія через __get() чи array_map()
; Segmentation fault (core dumped)
БЕЗПЕКА · 1

Безпека

5.1 · ЗМІНА

unserialize() помічає зайві дані

Якщо після коректного серіалізованого значення лишились зайві байти, unserialize() видає Warning. Раніше хвіст мовчки відкидався, і пошкоджений чи дописаний payload з cookie або кешу проходив непоміченим.

+ PHP 8.3
unserialize('i:42;junk');
// Warning: Extra data starting at offset 5 of 9 bytes
− PHP 8.2
unserialize('i:42;junk'); // 42, без жодного сигналу
DEPRECATED

Застаріло

Ще працює, але дає попередження і зникне в одній із наступних версій.

get_class() без аргументівТе саме стосується get_parent_class(). Пишіть self::class і parent::class.
assert_options()Разом із константами ASSERT_*. Перевірки вмикайте через zend.assertions.
MT_RAND_PHPСтара зламана реалізація Mt19937. Для нового коду беріть Random\Randomizer.
++ і -- на нечислових рядках-- на такому рядку нічого не робить, ++ з символами поза латиницею й цифрами теж застарів.
ReflectionProperty::setValue() з одним аргументомДля статичної властивості передавайте null першим, setValue(null, $value).
ldap_connect() з двома аргументамиПередавайте один URI, наприклад ldap_connect('ldap://ldap.example.com:389').
BREAKING

Ламає сумісність

Перевірте це до оновлення: такий код перестане працювати або поводитиметься інакше.

range()Нульовий крок або відʼємний крок для зростаючого діапазону кидають ValueError, масив як межа кидає TypeError, а порожній чи багатобайтовий рядок дає Warning.
unserialize()Помилки розбору тепер E_WARNING замість E_NOTICE, суворий обробник може зробити з них виняток.
array_sum() і array_product()Рахують так само, як + і *. Масив чи обʼєкт серед значень дає Warning, раніше їх мовчки пропускали.
Відʼємний ключ у порожньому масивіПісля $a[-5] = 'x' наступний $a[] отримає ключ -4, а не 0.
modify() і sub() у датахНекоректний рядок у modify() кидає DateMalformedStringException замість Warning і false, а sub() з відносним інтервалом на кшталт «next weekday» кидає DateInvalidOperationException.

Як оновитися

0 / 5