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

PHP 8.5 продолжает курс «выразить больше в самом языке»: оператор конвейера (|>), #[\NoDiscard], чтобы не игнорировать возвращаемые значения, и замыкания в константных выражениях для атрибутов и значений по умолчанию. На уровне платформы — всегда включённое URI-расширение, строгий режим filter и серьёзные изменения PDO / Opcache: закладывайте время на QA не только под новый синтаксис, но и под расширения и сборку/INI.

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


Оператор конвейера (|>)

Оператор pipe передаёт значение слева в одноаргументный callable справа. Удобен для цепочек преобразований без временных переменных.

$result = 'Hello World' |> strlen(...);
// То же: $result = strlen('Hello World');

Ограничения: справа — callable ровно с одним параметром (не by-ref). Стрелочные функции в связке с |> нужно заключать в скобки. Подробности — в руководстве по functional operators.

Замыкания и first-class callable в константных выражениях

В PHP 8.5 замыкания и first-class callable разрешены в константных выражениях, в том числе:

  • аргументы атрибутов;
  • значения по умолчанию у свойств и параметров;
  • константы и константы класса.

Это расширяет выразительность там, где недостаточно скаляра на этапе компиляции/линковки.

#[\NoDiscard] и приведение (void)

#[\NoDiscard] помечает функции/методы, у которых возвращаемое значение нежелательно игнорировать (по духу близко к [[nodiscard]] в C++).

(void) явно говорит: «значение я отбрасываю намеренно». Само по себе выполнение не меняет, но может подавлять предупреждения, связанные с #[\NoDiscard], и помогает статическому анализу.

#[\NoDiscard]
function loadConfig(): array { return []; }

(void) loadConfig(); // намеренный discard

Расширение URI (RFC 3986 / WHATWG URL)

В PHP 8.5 добавлено всегда включённое расширение uri для разбора и работы с URI/URL по RFC 3986 и модели WHATWG URL. Имеет смысл там, где важна спецификация, а не ручной substr по пользовательскому вводу.

Заметные обновления библиотек и расширений

  • Filter: флаг FILTER_THROW_ON_FAILURE — при ошибке валидации можно получить исключение вместо false (нельзя совмещать с FILTER_NULL_ON_FAILURE).
  • Intl: например IntlListFormatter, Locale::addLikelySubtags() / minimizeSubtags(), дополнительные константы стилей NumberFormatter для валют (зависят от версии ICU).
  • Session / cookies: session_set_cookie_params(), session_get_cookie_params(), session_start(), а также setcookie() / setrawcookie() — поддержка partitioned cookies через ключ "partitioned".
  • Standard: mail() при sendmail возвращает реальные ошибки и предупреждает при аварийном завершении sendmail; getimagesize() — HEIF/HEIC, SVG (при libxml), дополнительные поля единиц измерения размеров.
  • DOM: например Dom\Element::$outerHTML, свойство $children у реализаций Dom\ParentNode.
  • cURL: новые ключи/опции curl_getinfo и curl_setopt (прокси, аутентификация, большие размеры, режимы follow location, TLS и т.д. — часто зависят от версии libcurl).

Новые функции (array_first, array_last, обработчики ошибок, …)

Кратко:

  • Core: get_error_handler(), get_exception_handler(), Closure::getCurrent()
  • Standard: array_first(), array_last()
  • Reflection: доработки ReflectionConstant, ReflectionProperty::getMangledName()
  • Opcache: opcache_is_script_cached_in_file_cache()
  • PostgreSQL / SQLite / DOM и др. — см. new functions

Практические рецепты

Конвейер нормализации

$slug = $raw
    |> trim(...)
    |> strtolower(...)
    |> preg_replace('/\s+/', '-', ...);

Filter с исключениями вместо false

$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);

Первый и последний элемент без ручного индекса

$first = array_first($list);
$last  = array_last($list);

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

