PHP 8.4: Основни нововъведения
PHP 8.4 е релийз с „power tools“: дава нови начини да изразявате инварианти на границата на свойствата (Property Hooks, асиметрична видимост), развива вътрешностите на framework-ите (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 върху свойства)
Свойствата вече могат да имат get и/или set hooks. Така валидацията, нормализацията, computed access и правилата за storage са в самото свойство — без отделни getter/setter методи и без разпръснати инварианти.
Типични шаблони:
- Нормализация при запис (напр. 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 слоеве и framework-и 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 позволява обекти с отложена инициализация (до първи достъп). Това е насочено към библиотеки/framework-и: DI containers, ORM-и и proxy генератори могат да отложат скъпа работа (IO, hydration, graph build), но да върнат правилно типизиран обект.
$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 и съобщенията са по-четими.
За автори на библиотеки това е голямо улеснение: deprecation-ите стават предвидими и последователни.
Нов 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()поддържа native function call синтаксис (вместоphp:function('...'))
Ако наследявате DOM класове в userland, внимавайте за нови членове и съвместимост на декларациите (възможни compile errors при конфликт на имена).
Multipart и извън POST: request_parse_body()
request_parse_body() позволява парсване на RFC1867 (multipart) тела в не-POST HTTP заявки. Полезно за API-та, които използват PUT/PATCH с multipart (файлове + form data).
Практичен набросък (server-side обработка):
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
Multibyte-safe trim (mb_trim())
$name = mb_trim($name);
$title = mb_ucfirst(mb_trim($title));
Финанси / явни режими на закръгляване (RoundingMode + round())
В PHP 8.4 round() приема RoundingMode enum (освен старите PHP_ROUND_*), което е по-четимо в пари и отчети от „магически“ константи.
$lineTotal = round($amount * $qty, 2, RoundingMode::HalfAwayFromZero);
Претествайте snapshot тестове за суми: реализацията на round() в 8.4 е пренаписана и част от edge cases се различава от по-ранни версии PHP.
Deprecations в userland (#[\Deprecated])
Библиотеките могат да маркират API като core, с подсказка за замяна и since (не се валидира от PHP — използвайте semver или release tag).
#[\Deprecated(message: 'Use findById()', since: '2.0')]
function find(): array
{
// ...
}
Обратно несъвместими промени (миграционни бележки)
Core език/runtime
exit()/die(): по-функционално поведение (може да се подава като callable, влияе се отstrict_types, работят стандартни coercions). Невалидни типове вече последователно хвърлятTypeErrorвместо да се cast-ват към string.- Рекурсия при сравнения: вече хвърля
Errorвместо фаталенE_ERROR. - Readonly +
__clone(): индиректна промяна чрез reference в__clone()вече не е позволена (напр.$ref = &$this->readonly). - Типове на константи:
PHP_DEBUGиPHP_ZTSвече саbool(предиint). - Temp filenames: имената за 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_ERRе deprecated; някои стари DOM properties са формално deprecated. - Random:
lcg_value()е deprecated; ползвайтеRandom\Randomizer::getFloat(). - Reflection:
ReflectionMethod::__construct()с 1 аргумент е deprecated; ползвайтеReflectionMethod::createFromMethodName(). - MySQLi:
mysqli_ping(),mysqli_kill(),mysqli_refresh()и related 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 — ретествайте финансови изчисления със snapshot-и.
Обобщение
За framework екипи най-голямата стойност в 8.4 е Property Hooks + асиметрична видимост + Lazy Objects: инварианти се описват точно там, където данните се променят, а тежка инициализация се отлага до реален достъп. За приложенията миграцията най-често е заради по-строги грешки (ValueError/TypeError), resource→object преходи и изчистване на deprecations (implicitly-nullable параметри, lcg_value(), CSV defaults) преди да станат твърди грешки.