PHP 13029 ~ 12 мин.

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

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

PHP 8.5 был выпущен 20 ноября 2025 года, в данном посте рассмотрен новый функционал, а так же устаревание и удаление некоторых функций

В отличие от поста 4-х летней давности об PHP 7.5 - PHP 8.5 реально был запланирован и есть следующая минорная версия после PHP 8.4.

Новое в PHP 8.5

Pipe-оператор |>

В PHP 8.5 добавлен новый оператор - так называемый оператор конвейера ( |> ) для объединения нескольких вызываемых объектов слева направо, принимая возвращаемое значение левого вызываемого объекта и передавая его направо, то есть вместо этого:

$input = 'Какая_то строка.';

$output = strtolower(
    str_replace(['.', '/', '…'], '',
        str_replace(' ', '-',
            trim($input)
        )
    )
);

и этого:

$input = 'Какая_то строка.';

$temp = trim($input);
$temp = str_replace(' ', '-', $temp);
$temp = str_replace(['.', '/', '…'], '', $temp);

$output = strtolower($temp);

можно будет писать:

$output = $input 
    |> trim(...)
    |> fn (string $string) => str_replace(' ', '-', $string)
    |> fn (string $string) => str_replace(['.', '/', '…'], '', $string)
    |> strtolower(...);

Clone v2 как функция

В PHP 8.5 языковая конструкция clone теперь работает как функция, с возможностью передать вторым аргументом значения для клонируемого объекта, для их переопределения:

class Foo {
 
    public function __construct(
        private readonly int $c = 1,
    ) {}
 
    public function just_clone() {
        return clone $this;
    }
 
    public function clone_with($newC) {
        return clone($this, ["c" => $newC]);
    }
}
 
$foo = new Foo();
// object(Foo)#2 (1) { ["c":"Foo":private]=> int(1) }
var_dump($foo->just_clone());
 
// object(Foo)#2 (1) { ["c":"Foo":private]=> int(5) }
var_dump($foo->clone_with(5));

Старый способ clone $object; так же работает:

$y = clone $x;
$y = clone($x);
$y = \clone($x);
$y = clone($x, []);
$y = clone($x, [
    "foo" => $foo,
    "bar" => $bar,
]);
$y = clone($x, $array);

Новая функция curl_multi_get_handles

Расширение Curl в PHP 8.5 добавляет новую функцию curl_multi_get_handles, которая возвращает массив объектов CurlHandle из объекта CurlMultiHandle. Без этой функции для извлечения объектов CurlHandle, добавленных в CurlMultiHandle, приложению пришлось бы поддерживать список объектов CurlHandle, желательно в WeakMap:

$cm = curl_multi_init();
curl_multi_get_handles($cm);
// пустой массив

$ch1 = curl_init('https://example.com/foo');
$ch2 = curl_init('https://example.com/bar');

curl_multi_add_handle($cm, $ch1);
curl_multi_add_handle($cm, $ch2);

curl_multi_get_handles($cm);
// [$ch1, $ch2]

Новая константа PHP_BUILD_DATE

В PHP 8.5 появится новая константа PHP_BUILD_DATE, которая показывает дату и время билда PHP, ранее эти данные были доступны только из функции phpinfo(), теперь же можно будет вывести так:

echo PHP_BUILD_DATE;
// Nov 22 2025 00:00:10

Новая константа PHP_BUILD_PROVIDER

В PHP 8.5 появилась новая глобальная константа PHP с именем PHP_BUILD_PROVIDER, которая содержит переменную окружения PHP_BUILD_PROVIDER, заданную во время сборки. Чтобы объявить константу PHP_BUILD_PROVIDER, установите переменную окружения с тем же именем перед вызовом скрипта ./configure:

./buildconf
 export PHP_BUILD_PROVIDER="Example Value"
 ./configure ...
 make
 make install

echo PHP_BUILD_PROVIDER;
// "Example Value"

Новые функции get_exception_handler и get_error_handler

До PHP 8.5 не было прямого способа получить текущие установленные обработчики ошибок и исключений с помощью set_error_handler и set_exception_handler. PHP 8.5 же добавляет функции: get_error_handler и get_exception_handler, которые закроют эту потребность:

/**
 * Returns the currently set error handler, or null if none is set.
 * @return callable|null
 */
