PHP 8.5 вийшов 20 листопада 2025 року, і за структурою це типовий мінорний реліз: три зміни, які видно прямо в синтаксисі (|>, clone with, #[\NoDiscard]), одне велике доповнення стандартної бібліотеки (розширення URI) і десяток дрібниць — array_first()/array_last(), атрибути на константах, замикання в константних виразах, final для просунутих у конструкторі властивостей, get_error_handler()/get_exception_handler(), бектрейси для фатальних помилок, персистентні share-хендли cURL. Сильна відповідь на це питання — не перелік, а сортування: що змінює те, як ви пишете код щодня, що закриває конкретний клас багів, а що є нішевим і чекатиме свого випадку.
Оператор конвеєра |> розвертає ланцюжок викликів зліва направо: замість htmlspecialchars(trim($raw)) пишеться $raw |> trim(...) |> htmlspecialchars(...). Механіка проста і має рівно одну пастку: права частина — це вираз, значення якого має бути callable, і викликається він з одним аргументом. Тому там стоїть синтаксис callable першого класу trim(...) (PHP 8.1), замикання або змінна з callable, але не trim() — такий запис викличе функцію без аргументів. Функції з параметром за посиланням у конвеєр не передаються взагалі, а для функцій із кількома обовʼязковими аргументами доводиться загортати виклик у fn, бо часткового застосування (str_replace('a', 'b', ?)) у PHP немає — відповідний RFC відхилили. Пріоритет у |> нижчий за арифметику й конкатенацію, тож 'x' . $y |> f(...) спершу склеїть рядки, а вже потім передасть їх далі; у сумнівних місцях (особливо поруч із ?? і тернарником) простіше поставити дужки. І окремо: жодного «пропускати null», як у ?->, тут немає — null спокійно піде в наступну ланку і впаде вже на її типі.
clone with закриває найнудніший бойлерплейт незмінних обʼєктів. До 8.5 wither писався як new static($this->a, $this->b, $new, $this->d) — і кожна нова властивість вимагала правки всіх таких методів. Тепер return clone $this with {url: $url}; копіює обʼєкт і присвоює лише перелічені властивості. Важливі деталі: конструктор не викликається, тому валідація з нього не спрацює; копіювання лишається поверхневим, тож вкладені мутабельні обʼєкти й далі спільні і глибоку копію робить __clone(), який викликається вже після присвоєння нових значень; readonly-властивість змінити так можна, але лише з коду, який має право її записувати, тобто зсередини класу-власника — ззовні буде Error: Cannot modify readonly property. Це саме той випадок, коли фічу варто брати одразу: вона не змінює семантику, лише прибирає рядки, які й так були помилконебезпечними.
Атрибут #[\NoDiscard] позначає функцію чи метод, чий результат не можна мовчки викидати; разом із ним у мову додали приведення (void), яким ігнорування оформлюють свідомо. Ефект — E_WARNING, а не виняток, тож це підказка, а не гарантія, і користь від неї є там, де попередження реально видно в логах і CI. Найкращі кандидати — withers незмінних обʼєктів і чисті перетворення, у яких виклик без присвоєння не робить нічого. Розширення URI — інша за вагою зміна: воно приносить Uri\Rfc3986\Uri (сувора граматика RFC 3986, resolve(), equals(), toString()/toRawString()) і Uri\WhatWg\Url (алгоритм браузерів, нормалізація хоста й IDN через getAsciiHost()/getUnicodeHost()). Обидва незмінні, з with*()-методами й без __toString(), а невдалий розбір — це Uri\InvalidUriException / Uri\WhatWg\InvalidUrlException у конструкторі або null зі статичного parse(). Практичний сенс — вибір стандарту стає явним: parse_url() не відповідає жодному з них, лише розрізає рядок і мовчки ковтає значну частину сміття, а розбіжність між вашою перевіркою і тим, куди насправді піде браузер, — класичне джерело SSRF-обходів.
Межі варто назвати чесно. Новий синтаксис — це підвищення мінімальної версії: для застосунку, який ви деплоїте самі, це питання CI й версії на серверах, а для бібліотеки з зовнішніми користувачами |> або clone with у коді означає відсікти всіх, хто ще на 8.3–8.4. Перед першим рядком нового синтаксису переконайтесь, що всі інструменти, які парсять код — статичний аналізатор, Pint/PHP-CS-Fixer, генератори документації, — уже вміють 8.5, бо інакше вони не розберуть файл цілком. І тримайте пропорцію: clone with і розширення URI дають користь одразу і майже без ризику, #[\NoDiscard] — точково на кількох ключових API, а |> варто вводити лише там, де ланцюжок справді складається з унарних функцій; конвеєр із трьох fn поспіль читається гірше, ніж три звичайні змінні з іменами.