PHP 8.4: Головні нововведення
PHP 8.4 — реліз «power tools»: він дає нові способи виражати інваріанти на межі властивостей (Property Hooks, асиметрична видимість), рухає внутрішності фреймворків уперед (Lazy Objects, нові Reflection helper’и) та модернізує проблемні зони (WHATWG-сумісні класи Dom\*, коректний multipart parsing не лише для POST). Водночас він підкручує edge cases (наприклад типову поведінку exit(), рекурсію під час порівнянь) і починає прибирати з обігу патерни, які роками дивували (implicitly-nullable параметри, lcg_value(), старі CSV defaults).
Зміст
- Property Hooks (логіка get/set у властивості)
- Асиметрична видимість (
public get,private set) - Lazy Objects (ghost/proxy через Reflection)
#[\\Deprecated]— deprecations у userland з хорошими повідомленнями- Новий DOM:
Dom\\*(WHATWG) + покращення XPath - Multipart не лише в POST:
request_parse_body() - Нові функції, які варто взяти в роботу (array_find, fpow, mb_trim, …)
- Зворотно несумісні зміни (міграційні нотатки)
- Deprecated (виправити до того, як стануть помилками)
- Інші зміни та експлуатація (Fibers/GC, builtin server, bcrypt cost)
Property Hooks (логіка get/set у властивості)
Властивості тепер можуть мати hooks get і/або set. Це дозволяє робити валідацію, нормалізацію, computed access і правила збереження на рівні властивості — без окремих методів і без «витоку» інваріантів по всьому коду.
Типові сценарії:
- Нормалізація під час запису (наприклад, casing)
- Валідація під час запису (fail fast)
- Computed read-only властивості (virtual / без backing value)
final class Person
{
public string $firstName {
set => ucfirst(strtolower($value));
}
public string $lastName {
set {
if (strlen($value) < 2) {
throw new InvalidArgumentException('Too short');
}
$this->lastName = $value;
}
}
public string $fullName {
get => $this->firstName . ' ' . $this->lastName;
}
}
Для DTO-шарів і фреймворків hooks часто знімають тонни boilerplate та тримають інваріанти поруч із даними.
Асиметрична видимість (public get, private set)
Тепер можна контролювати видимість set окремо від get. Це практичний компроміс: читання відкрите, а запис обмежений (наприклад, лише всередині класу).
final class User
{
public private(set) string $email;
public function __construct(string $email)
{
$this->email = $email;
}
}
Lazy Objects (ghost/proxy через Reflection)
У PHP 8.4 можна створювати об’єкти з відкладеною ініціалізацією (до першого доступу). Це насамперед для бібліотек/фреймворків: DI containers, ORM і proxy генератори можуть відкласти дорогу роботу (IO, hydration, побудову графа), але повернути коректно типізований об’єкт.
$initializer = static function ($obj) {
// initialize $obj lazily
};
$r = new ReflectionClass(Example::class);
$lazy = $r->newLazyGhost($initializer);
Reflection отримав нові методи/константи для перевірки та керування lazy-станом (явна ініціалізація, skip initialization під час serialize тощо).
#[\\Deprecated] — deprecations у userland з хорошими повідомленнями
Тепер можна позначати userland функції/методи/константи класу як deprecated через #[\Deprecated]. Поведінка відповідає core deprecations, але емісія йде як E_USER_DEPRECATED, а повідомлення можна зробити більш читабельними.
Для авторів бібліотек це великий плюс: deprecations стають передбачуваними й однаковими.
Новий DOM: Dom\\* (WHATWG) + покращення XPath
PHP 8.4 вводить Dom namespace з аналогами класичних DOM-класів (наприклад Dom\Node замість DOMNode). Мета — краща HTML5 сумісність і WHATWG spec alignment, щоб закрити давні проблеми DOM extension.
Є й практичні покращення:
DOMNode::compareDocumentPosition()і пов’язані константиDOMXPath::registerPhpFunctions()приймає будь-який callableDOMXPath::registerPhpFunctionNs()підтримує нативний синтаксис виклику (замістьphp:function('...'))
Якщо ви наслідуєте DOM класи в userland, стежте за новими членами та сумісністю декларацій (можливі compile errors при конфлікті імен).
Multipart не лише в POST: request_parse_body()
request_parse_body() дозволяє парсити RFC1867 (multipart) тіла у не-POST HTTP запитах. Це важливо для API, які коректно використовують PUT/PATCH з multipart (файли + form data).
Практичний начерк (обробка запиту на сервері):
if ($_SERVER['REQUEST_METHOD'] === 'PATCH') {
[$post, $files] = request_parse_body();
// $post — поля форми, $files — завантажені файли (RFC1867)
}
Нові функції, які варто взяти в роботу (array_find, fpow, mb_trim, …)
Хайлайти зі списку нових функцій:
- Core:
request_parse_body() - Standard:
array_all(),array_any(),array_find(),array_find_key(),fpow(),http_get_last_response_headers(),http_clear_last_response_headers() - MBString:
mb_trim(),mb_ltrim(),mb_rtrim(),mb_ucfirst(),mb_lcfirst() - Intl:
grapheme_str_split()і helper’и для time zones / parsing - BCMath:
bcround(),bcceil(),bcfloor(),bcdivmod() - Opcache:
opcache_jit_blacklist()
Якщо вам потрібні IEEE 754 semantics для edge cases під час піднесення до степеня, fpow() — рекомендована заміна для deprecated поведінки “(0) у від’ємному степені” в ** / pow().
Практичні рецепти
Знайти елемент без ручних циклів (array_find() / array_find_key())
$user = array_find($users, fn($u) => $u['id'] === $id);
if ($user === null) {
throw new RuntimeException('User not found');
}
Перевірити колекції (array_any() / array_all())
if (array_any($orders, fn($o) => $o['status'] === 'failed')) {
// alert / retry / short-circuit
}
$allPaid = array_all($orders, fn($o) => $o['status'] === 'paid');
IEEE 754 semantics для степенів (fpow())
// Deprecated у 8.4: 0 ** -2 (семантика ділення на нуль)
$x = fpow(0.0, -2.0); // IEEE 754 style result
Trim для multibyte рядків (mb_trim())
$name = mb_trim($name);
$title = mb_ucfirst(mb_trim($title));
Фінанси / явні режими округлення (RoundingMode + round())
У PHP 8.4 round() приймає enum RoundingMode (окрім старих PHP_ROUND_*), що краще читається в грошах і звітах, ніж «магічні» константи.
$lineTotal = round($amount * $qty, 2, RoundingMode::HalfAwayFromZero);
Переперевірте snapshot-тести сум: реалізацію round() у 8.4 переписано, частина крайніх випадків відрізняється від попередніх версій PHP.
Deprecations у userland (#[\Deprecated])
Бібліотеки можуть позначати API так само, як ядро PHP, із підказкою заміни та рядком since (PHP не валідує — використовуйте semver або тег релізу).
#[\Deprecated(message: 'Use findById()', since: '2.0')]
function find(): array
{
// ...
}
Зворотно несумісні зміни (міграційні нотатки)
Ядро мови / runtime
exit()/die(): поводяться більш «як функція» (їх можна передавати як callable, на них впливаєstrict_types, працює стандартна coercion). Невірні типи тепер стабільно кидаютьTypeErrorзамість приведення до рядка.- Рекурсія під час порівнянь: тепер
Errorзамість фатальногоE_ERROR. - Readonly +
__clone(): непряма модифікація через reference всередині__clone()заборонена (наприклад$ref = &$this->readonly). - Типи констант:
PHP_DEBUGіPHP_ZTSтеперbool(ранішеint). - Тимчасові імена файлів: імена upload/tempnam стали довші (+13 bytes).
E_STRICTприбрано: рівень помилок видалено; константаE_STRICTтепер deprecated.
Міграція resource → object (високий ризик для legacy перевірок)
Кілька extension-ів перейшли з resources на objects. Перевірки is_resource() потрібно замінити на перевірки false/null за документацією.
- DBA: тепер
Dba\Connectionзамістьdba_connectionresource. - ODBC: тепер
Odbc\Connection/Odbc\Resultobjects. - SOAP:
SoapClient::$httpurlтеперSoap\Url(null якщо відсутній);SoapClient::$sdlтеперSoap\Sdl(null якщо відсутній).
Нові warnings та exceptions
У багатьох місцях неправильні аргументи тепер дають ValueError / TypeError замість warnings або непослідовної поведінки (наприклад, валідація GD quality/speed, перевірка round() mode, обмеження на 1 byte в str_getcsv(), null bytes у XMLReader/Writer, валідації XSL тощо). Це покращує коректність, але може зламати код, який покладався на warnings.
Акценти по extension-ах
- DOM:
DOMImplementation::getFeature()видалено; клонуванняDOMXPathтепер кидаєError(раніше створювався непридатний клон). - GMP: клас
GMPтеперfinal(не можна наслідувати).
Deprecated (виправити до того, як стануть помилками)
Core
- Implicitly nullable parameters:
function f(string $s = null)deprecated, бо неявно розширює тип. Використовуйте?string $s = null(або переробіть сигнатуру, якщо далі йдуть обов’язкові параметри). 0 ** -n/pow(0, -n): deprecated (семантика ділення на нуль). Для IEEE 754 використовуйтеfpow().- Клас з ім’ям
_: deprecated. trigger_error(..., E_USER_ERROR): deprecated — краще exception абоexit().
Стандартна бібліотека / extension-и
- DatePeriod ISO-string constructor: deprecated; використовуйте
DatePeriod::createFromISO8601String(). - DOM: константа
DOM_PHP_ERRdeprecated; частина старих DOM properties формально deprecated. - Random:
lcg_value()deprecated; використовуйтеRandom\Randomizer::getFloat(). - Reflection:
ReflectionMethod::__construct()з 1 аргументом deprecated; використовуйтеReflectionMethod::createFromMethodName(). - MySQLi:
mysqli_ping(),mysqli_kill(),mysqli_refresh()та пов’язані refresh-константи deprecated (reconnect прибрали; натомість SQL команди). - CSV defaults: default escape для
fputcsv()/fgetcsv()/str_getcsv()deprecated — передавайте явно. - XML:
xml_set_object()deprecated; передавання non-callable strings уxml_set_*deprecated.
Sessions (ops/security hygiene)
Зміна session.sid_length і session.sid_bits_per_character deprecated; припиніть override та переконайтесь, що storage backend приймає 32-char hex session IDs. Кілька session.* INI опцій для trans-sid deprecated; константа SID deprecated.
Інші зміни та експлуатація (Fibers/GC, builtin server, bcrypt cost)
Що помітно в експлуатації:
- Fibers & destructors: fiber switching під час виконання destructor тепер дозволено; destructors, викликані GC, можуть виконуватися в окремому fiber (
gc_destructor_fiber). Якщо ви змішуєте Fibers і важкі destructors — тестуйте уважно. - Builtin server: пошук index-файла тепер піднімається по батьківських директоріях навіть коли шлях “виглядає як файл” (крапка в останньому компоненті).
- Apache: прибрано підтримку EOL Apache 2.0/2.2 (мінімум 2.4).
password_hash(): default bcrypt cost піднято з 10 до 12 — більше CPU, якщо покладались на defaults.- Rounding:
round()приймаєRoundingMode|int, додано нові режими та виправлено edge cases — перевірте фінансову математику снапшотами.
Підсумок
Для framework-команд найбільший виграш у 8.4 — Property Hooks + асиметрична видимість + Lazy Objects: інваріанти описуються саме там, де дані змінюються, а важку ініціалізацію можна відкласти до реального доступу. Для прикладних команд міграція здебільшого зводиться до суворіших помилок (ValueError/TypeError), переходів resource→object і прибирання deprecations (implicitly-nullable параметри, lcg_value(), CSV defaults) до того, як вони стануть жорсткими помилками.