function get_error_handler(): ?callable {}

/**
 * Returns the currently set exception handler, or null if none is set.
 * @return callable|null
 */
function get_exception_handler(): ?callable {}

Пример использования:

set_error_handler(fn() => null);
get_error_handler(); // object(Closure)#1 (3) {...}

set_error_handler(fn() => null);
get_error_handler(); // object(Closure)#1 (3) {...}

Новые функции array_first и array_last

До PHP 8.5, а если быть точнее в PHP 7.3 были добавлены функции array_key_first и array_key_last, для получения первого и последнего ключа в массиве, что интересно, извлечение значений, без изменения состояния массива, было добавлено только в PHP 8.5:

  • array_first - Извлечь первое значение из заданного массива, будет возвращен null если массив пуст.

  • array_last - Извлечь последнее значение из заданного массива, будет возвращен null если массив пуст.

Обратите внимание, что эти функции возвращают значение null если массив пуст. Однако null так же может быть допустимым значением массива.

Примеры использования:

array_first([1, 2, 3]); // 1
array_first([2, 3]); // 2
array_first(['a' => 2, 'b' => 1]); // 2
array_first([null, 2, 3]); // null
array_first([]); // null
array_first([$object, 2, 3]); // $object
array_first([1])); // 1
array_first([true]); // true

Новая директива INI max_memory_limit для установки максимума для memory_limit

Директива PHP INI memory_limit позволяет установить максимальный объём памяти, разрешённый для использования одним запросом. При превышении этого лимита выполнение останавливается. Но memory_limit - это директива INI_ALL, то есть её можно переопределить, используя ini_set('memory_limit', '200M');

Для более точного управления этим ограничением в PHP 8.5 появилась новая INI-директива max_memory_limit. При установке значения max_memory_limit - оно становится максимальным значением, которое разрешено установить memory_limit. Она установлена ​ по умолчанию значением -1, то есть это ограничение отключено по умолчанию. max_memory_limit - это INI_SYSTEM INI-директива, то есть её нельзя изменить во время выполнения:

max_memory_limit=256M
memory_limit=128M

ini_get('max_memory_limit'); // "256M";
ini_get('memory_limit'); // "128M";

ini_set('memory_limit', '200M'); // Разрешено
ini_set('memory_limit', '256M'); // Разрешено

ini_set('memory_limit', '300M'); // Запрещено

Warning: Failed to set memory_limit to 314572800 bytes. Setting to max_memory_limit instead (currently: 268435456 bytes) in ... on line ...

Атрибут #[NoDiscard] rfc

Данное RFC вводит в PHP 8.5 новый атрибут #[NoDiscard], позволяющий указать, что возвращаемое значение функции или метода "важно" и что отсутствие каких-либо действий с возвращаемым значением, вероятно, является ошибкой:

#[NoDiscard("you must use this return value, it's very important.")]
function foo(): string {
    return 'bar';
}

// Предупреждение: 
// Ожидается, что возвращаемое значение функции foo() будет использовано, 
// вы должны использовать это возвращаемое значение, это очень важно.
foo();

// Это нормально:
$string = foo();


Исключение по-прежнему можно обойти, используя новое приведение к типу void:

(void) foo();


Новое расширение URI

В PHP 8.5 представлено новое расширение URI - мощный парсер, соответствующий стандартам RFC 3986 и WHATWG URL. Теперь он входит в стандартную библиотеку PHP под именем URI, и доступен из "коробки":

use UriRfc3986Uri;

$url = new Uri('https://sergeymukhin.com/blog/chto-novogo-v-php-85');

echo $url->toString(); // https://sergeymukhin.com/blog/chto-novogo-v-php-85

echo $uri->getHost(); // sergeymukhin.com
echo $uri->getScheme(); // https
echo $uri->getPort(); // 443

Атрибуты констант

PHP 8.5 позволяет добавлять атрибуты к константам. Достать их можно будет с помощью ReflectionClassConstant::getAttributes() или ReflectionConstant::getAttributes(). Встроенный атрибут #[Deprecated]также работает с константами, поэтому вы можете отметить константы как устаревшие и вывести предупреждения:

#[MyAttribute]
const A = 1;

#[Deprecated(reason: 'Не использовать, лучше обратитесь к A')]
const B = 2; 

