<? phpukraine СПІВБЕСІДИ
Пошук по платформі
CORE PHP · MIDDLE ЧАСТО ПИТАЮТЬ

Що нового в PHP 8.0–8.4 і що з цього ви використовуєте щодня?

8.0 дав match, іменовані аргументи, promotion, union-типи і `?->`; 8.1 - enum, readonly, first-class callable, Fibers; 8.2 - readonly-класи і DNF-типи; 8.3 - типізовані константи класу і `#[\Override]`; 8.4 - property hooks, асиметричну видимість і `new Foo()->method()` без дужок. Спільна лінія цих релізів: більше типів у сигнатурах і менше способів зіпсувати стан обʼєкта ззовні.

Проєкт на 8.3. Що з нового у вас реально в коді, а не в changelog?
Яку мінімальну версію PHP ви поставили в composer.json на останньому проєкті і чому саме її?
Назвіть три речі з вісімки, які змінили те, як ви пишете класи.
А що з PHP 8 ви свідомо не використовуєте і чому?
php8 match enum readonly property-hooks asymmetric-visibility versions

У п'яти релізах вісімки видно спільний напрямок. PHP додає типи туди, де їх раніше не було (union у 8.0, intersection у 8.1, DNF у 8.2, константи класу у 8.3), і поступово забирає способи змінити стан обʼєкта повз його методи (readonly у 8.1, readonly-класи у 8.2, private(set) у 8.4). Побічний ефект: код стає дружнішим до статичного аналізу, бо значна частина того, що раніше жила в PHPDoc і перевірялась PHPStan, переїхала в сигнатури й перевіряється рушієм.

Щоденний набір із 8.0 у більшості проєктів той самий: match замість довгих switch і if/elseif, promotion у конструкторі, іменовані аргументи для методів із трьома-чотирма опційними параметрами, ?-> там, де звʼязок справді буває порожнім. Union-типи використовуються рідше, ніж можна очікувати: у нормально спроєктованому коді int|string частіше означає, що абстракція протікає, а справжня їхня ніша - межа із зовнішніми даними. З 8.1 у щоденний обіг зайшли enum і readonly. Класичний набір const STATUS_NEW = 'new' перетворюється на backed enum із методами, а DTO між шарами - на final readonly class, після чого питання «хто це зіпсував по дорозі» просто зникає. First-class callable ($this->handle(...), Currency::from(...)) прибрав рядкові callable, які нічим не перевірялись і мовчки ламались при перейменуванні методу.

8.2 і 8.3 виглядають як дрібні релізи, і за кількістю RFC так і є, але саме вони добили обʼєктну модель до робочого стану. readonly class знімає повторення модифікатора на кожній властивості й заразом забороняє динамічні властивості. DNF-типи дозволили нарешті писати (Countable&ArrayAccess)|null: до цього перетин узагалі не можна було зробити нульованим. Типізовані константи класу (const string Draft = 'draft') закривають старий трюк, коли нащадок тихо підміняв константу значенням іншого типу. #[\Override] ловить у компіляції найнеприємніший клас помилок: метод, який ви вважали перевизначенням, а він через одруківку в назві чи зміну сигнатури в базовому класі перетворився на просто ще один метод. У 8.3 також полагодили readonly і __clone: readonly-властивість тепер можна переприсвоїти всередині __clone(), тому глибоке копіювання незмінних обʼєктів більше не впирається в Error.

8.4 - перший реліз, який змінює саме те, як пишеться тіло класу. Property hooks прибирають гетери-однорядковики: public string $title { get => sprintf(...); } дає обчислювану властивість без бекінг-поля, доступну як звичайне поле, тому рефакторинг з простої властивості в обчислювану більше не ламає жоден виклик. Асиметрична видимість (public private(set) int $version) розвʼязує стару проблему сутностей, де вибір був між публічною властивістю, яку хтось ззовні перепише, і приватною з гетером на три рядки. new Foo()->method() без зовнішніх дужок - косметика, зате прибирає найчастіший привід завести зайву змінну. Дужки після імені класу лишились обовʼязковими: new Foo->method() не працює.

Практичну межу задає рядок "php": "^8.х" у composer.json і версія на проді, а не те, скільки фіч ви знаєте. Станом на вересень 2026 PHP 8.1 не отримує навіть security-патчів (закінчилось 31 грудня 2025), у 8.2 security-фаза добігає кінця в грудні 2026, 8.3 підтримується патчами безпеки до кінця 2027, 8.4 в активній підтримці до кінця 2026. Розумний мінімум для нового проєкту - 8.3; 8.4 варто брати одразу, якщо ви пишете багато сутностей і DTO, бо hooks плюс private(set) економлять помітний обсяг механічного коду. Для легасі корисніше рухатись версіями по одній і після кожного стрибка проганяти PHPStan: найбільше часу зʼїдають не нові фічі, а накопичені депрекейти - динамічні властивості з 8.2, ${} в рядках з 8.2, неявний nullable у сигнатурах (function f(Foo $x = null)) з 8.4.