Ядро языка / runtime

  • class_alias(): нельзя использовать "array" и "callable" как имена алиаса.
  • Нестрогое сравнение с bool для «несравнимых» объектов (enum, CurlHandle, …): поведение унифицировано с (bool)$object.
  • gc_collect_cycles(): возвращаемое значение больше не учитывает строки/ресурсы, косвенно собранные через циклы.
  • Final-подклассы: где раньше ограничивали подстановку static, допускаются self или имя конкретного класса.
  • Tick handlers: выполняются после shutdown-функций, деструкторов и очистки output handlers.
  • Трейты: изменён порядок привязки относительно родителя (тонкий момент — перепроверьте трейт-тяжёлый код).
  • Ошибки компиляции/линковки классов: изменён порядок обработки; исключения из user error handler могут вести себя иначе.
  • #[\Attribute] на abstract class, enum, interface или trait: ошибка на этапе компиляции, если не отложена валидация через #[\DelayedTargetValidation] (см. мануал).
  • INI disable_classes: удалена.
  • Деструктуризация не-массива и не-null через []/list(): предупреждение.
  • Приведение к int для вне диапазона float / числовых строк и NAN: предупреждения.

Opcache

  • Opcache всегда встроен в бинарник PHP и загружен; строка zend_extension=opcache.so / .dll даёт предупреждение. Сборка shared opcache.so и флаги configure для отдельного модуля убраны — обновите документацию и образы.

PDO (высокий риск)

  • Числовые значения PDO::FETCH_* для ряда флагов изменились — перепроверьте конфиги/БД, где хранятся режимы выборки числом.
  • Аргументы конструктора при PDO::FETCH_CLASS: семантика как у call_user_func_array (строковые ключи как именованные аргументы; изменены правила по ref).
  • Ограничения на сочетание FETCH_PROPS_LATE, FETCH_INTO с fetchAll() и т.д. — см. migration85 incompatible.

DOM / SPL / прочее

  • Клонирование живых NodeList / NamedNodeMap (классический DOM и Dom\*) теперь падает (раньше давало нерабочий объект).
  • ArrayObject: не принимает enum как элементы.
  • mysqli: повторный вызов конструктора на уже созданном объекте — Error.
  • Многие расширения вместо TypeError или тихого поведения бросают ValueError (FileInfo и nul bytes, LDAP, сокеты, SNMP и т.д.).

Deprecated (чеклист верхнего уровня)

В 8.5 затронуты не только «мелочи»: старые имена приведений (integer) и т.п., обратные кавычки как shell_exec, мягкий отказ от __sleep/__wakeup в пользу __serialize/__unserialize, поверхность PDO (схема uri: в DSN, драйверные константы/методы на базовом PDO), curl_close / share close, finfo_close, imagedestroy, xml_parser_free и многое другое. Источник истины — официальный список migration85 deprecated; планируйте рефакторинг до перехода на жёсткие ошибки в следующих минорных версиях.

Прочие изменения и эксплуатация (Opcache, INI, производительность)

  • CLI: --ini=diff показывает отличия INI от встроенных дефолтов; cli_set_process_title() падает, если заголовок слишком длинный (без тихого обрезания).
  • Core INI: например fatal_error_backtraces, max_memory_limit, ограничения на рост memory_limit в runtime.
  • Opcache INI: например opcache.file_cache_read_only для read-only кэша; настройка JIT hot loop; корректный отчёт при смене opcache.memory_consumption.
  • Производительность: быстрее создание исключений, ускорение колбэков по массивам, urlencode, Reflection, SIMD на ARM, опциональный TAILCALL VM при Clang ≥ 19 и др. — см. other changes.

Итог

PHP 8.5 сочетает удобный синтаксис (pipes, константные замыкания, семантика discard) и ужесточение платформы (PDO, Opcache, валидация в расширениях). План апгрейда: внедрить синтаксис там, где он проясняет намерение, затем прогнать интеграционные тесты по режимам PDO, cookie/session, коду, завязанному на устаревшие close-хелперы и на старое нестрогое сравнение объектов с bool.