Следует отметить, что атрибуты нельзя применить для констант, определённых с помощью define(), а только объявленных с помощью const.

Улучшения замыкания

Замыкания и callable объекты первого класса теперь можно использовать в константных выражениях. На практике это означает, что вы сможете определять замыкания в атрибутах, что является невероятной новой возможностью:

#[SkipDiscovery(static function (Container $container): bool {
    return ! $container->get(Application::class) instanceof ConsoleApplication;
})]
final class BlogPostEventHandlers
{ /* … */ }

или так:

class Foo {
    // Static closure as a constant
    public const UPPER = static function (string $v): string { return strtoupper($v); };

    // First-class callable as a constant
    public const LOWER = 'strtolower'(...);
}

Поддержка трассировки стека для фатальных ошибок

В PHP 8.5 добавлен новый функционал, благодаря которому сообщения о фатальных ошибках содержат полную трассировку стека. Как это было до 8.5:

Fatal error: Allowed memory size of 2097152 bytes exhausted (tried to allocate 5242912 bytes) in ... on line ...

и как после:

Fatal error: Allowed memory size of 2097152 bytes exhausted (tried to allocate 5242912 bytes) in /var/www/test/php85/error_handlers.php on line 6
Stack trace:
#0 file.php(...): str_repeat('A', 5242880)
#1 file.php(...): my_function()
#2 {main}

Новая функция Левенштейна для группы графем

Функция grapheme_levenshtein() вычисляет расстояние Левенштейна с учетом Unicode, которое понимает группы графем (то, что пользователи воспринимают как символы), и позволяющая точно сравнивать строки, что идеально подходит для таких случаев как "café":

grapheme_levenshtein('café', 'cafe'); // 1

Устаревание магических методов __sleep() и __wakeup()

С PHP 8.5 будет постепенное выведение из употребления магических методов __sleep() и __wakeup(), в документации будут помечены как устаревшие, и вместо них будет предложено использовать __serialize()/__unserialize()

Асимметричная видимость для статических свойств

В RFC по асимметричной видимости PHP 8.4 намеренно не реализована поддержка асимметричной видимости для статических свойств из-за ожидаемой сложностью реализации и относительно небольшим количеством вариантов использования. PHP 8.5 позволит статическим свойствам в PHP так же иметь асимметричную видимость:

class Example
{
    public private(set) static string $classTitle = 'Example class';
 
    // Implicitly public-read, just like object properties.
    protected(set) static int $counter = 0;
 
    public static function changeName(string $name): void
    {
        // From private scope, so this is allowed.
        self::$classTitle = $name;
    }
}
 
print Example::$classTitle; // Можно
 
Example::$classTitle = 'Nope'; // Нельзя.

Расширение OPCache всегда загружаемое

В PHP 8.5 OPcache будет обязательной частью PHP. OPcache будет статически скомпилировано в PHP, как ext/date, ext/hash , ext/pcre, ext/random, ext/standard и другие.

Final свойств объявленных в конструкторе

В PHP 8.5 теперь есть возможность задать модификатор final для свойств заданных в конструкторе, т.е. раньше делали так:

class User {
    final public string $name;

    public function __construct(string $name) {
        $this->name = $name;
    }
}

а теперь можно так:

class User {
    public function __construct(
        final string $name
    ) {}
}

Новая опция INI --diff

Небольшое, но мощное дополнение CLI: php --ini=diff выводит только те настройки INI, которые отличаются от встроенных значений PHP по умолчанию, что отлично подходит для быстрой диагностики:

php --ini=diff

// Non-default INI settings:
// html_errors: "1" -> "0"
// implicit_flush: "0" -> "1"
// max_execution_time: "30" -> "0"

Поддержка атрибута #[Deprecated] в трейтах

Атрибут #[Deprecated] был представлен в PHP 8.4 и мог использоваться для выдачи предупреждений об устаревании при вызове функции (или метода класса) или при обращении к константе класса (или в случае перечисления). Начиная с версии 8.5, предупреждения об устаревании также могут выдаваться для трейта:

#[Deprecated]
trait DemoTrait {}
 
class DemoClass {
	use DemoTrait;
}
 
// Reports:
// Deprecated: Trait DemoTrait used by DemoClass is deprecated in %s on line %d
?>