declare(strict_types=1);

enum Currency: string                       // 8.1: backed enum замість набору констант
{
    case Uah = 'UAH';
    case Eur = 'EUR';
}

final readonly class Money implements Stringable  // 8.2: readonly-клас цілком
{
    public function __construct(
        public int $minor,                  // 8.0: promotion прямо в конструкторі
        public Currency $currency = Currency::Uah,
    ) {}

    #[\Override]                            // 8.3: не скомпілюється, якщо перекривати нічого
    public function __toString(): string
    {
        return match ($this->currency) {    // 8.0: match, строге порівняння, без break
            Currency::Uah => sprintf('%.2f грн', $this->minor / 100),
            Currency::Eur => sprintf('%.2f EUR', $this->minor / 100),
        };
    }
}

final class Order
{
    public const string Draft = 'draft';    // 8.3: типізована константа класу

    public private(set) int $version = 1;   // 8.4: читають усі, пише лише сам клас

    public string $title {                  // 8.4: hook, окремого поля під ним немає
        get => sprintf('Замовлення #%d', $this->id);
    }

    public function __construct(private int $id, private ?Money $total = null) {}

    public function applyMeta((Countable&ArrayAccess)|null $meta): void  // 8.2: DNF-тип
    {
        $this->version += $meta?->count() ?? 0;   // 8.0: nullsafe обриває весь ланцюг
    }
}

echo new Order(id: 7)->title;               // 8.0 named args + 8.4 new без зайвих дужок
$codes = array_map(Currency::from(...), ['UAH', 'EUR']);  // 8.1: first-class callable
echo new Money(minor: 24_990);              // 249.90 грн
Що фічі привʼязані до версій правильно: enum і readonly - 8.1, readonly-клас - 8.2, `#[\Override]` - 8.3, property hooks і `private(set)` - 8.4.
Що кандидат називає не список RFC, а те, що змінилось у щоденному коді: константи стали enum, DTO стали readonly, гетери-однорядковики зникли на користь hooks.
Розуміння, що межа доступного визначається рядком `"php": "^8.х"` у composer.json і версією на проді, а не тим, що вийшло останнім.
Знання поточного стану підтримки: 8.1 уже без будь-яких патчів, 8.2 доживає security-фази, робочий мінімум для нового проєкту - 8.3/8.4.
Чесну відповідь про те, що використовується рідко: Fibers напряму майже ніхто не пише, JIT у типовому вебі не дає приросту.
Плутати релізи: називати enum фічею 8.0, readonly-класи - 8.1, а property hooks - 8.3.
Казати «match - це просто switch без break»: різниця в строгому порівнянні, у тому, що match - вираз, і в `\UnhandledMatchError` замість тихого проходу повз.
Вважати, що `readonly` дає глибоку незмінність: посилання заморожене, а стан обʼєкта всередині властивості міняється вільно.
Плутати `readonly` з `private(set)`: перше забороняє будь-який другий запис, друге дозволяє класу міняти властивість скільки завгодно і закриває запис лише ззовні.
Розповідати про 8.4-фічі на співбесіді в проєкт, де в composer.json стоїть `^8.1`, і не мати відповіді на «а як це зробити без hooks».
Перелічувати весь changelog без жодного прикладу з власного коду - це найпомітніший сигнал, що фічі відомі з статті, а не з практики.
ПОРАДА

Не переказуйте changelog. Візьміть один клас і покажіть, як він виглядав на 7.4 і як виглядає зараз: константи стали backed enum, конструктор схуднув до promotion, DTO став `final readonly`, ланцюжок `if/elseif` став `match`, а гетер на один рядок - property hook. Одразу після цього назвіть версію з composer.json - це показує, що ви думаєте про те, що доступно, а не про те, що модно.

оновлено 9 вересня 2026 · ліцензія CC-BY-SA-4.0 Знайшли неточність? Напишіть →
ПЕРЕВІРТЕ СЕБЕ

8.0: match, іменовані аргументи, promotion, union-типи, `?->`. 8.1: enum, readonly-властивості, Fibers, first-class callable. 8.2: readonly-класи і DNF-типи. 8.3: типізовані константи класу і `#[\Override]`. 8.4: property hooks, асиметрична видимість, `new Foo()->method()` без обгортання в дужки.