Что нового в PHP 8.5?
PHP 8.5, выпущенный в ноябре 2025 года, продолжает курс на расширение возможностей, упрощение синтаксиса и улучшение типизации.
Рассмотрим его основные изменения.
Оператор Pipe
Оператор Pipe (|>), также называемый конвейерным оператором, позволяет связывать вызовы функций в цепочку без использования промежуточных переменных. То есть теперь вместо такой записи:
$input = ' Pipe operator in PHP 8.5 ';
$slug = strtolower(
str_replace('.', '',
str_replace(' ', '-',
trim($input)
)
)
);
или такой:
$input = ' Pipe operator in PHP 8.5 ';
$temp = trim($input);
$temp = str_replace(' ', '-', $temp);
$temp = str_replace('.', '', $temp);
$slug = strtolower($temp);
можно делать так:
$input = ' Pipe operator in PHP 8.5 ';
$slug = $input
|> trim(...)
|> (fn($str) => str_replace(' ', '-', $str))
|> (fn($str) => str_replace('.', '', $str))
|> strtolower(...);
Оператор передает результат одной функции напрямую в следующую, что делает его удобным для подобного рода задач.
Функции array_first() и array_last()
Функции array_first() и array_last() возвращают первое или последнее значение массива, соответственно. В случае, если массив пустой, возвращается null. Теперь вместо использования тернарного оператора:
$firstEl = $array === []
? null
: $array[array_key_first($array)];
$lastEl = $array === []
? null
: $array[array_key_last($array)];
можно использовать новые функции:
$firstEl = array_first($array);
$lastEl = array_last($array);
Благодаря этим функциям работать с массивами станет гораздо проще.
Функция grapheme_levenshtein()
Существующие в PHP функции levenshtein() и mb_levenshtein() работают с байтами или кодовыми точками Unicode, что может давать неверные результаты для сложных символов Unicode, таких как эмодзи или буквы с диакритическими знаками. Новая функция grapheme_levenshtein() вычисляет расстояние Левенштейна на основе графем — единиц текста, которые пользователь видит как один символ, даже если они состоят из нескольких кодовых точек Unicode, например:
var_dump(
grapheme_levenshtein(
"\u{00e9}", // é
"\u{0065}\u{0301}" // e + комбинирующий акут, визуально тоже é
)
); // Результат: 0
Функция считает символы одинаковыми, даже если их базовое кодирование различается.
Функции get_error_handler() и get_exception_handler()
В PHP можно настроить собственные обработчики ошибок и исключений с помощью функций set_error_handler() и set_exception_handler(). Однако ранее не было простого способа проверить, какой обработчик используется в данный момент. Функции get_error_handler() и get_exception_handler() позволяют напрямую получать текущие обработчики ошибок и исключений:
// Обработчик ошибок
$handler = function (int $errno, string $errstr, ?string $errfile, ?int $errline) {
echo "Ошибка: " . $errstr . "\n";
};
var_dump(get_error_handler()); // NULL, пока обработчик не установлен
set_error_handler($handler);
var_dump(get_error_handler() === $handler); // bool(true), установлен наш обработчик
// Обработчик исключений
$handler = function (Throwable $ex) {
echo "Исключение: " . $ex::class . ": " . $ex->getMessage() . "\n";
};
var_dump(get_exception_handler()); // NULL, пока обработчик не установлен
set_exception_handler($handler);
var_dump(get_exception_handler() === $handler); // bool(true), установлен наш обработчик
Это Особенно полезно для библиотек или фреймворков, которым нужно временно подменять глобальные обработчики ошибок и исключений, не теряя ссылку на исходные.
Трассировка фатальных ошибок
Фатальные ошибки (такие, как истечение времени выполнения или памяти) теперь содержат обратную трассировку. Это настраивается через новую INI-директиву fatal_error_backtraces, что значительно упростит отладку в сложных случаях. Трассировка стека показывает последовательность вызовов функций, приведших к ошибке, аналогично тому, что выводится при исключениях.
Модуль URI
Модуль URI предоставляет API для безопасного анализа и модификации URI и URL в соответствии со стандартами RFC 3986 и WHATWG URL. Например:
use Uri\Rfc3986\Uri;
$uri = new Uri('https://avsoft-web.ru/new-in-php85');
echo $uri->getScheme(); // https
echo $uri->getHost(); // avsoft-web.ru
Начиная с PHP 8.5, этот модуль входит в стандартную библиотеку PHP.
Постоянные дескрипторы cURL Share
В отличие от предыдущей функции curl_share_init(), дескрипторы, созданные с помощью curl_share_init_persistent(), живут между PHP запросами. Если повторно вызвать функцию с теми же параметрами, будет получен тот же ресурсный дескриптор:
$share = curl_share_init_persistent([
CURL_LOCK_DATA_DNS, // Кэшируем DNS записи
CURL_LOCK_DATA_CONNECT, // Кэшируем TCP/SSL соединения
]);
$ch1 = curl_init("https://example.org");
curl_setopt($ch1, CURLOPT_SHARE, $share);
$response1 = curl_exec($ch1);
curl_close($ch1);
// При следующем запросе к тому же домену
// DNS и TCP/SSL соединения будут использоваться из кеша
$ch2 = curl_init("https://example.org/foo");
curl_setopt($ch2, CURLOPT_SHARE, $share);
$response2 = curl_exec($ch2);
curl_close($ch2);
Теперь PHP сам управляет ресурсом между запросами, и вам не нужно заботиться о хранении или согласовании дескрипторов.
Clone как функция
Передавая вторым аргументом ассоциативный массив в функцию clone(), можно обновлять свойства во время клонирования объектов. Это позволяет создавать измененные копии без изменения исходного объекта:
class App {
public function __construct(public readonly string $env) {}
}
$prod = new App('production');
$dev = clone($prod, ['env' => 'development']);
Этот подход особенно удобен при работе с иммутабельными объектами и readonly свойствами.
Замыкания в константных выражениях
Новая возможность PHP 8.5 позволяет использовать статические замыкания (анонимные функции) в константных выражениях. Константное выражение — это значение, которое должно быть полностью определено во время компиляции. К таким выражениям относятся параметры атрибутов, константы, значения по умолчанию свойств и параметров функций. Пример с пользовательским атрибутом:
#[Validator(
static function(string $value): bool {
return preg_match('/^[a-z]+$/', $value);
}
)]
class User {
public string $username;
}
Пример с константой класса:
class Formatter {
public const TO_UPPER = static function(string $str): string {
return strtoupper($str);
};
}
echo (Formatter::TO_UPPER)('php'); // PHP
И пример со значением по умолчанию:
function upper_map(
array $data,
Closure $fn = static function ($x) { return strtoupper($x); }
) {
return array_map($fn, $data);
}
var_dump(upper_map(['a', 'b', 'c'])); // ['A', 'B', 'C']
Однако есть некоторые ограничения. Чтобы использовать замыкания в константных выражениях, они должны быть static, то есть не могут использовать $this или захватывать переменные из окружающей области видимости. Поэтому нельзя использовать use($foo) или стрелочные функции.
Атрибут #[\NoDiscard]
Атрибут #[\NoDiscard] позволяет разработчикам указывать, что возвращаемое значение функции не должно игнорироваться. Добавив атрибут к функции, PHP будет проверять, используется ли возвращаемое значение, и выдавать предупреждение, если это не так:
#[\NoDiscard]
function setStatus(int $status): int
{
// Логика изменения статуса
return $result;
}
setStatus(5); // Warning
$result = setStatus(5); // Предупреждения не будет
Такой подход позволит повысить безопасность API, где возвращаемое значение важно, но про него можно легко забыть.
Атрибуты констант
Теперь к константам, объявленным с помощью const, можно добавлять атрибуты:
#[Deprecated(reason: 'Use API_V2_URL instead. This will be removed in v2.0.')]
const API_V1_URL = 'https://api.example.com/v1';
Это позволяет указывать метаданные для них. Но следует иметь в виду, что атрибуты не применимы к константам, созданным с помощью define().
Устаревшая функциональность и изменения в обратной совместимости
Альтернативные приведения скалярных типов через (boolean), (integer), (double) и (binary) больше не поддерживаются. Вместо них следует использовать (bool), (int), (float) и (string) соответственно.
$value = (boolean) $foo; // Устаревший синтаксис
$value = (bool) $foo; // Современный вариант
Завершение операторов case точкой с запятой вместо двоеточия больше не поддерживается.
switch ($status) {
case 1; // Больше не работает
echo 'success';
break;
}
switch ($status) {
case 1: // Правильно
echo 'success';
break;
}
Использование null в качестве смещения массива или при вызове array_key_exists() объявлено устаревшим. Теперь нужно использовать пустую строку.
$array[null] = 'value'; // Устаревшее поведение
array_key_exists(null, $array);
$array[''] = 'value'; // Современный вариант
array_key_exists('', $array);
Теперь при преобразовании NAN в другие типы выдается предупреждение.
$value = (int) NAN; // Warning
Деструктуризация значений, не являющихся массивами (кроме null), с помощью [] или list() теперь также выдает предупреждение.
['a', 'b'] = 'test'; // Warning
Предупреждение выдается при преобразовании чисел с плавающей точкой (или строк, похожих на числа с плавающей точкой) в целые числа (int), если они не могут быть представлены в виде целого числа.
$value = (int) 6.7; // Warning
Магические методы __sleep() и __wakeup() были помечены как мягко устаревшие. Современный способ управления сериализацией — методы __serialize() и __unserialize(), которые дают больше контроля и предсказуемости.
🐘 Стоит ли обновляться?
PHP 8.5 не является революционным релизом,
однако он делает код более безопасным и простым в поддержке,
последовательно устраняя исторические недочеты языка.
Обновление особенно оправдано для проектов,
в которых важны предсказуемость поведения, строгая типизация
и долгосрочная поддерживаемость кода — например,
при разработке библиотек и фреймворков.
Чтобы избежать неожиданных проблем,
перед обновлением важно убедиться в совместимости зависимостей и используемых библиотек.