Связаться через Telegram Написать

← Все статьи

Что нового в PHP 8.5?

PHP 8.5, выпущенный в ноябре 2025 года, продолжает курс на расширение возможностей, упрощение синтаксиса и улучшение типизации.

Что нового в PHP 8.5?

Рассмотрим его основные изменения.


Оператор 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.dev/new-in-php85');

echo $uri->getScheme(); // https
echo $uri->getHost();   // avsoft-web.dev

Начиная с 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);

// При следующем запросе к тому же домену
// DNS и TCP/SSL соединения будут использоваться из кеша
$ch2 = curl_init("https://example.org/foo");
curl_setopt($ch2, CURLOPT_SHARE, $share);
$response2 = curl_exec($ch2);

Теперь PHP сам управляет ресурсом между запросами, и вам не нужно заботиться о хранении или согласовании дескрипторов.


Clone как функция

Передавая вторым аргументом ассоциативный массив в функцию clone(), можно обновлять свойства во время клонирования объектов. Это позволяет создавать измененные копии без изменения исходного объекта:

class App {
    public function __construct(public readonly string $env) {}

    public function withEnv(string $env): static
    {
        return clone($this, ['env' => $env]);
    }
}

$prod = new App('production');
$dev = $prod->withEnv('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 $status;
}

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) 1e20; // Warning

Магические методы __sleep() и __wakeup() были помечены как мягко устаревшие. Современный способ управления сериализацией — методы __serialize() и __unserialize(), которые дают больше контроля и предсказуемости.


🐘 Стоит ли обновляться?

PHP 8.5 не является революционным релизом, однако он делает код более безопасным и простым в поддержке, последовательно устраняя исторические недочеты языка.

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

Чтобы избежать неожиданных проблем, перед обновлением важно убедиться в совместимости зависимостей и используемых библиотек.

Опубликовано

← Все статьи