PHP 8.3: Главные нововведения

Выпуск про «полировку» для команд на PHP 8.2.x: сильнее инварианты (#[Override], типизированные константы), удобнее JSON и строки (json_validate, str_increment/str_decrement), плюс длинный хвост мелких изменений runtime, которые проявляются под нагрузкой или в редких ветках кода.

Содержание (Оглавление)


PHP 8.3 меньше про один «главный» фичер и больше про жёсткие контракты: можно задать типы констант, проверять override через #[\Override] и валидировать JSON без декодирования — при этом движок и стандартная библиотека местами строже (range(), proc_get_status(), исключения в Date/DOM). Заложите время на QA вокруг инкремента/декремента строк (deprecated операторы, новые helper’ы), reflection (ReflectionProperty::setValue) и настроек assert (deprecated INI и assert_options()).

#[\Override] — ловим отсутствующие override на этапе компиляции

Помечайте методы, которые должны переопределять родителя или интерфейс. Если имя ошибочно или метод убрали из родителя, PHP падает рано, вместо того чтобы тихо добавить новый метод.

interface Logger
{
    public function log(string $message): void;
}

final class FileLogger implements Logger
{
    #[\Override]
    public function log(string $message): void
    {
        // ...
    }
}

Особенно полезно в больших кодовых базах при рефакторингах интерфейсов.

Типизированные константы класса

Константы в классах, интерфейсах, трейтах и enum могут объявлять типы — константы приближаются к остальной системе типов.

interface Config
{
    public const string APP_NAME = 'MyApp';
}

Меньше ошибок «не тот тип константы», которые раньше всплывали только в местах использования.

Readonly: анонимные классы и клонирование

  • Анонимные классы могут быть объявлены readonly.
  • Readonly-свойства можно переинициализировать при clone (когда это допускает граф объектов) — клоны immutable-объектов становятся менее болезненными, чем чисто на паттернах 8.2.

Улучшения языка (quality-of-life)

  • Динамический доступ к константе класса: SomeClass::{$name} для имён констант во время выполнения.
  • Инициализаторы static-переменных могут использовать произвольные выражения (не только константы).
  • final у методов трейта при импорте метода из трейта.
  • Замыкания из magic methods могут принимать именованные аргументы при вызове.
  • php.ini: синтаксис fallback/default для более чистой конфигурации.

Новые функции (json_validate, str_*, DOM, Random и др.)

json_validate()

Проверка JSON без декодирования в значения PHP — удобно для API, очередей и быстрой проверки до дорогого json_decode().

if (!json_validate($payload)) {
    throw new InvalidArgumentException('Invalid JSON');
}
$data = json_decode($payload, true, flags: JSON_THROW_ON_ERROR);

str_increment() / str_decrement()

Предпочтительная замена устаревшим ++/-- для строк (см. deprecations). Используйте для алфавитно-цифровых счётчиков, где раньше опирались на операторы инкремента.

$next = str_increment('a9'); // шаг без строкового ++
$prev = str_decrement($next);

DOM, Intl, Random, POSIX и др.

В PHP 8.3 добавлено много методов DOM (например insertAdjacentElement, getRootNode, replaceChildren), helper’ов Intl для календарей, методов Random у Randomizer (getFloat, nextFloat, …) и функций POSIX (sysconf/pathconf/eaccess) — полезно для системных скриптов и tooling’а.

Обратно несовместимые изменения (миграционные заметки)

Стек / таймеры / fibers

  • Глубокая рекурсия у предела стека может выбросить Error при превышении zend.max_allowed_stack_size (минус резерв); у fibers учитывается fiber.stack_size.
  • Zend Max Execution Timers по умолчанию включены для ZTS-сборок на Linux — следите за долгоживущими CLI-воркерами.

Процессы

  • proc_get_status() на POSIX возвращает корректные значения при повторных вызовах; результат может кэшироваться (смотрите ключ "cached"). proc_close() после proc_get_status() теперь даёт корректный код выхода (не -1).

Массивы и трейты

  • Видимость констант класса: при наследовании из интерфейсов теперь проверяется variance — код с более мягкой видимостью может потребовать правок.
  • Пустой массив + отрицательный первый индекс: следующий неявный ключ — n+1 (не 0).
  • Трейты со static-свойствами: унаследованные static у родителя переобъявляются для каждого использующего трейт класса (отдельное хранилище) — может затронуть тонкое static-состояние.

Стандартная библиотека (высокий риск)

  • range(): строже валидация (TypeError/ValueError для плохих входов), больше предупреждений на странных границах, иное поведение для символьных диапазонов, когда границы выглядят как числа — перепроверьте код, где диапазоны строятся из пользовательского ввода.
  • number_format(): отрицательный $decimals теперь округляет до десятичной точки (раньше игнорировалось).
  • file(): неверные комбинации флагов отклоняются (например FILE_APPEND раньше молча принимался).

Date / DOM / FFI / Opcache

  • Date: более выразительные иерархии DateError/DateException вместо общих предупреждений/исключений — обновите catch.
  • DOM: поведение ближе к спецификации для узлов без родителя; правки createAttributeNS(); новые члены могут конфликтовать с пользовательскими подклассами — проверьте совместимость сигнатур.
  • FFI: void C-функции возвращают null, а не фиктивный объект FFI\CData:void.
  • Opcache: opcache.consistency_checks удалён (был несовместим с tracing JIT / кэшем наследования).

WeakMap

  • Самоссылочные ключи в WeakMap могут собираться в циклах, где достижимость есть только через итерацию — пересмотрите экзотические кэши.

Deprecated (инкремент/декремент строк, get_class(), assert INI)

Строковые ++ / --

Инкремент/декремент для пустых или нечисловых строк помечен deprecated; нечисловой инкремент — «мягкий» deprecated. Для нового кода предпочтительны str_increment() / str_decrement().

get_class() / get_parent_class() без аргументов

Вызов без аргументов deprecated — передавайте объект или имя класса явно.

Assert

  • assert_options() и связанные константы — deprecated.
  • INI-настройки assert.* — deprecated; миграция описана в разделе migration 8.3 про обработку INI.

Прочее

  • Reflection: ReflectionProperty::setValue($value) (один аргумент) для static-свойств deprecated — для static передавайте null как объект.
  • SQLite3: предпочтительны исключения; enableExceptions(false) даёт deprecation.
  • Random: вариант MT_RAND_PHP deprecated.

Прочие изменения и эксплуатация (gc_status, streams, highlights)

  • gc_status() отдаёт больше полей по времени (collector/destructor/free) — полезно при настройке памяти.
  • Streams: fread() на сокетах может вернуть раньше при наличии данных в буфере; memory streams ведут себя ближе к файлам при seek за конец.
  • open_basedir: runtime ini_set отклоняет пути с .. даже с префиксом ./.
  • highlight_string/file: изменилась структура HTML — обновите снапшоты тестов, если подсветку сравниваете построчно.

Итог

PHP 8.3 выгоден тем, кто считает константы и override частью типового контракта: типизированные константы и #[\Override] уменьшают «тихий дрейф» при рефакторинге. Дополните это json_validate для дешёвой проверкой и явным планом по строковому инкременту и assert — именно там чаще всего удивляется legacy-код.