Новая функция locale_is_right_to_left и метод Locale::isRightToLeft

Расширение Intl получило несколько улучшений. Новая функция locale_is_right_to_left() (и соответствующий метод Locale::isRightToLeft()) помогает определить, использует ли локаль письмо справа налево:

locale_is_right_to_left ( 'ru-RU' );   // false 
locale_is_right_to_left ( 'en-US' );   // false
locale_is_right_to_left ( 'ar-SA' );   // true 
locale_is_right_to_left ( 'he-IL' );   // true

Также появился новый класс IntlListFormatter для создания списков с учетом локали:

$formatter = new IntlListFormatter('en-US');
echo $formatter->format(['Paris', 'London', 'Moscow']);
// "Paris, London, and Moscow"

$formatter = new IntlListFormatter('ru-RU');
echo $formatter->format(['Париж', 'Лондон', 'Москва']);
// "Paris, London и Tokyo"

Улучшенный класс Directory

Теперь в PHP 8.5 класс Directory был изменён так, что теперь он ведёт себя как строгий, немодифицируемый ресурсный объект. Эти объекты ресурсов, как правило, нельзя создать с помощью new, сериализовать или клонировать, и, как правило, они ведут себя не как обычные классы.

То есть new Directory() использовать будет невозможно. Способ создания экземпляра класса - использование функции dir.

#[Override] можно применять к свойствам

Так же, как раньше можно было отмечать методы, переопределяющие реализацию родительского класса, теперь можно применять атрибут #[Override] к свойствам. Как и в случае с методами, PHP выдаст ошибку, если свойство фактически ничего не переопределяет:

class P {
    abstract public mixed $p { get; }
}
 
class C extends P {
    #[Override]
    public mixed $p;
}


trait T {
    #[Override]
    public mixed $p;
}
 
interface I {
    public mixed $p { get; }
}
 
class C implements I {
    use T;
}

class C {
    #[Override]
    public mixed $c; // Fatal error: C::$c has #[Override] attribute, but no matching parent property exists
}

Устаревшее в PHP 8.5

Все MHASH_*константы устарели

В PHP 8.1 все mhash функции были объявлены устаревшими: mhash, mhash_​count, mhash_​get_​block_​size, mhash_​get_​hash_​name, и mhash_​keygen_​s2k. Однако константы, которые эти функции принимали в качестве параметров, не были объявлены устаревшими. В PHP 8.5 все константы MHASH_* объявлены устаревшими вместе с существующим устареванием функций mhash:

mhash(MHASH_SHA1, 'test');

Constant MHASH_SHA1 is deprecated ....
Function mhash() is deprecated since 8.1 ...

Устарели альтернативные приведения скалярных типов (boolean|double|integer|binary)

Скалярное приведение типов в PHP допускает некоторые вариации. Например, можно привести переменную к целому числу, используя как (int) так и (integer), например:

$value = '42';

(integer) $value; //int(42)
(int) $value; //int(42)

Но те же альтернативные имена типов не поддерживаются в типах параметров, возвращаемых значений или свойств:

function test(integer $value): double {}

Warning: "double" will be interpreted as a class name. Did you mean "float"? Write "double" to suppress this warning in ... on line ...
Warning: "integer" will be interpreted as a class name. Did you mean "int"? Write "integer" to suppress this warning in ... on line ...

Из-за этой непоследовательности PHP 8.5 прекращает поддержку четырех альтернативных имен скалярных типов:

  • (integer)

  • (double)

  • (boolean)

  • (binary)

Рекомендуется заменить их на (int), (float), (bool), (string)

Удаленное из PHP 8.5

CLI/CGI: опция -z/--zend-extension

До версии PHP 8.5 в справке исполняемого файла PHP ( php --help/ php -h) отмечалось, что он поддерживает -z (--zend-extension) - возможность загрузки расширения Zend перед выполнением заданного файла или запуском интерпретатора:

php --help
Usage: php [options] [-f] <file> [--] [args...]
   php [options] -r <code> [--] [args...]
   php [options] [-B <begin_code>] -R <code> [-E <end_code>] [--] [args...]
   php [options] [-B <begin_code>] -F <file> [-E <end_code>] [--] [args...]
   php [options] -S <addr>:<port> [-t docroot] [router]
   php [options] -- [args...]
   php [options] -a

  -a               Run as interactive shell (requires readline extension)
