Что нового в 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 installecho 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 --helpUsage: 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
Спасибо за обзор! Очень даже внятно!