...
  -z <file>        Load Zend extension <file>
...

В PHP 8.5 оба параметра -z и --zend-extension удалены, поскольку они не работают во всех версиях PHP. Альтернатива -d на основе опций совместима со всеми версиями PHP, включая PHP 8.5:

php -d zend_extension=xdebug.so script.php

Что думаешь?

Егор11.11.2025

Спасибо за обзор! Очень даже внятно!

Константин03.08.2025

|> - красиво) мне оч. нравится. Вот бы еще наконец созрели до обявления методов как у здоровых людей

function qwe {}
в
qwe {}

Тошнит от этого слова function и ->
мне не лень конечно (на самом деле лень) pf и tab наждать, но блин так некрасиво

А вообще обожаю php за грубость. Порой думаю, мол в следующий раз когда серв буду писать с 0 все на шарпе сделаю - строго, быстро, стильно (мою любимый язык по сути)... и каждый раз хватаю себя за руку. Думаю, Константин ты что курнул?, решил с этими типами возится? Опять те же грабли да? нафига тебе эти заморочки, тебе правда нужна скорость? Эта строгость? Иди проспись! ИМХО скорость кода это самое последнее что надо программисту в наше время. Часто никогда.
Так что иди и говнокоди дальше, забивая на все типы, все объявления и пр. как ты делал это всегда, а с шарпом в юните поиграешь или его огрызком TS во фронтенде.

P.S. Не знаю зачем все это написал. Прорвало на откровенность... Пожалею потом и удалю)

Сергей Мухин 04.08.2025

Спасибо за мысли и потраченное время!

У любого языка есть плюсы и минусы, без этого никуда, самое главное, когда язык развивается и не стоит на месте, чтобы не "умереть")

Сынок11.07.2025

что интересно, извлечение значений было добавлено только в PHP 8.5

а изучать PHP пробовали?

$array = [111, 222, 333];

echo current($array); // 111
echo reset($array); // 111
echo end($array); // 333

)))

Сергей Мухин 01.08.2025

Изучать пробовали, более детально об этом в моем телеграм-блоге https://t.me/flyphp/88

Плюс можно почитать много комментариев в канале Пыхе https://t.me/phpyh/720
Там я подробнее рассмотрел все варианты и чем каждый чреват.

mrFlyer03.07.2025

Шел 2025й год. ArrayFirst ))

PS Сергей, спасибо за ваши обзоры нововведений PHP. Слежу постоянно!

Сергей Мухин 01.08.2025

Спасибо вам) Сейчас не так много времени получается уделять, но стараюсь

MX30.05.2025

Статья в тему. Но пропущена важная вещь - новый оператор Pipe operator |>
Подробности: https://php.watch/versions/8.5

nnnnnnnnnn01.05.2025

Что, нихера не добавили получается?

Сергей Мухин 05.05.2025

Да нет, почему же, добавляют понемногу, до фича фриз еще много времени

Категории
  • PHP 68
  • Заметки 18
  • Безопасность 4
  • Флуд 2
  • Nginx 2
  • ИТ новости 2
  • Видео 1
  • Docker 1
  • Roadmap 1
  • Архитектура 0

Хочешь поддержать сайт?

Делаем из мухи слона

sergeymukhin.com

персональный блог о веб-разработке от Сергея Мухина. Блог был основан в 2018 году, и собирался уделять основное внимание последним тенденциям, учебным пособиям, а также советам и рекомендациям, позволяющим начинающим девелоперам встать быстрее на правильную дорогу веб разработки, но что-то пошло не так 😃

Релизы PHP 8.5

Дата Релиз
3 Июля 2025 Альфа 1
17 Июля 2025 Альфа 2
31 Июля 2025 Альфа 3 пропущена
31 Июля 2025 Альфа 4
12 Августа 2025 Feature freeze
14 Августа 2025 Бета 1
28 Августа 2025 Бета 2
11 Сентября 2025 Бета 3
25 Сентября 2025 RC 1
09 Октября 2025 RC 2
23 Октября 2025 RC 3
06 Ноября 2025 RC 4
20 Ноября 2025 GA

Что нового?