Всё, что появилось в PHP 8.5
Нововведения в PHP 8.5 делятся на несколько ключевых направлений:
- Улучшение синтаксиса и читаемости: оператор
|>, методыarray_first()/array_last(). - Более строгий и безопасный код: атрибут
#[\NoDiscard],final-промоушен, улучшенный классDirectory. - Расширение системы атрибутов: атрибуты для констант, устаревание трейтов.
- Работа с данными и API: новый URL API, улучшения cURL, клонирование объектов с параметрами.
- Улучшения для отладки и конфигурации: трассировка фатальных ошибок, INI-опция
diff.
Самые важные изменения в PHP 8.5
Если у вас мало времени, вот что стоит знать в первую очередь:
- Оператор конвейера
|>— для цепочечных вызовов без промежуточных переменных. array_first()иarray_last()— наконец-то встроенные функции для работы с массивами.- Клонирование с изменением свойств —
clone($obj, ['prop' => $value])теперь работает даже сreadonly. - Атрибут
#[\NoDiscard]— предупреждает, если возвращаемое значение функции проигнорировано. - Трассировка фатальных ошибок — включается через
fatal_error_backtraces=1. - OPcache всегда с вами — расширение стало неотключаемой частью PHP.
Остальные улучшения — ниже в детальном разборе.
Оператор конвейера |>
В PHP 8.5 введён оператор pipe/конвейера (|>), позволяющий писать код в беcточечном стиле, ограничивающем использование необязательных промежуточных переменных.
Итак, следующий код…
$value = "hello world";
$result1 = function3($value);
$result2 = function2($result1);
$result = function1($result2);…можно переписать с помощью оператора конвейера так:
$value = "hello world";
$result = $value
|> function3(...)
|> function2(...)
|> function1(...);Оператор |> (pipe) берёт результат выражения слева и передаёт его в качестве первого (и часто единственного) аргумента в callable, указанный справа. Callable может быть функцией, методом, замыканием или callable-объектом первого класса (например, strlen(...)).
Вот более реальный пример:
$fullName = 'Fred Flintstone';
$result = $fullName
|> fn($x) => explode(' ', $x) // Создаёт массив отдельных слов
|> fn($x) => implode('_', $x) // Соединяет эти слова с помощью символа _
|> strtolower(...) // Преобразует всё в нижний регистр
;
// $result === 'fred_flintstone'Помимо синтаксических улучшений для цепочек вызовов, PHP 8.5 также расширяет стандартную библиотеку удобными функциями для работы с массивами.
Новые методы массива
Работать с массивами становится проще: в PHP 8.5 добавлены долгожданные функции array_first() и array_last(), упрощающие получение первого и последнего элементов массива без изменения внутреннего указателя и использования ключей.
Пример использования с ассоциативными и индексированными массивами:
$array = [1, 2, 3, 4, 5];
$first = array_first($array); // 1
$last = array_last($array); // 5
$array = ['a' => 1, 'b' => 2, 'c' => 3];
$first = array_first($array); // 1
$last = array_last($array); // 3Для пустых массивов обе функции возвращают null. Важно отличать это от случая, когда первый или последний элемент массива содержит значение null. Для массива [null, 1] функция array_first() вернёт null как валидный элемент, а не как признак отсутствия элементов.
$first = array_first([]); // null
$last = array_last([]); // nullАтрибут #[\NoDiscard]
В PHP 8.5 добавлен новый атрибут #[\NoDiscard], позволяющий разработчикам указывать, что возвращаемое значение функции или метода не должно игнорироваться. Если функция или метод, помеченные атрибутом #[\NoDiscard], вызываются без использования их возвращаемого значения, PHP сгенерирует предупреждение (E_WARNING). Это заставит инструменты статического анализа и CI-системы обратить внимание на потенциальную ошибку.
#[\NoDiscard("as the operation result is important")]
function performOperation(): int {
// Выполнение какой-либо операции
return 1; // 1 — в случае успеха, 0 — в случае неудачи
}
// Вызов функции без использования возвращаемого значения
// Warning: The return value of function performOperation() is expected to be consumed, as the operation result is important in test.php on line 10
performOperation();
// Вызов функции и использование возвращаемого значения
// Это действие не вызовет предупреждение.
$status = performOperation();Это эффективный механизм, гарантирующий, что разработчики случайно не проигнорируют важные возвращаемые значения, особенно в тех случаях, когда возвращаемое значение имеет решающее значение для успеха или неудачи операции.
Атрибуты в PHP 8.5 становятся ещё более универсальными. Помимо контроля за возвращаемыми значениями через #[\NoDiscard], теперь их можно применять и к константам.
Атрибуты констант
Как вы, возможно, знаете, атрибуты — способ добавления метаданных к элементам кода (таким как классы, методы, свойства и т. д.) с помощью специального синтаксиса. Например:
#[MyAttribute]
class MyClass {}Теперь, с PHP 8.5, помимо таких вещей, как классы, методы и константы классов, можно также добавлять атрибуты к глобальным (неклассовым) константам.
Теперь можно добавлять атрибуты к неклассовым константам периода компиляции, например:
#[MyAttribute]
const MY_CONST = 42;Следует иметь в виду, что это не работает для констант, определённых с помощью define(), а только для объявленных с помощью const.
Теперь можно использовать ReflectionConstant::getAttributes() для чтения атрибутов констант в runtime. Кроме того, атрибут #[\Deprecated] обновлён, чтобы позволить ориентироваться на константы; при применении константа помечается как CONST_DEPRECATED.
#[Deprecated(reason: 'Use NEW_API_URL instead. This will be removed in v2.0.')]
const OLD_API_URL = 'https://old-api.example.com';
const NEW_API_URL = 'https://api.example.com';
// Пример использования
function fetchData() {
// IDE и статические анализаторы теперь могут предупреждать об использовании OLD_API_URL
$data = file_get_contents(OLD_API_URL);
return $data;
}Новый URL/URI API
В PHP 8.5 введён стандартизированный API, позволяющий анализировать, нормализовать, изменять и сравнивать URI/URL в соответствии с соответствующими спецификациями.
Для анализа и работы с адресами вводятся два иммутабельных класса.
- Uri\Rfc3986\Uri: соответствует RFC 3986 (generic URI, строгая валидация, опциональная нормализация, процентное декодирование, где это безопасно)
- Uri\WhatWg\Url: соответствует WHATWG URL (поведение браузера, хосты IDNA/Unicode, преобразования во время анализа, soft/hard ошибки)
Далее приведено как их использовать для анализа и безопасной проверки URL.
use Uri\Rfc3986\Uri;
use Uri\WhatWg\Url;
$rfc = Uri::parse("https://example.com/path?x=1"); // Uri или null
$whatwg = Url::parse("https://example.com/path?x=1"); // Url или null
$bad = Uri::parse("invalid uri"); // null (ошибка strict)
$errors = [];
$bad2 = Url::parse(" invalid url", null, $errors); // null с информацией о soft/hard ошибках// Нормализация и сравнение
$url1 = Url::parse("https://example.com/");
$url2 = Url::parse("https://EXAMPLE.COM");
var_dump($url1?->equals($url2)); // bool(true) - учитывается нормализацияПродвижение final свойства
class Example {
public function __construct(
final string $id
) {}
}- Свойство
$idтеперь является как продвинутым (созданным и инициализированным через конструктор), так иfinal(не может быть переопределено). - Ключевое слово
finalможно использовать как самостоятельный модификатор в продвижении свойств. В этом случае свойство получит видимость по умолчанию для промоушена (которая определяется контекстом, обычноpublic). Такжеfinalможно комбинировать с явным модификатором видимости:final public,final privateилиfinal protected.
Это делает синтаксис продвижения свойств конструктора более последовательным, и уменьшает количество шаблонного кода, который необходимо писать.
Улучшенный класс Directory
В PHP 8.5 класс Directory изменён так, что теперь он ведёт себя как строгий, немодифицируемый ресурсный объект. По сути, класс Directory в PHP используется для представления дескрипторов каталогов, обычно создаваемых функцией dir(). Исторически он вёл себя как обычный класс, но это могло приводить к ошибкам и неправильному использованию (например, созданию недопустимых объектов Directory с помощью new Directory()).
Решение: В PHP 8.5 класс Directory преобразован в настоящий ресурсный объект
(или непрозрачный объект
). Это означает:
- Конечный класс: Нельзя расширять (наследовать) класс
Directory. - Отсутствие прямого создания экземпляров: Нельзя создать объект
Directoryс помощьюnew Directory(). Только функцияdir()может создавать валидные экземпляры. - Отсутствие клонирования: Нельзя клонировать объект
Directory. - Отсутствие сериализации: Нельзя сериализовать или десериализовать объекты
Directory. - Отсутствие динамических свойств: Нельзя добавлять новые свойства к объектам
Directoryво время выполнения.
Теперь, при попытке создать объект Directory с помощью new Directory(), будет выброшена ошибка:
$dir = new Directory(); // Выбрасывает ошибку
$dir = dir('/tmp'); // Корректный способ создания объекта DirectoryТрассировка фатальных ошибок
PHP 8.5 предоставляет возможность добавлять трассировку стека к сообщениям о фатальных ошибках через новую INI-директиву fatal_error_backtraces, что значительно упростит отладку в сложных случаях.
До PHP 8.5, когда происходит фатальная ошибка (например, истечение времени выполнения или памяти), сообщение об ошибке не содержит трассировку (список вызовов функций, которые привели к ошибке). Без трассировки трудно понять, что вызвало ошибку, особенно в больших или сложных кодовых базах.
Что изменилось: В PHP 8.5 появилась возможность включить детальную трассировку для фатальных ошибок через новую INI-директиву fatal_error_backtraces.
Когда эта опция включена (а она может быть включена по умолчанию), PHP будет выводить трассировку стека для фатальных ошибок. Трассировка стека показывает последовательность вызовов функций, приведших к ошибке, аналогично тому, что вы видите при исключениях.
Так выглядела трассировка ранее:
Fatal error: Maximum execution time of 1 second exceeded in example.php on line 7После включения новой опции INI она будет выглядеть следующим образом:
Fatal error: Maximum execution time of 1 second exceeded in example.php on line 6
Stack trace:
#0 example.php(6): usleep(100000)
#1 example.php(7): recurse()
#2 example.php(7): recurse()
...
#11 {main}Ключевые моменты о новой трассировке ошибок:
- Чтобы избежать проблем с производительностью и памятью, трассировка выполняется только для фатальных ошибок (а не для предупреждений или уведомлений).
- Трассировка учитывает настройки конфиденциальности (например, атрибуты
SensitiveParameter). - Трассировка также доступна программно через
error_get_last()в функциях завершения работы.
Улучшения постоянных дескрипторов cURL
PHP 8.5 улучшает способ управления постоянными дескрипторами cURL, делая их более безопасными и простыми в использовании.
По сути, ресурсные дескрипторы cURL позволяют нескольким запросам cURL совместно использовать данные (например, кэш DNS) для повышения эффективности. Недавно в PHP была добавлена поддержка постоянных дескрипторов cURL, которые можно повторно использовать в нескольких PHP запросах. Первоначальная реализация имела некоторые риски и проблемы с удобством использования, особенно в отношении совместного использования cookie-файлов и управления постоянными идентификаторами.
PHP 8.5 вводит новую функцию curl_share_init_persistent(), создающую постоянный дескриптор cURL. Эта функция более безопасна и последовательна, чем предыдущая curl_share_init(). Больше не нужно предоставлять настраиваемый постоянный идентификатор; PHP управляет этим автоматически на основе переданных вами параметров. Если повторно вызвать функцию с теми же параметрами, вы получите тот же ресурсный дескриптор (он будет повторно использован).
$options = [CURL_LOCK_DATA_DNS];
$shareHandle = curl_share_init_persistent($options);
//Использование $shareHandle с CURLOPT_SHARE в запросах cURL
curl_setopt($ch, CURLOPT_SHARE, $shareHandle);Это улучшение важно, поскольку предотвращает случайную передачу конфиденциальных cookie-файлов. Кроме того, разработчикам не нужно управлять постоянными идентификаторами или беспокоиться о несоответствии ресурсных дескрипторов.
Callable объекты первого класса в константных выражениях
PHP 8.5 позволит использовать callable объекты первого класса (FCC) в константных выражениях PHP.
Как вы, возможно, знаете, callable объекты первого класса — это лаконичный способ ссылки на функции или статические методы в качестве вызываемых объектов с помощью синтаксиса … . Например:
// $callable теперь callable strlen()
$callable = strlen(...);
// callable MyClass::myMethod()
$callable = MyClass::myMethod(...);Раньше нельзя было использовать FCC внутри константных выражений (таких как значения свойств по умолчанию, аргументы атрибутов или определения констант). В PHP 8.5 это изменилось, и теперь можно использовать FCC в константных выражениях.
const MY_CALLABLE = strlen(...);
#[Attr(self::myMethod(...))]
class C {}Это делает использование FCC более последовательным в PHP и упрощает определение повторно используемых callable объектов в качестве констант, их использование в атрибутах или в качестве значений по умолчанию, что делает код более выразительным и DRY.
Логичным развитием поддержки callable в константных выражениях стала возможность использовать и статические замыкания. Это открывает новые возможности для использования замыканий в константных выражениях.
Использование замыканий в константных выражениях
PHP 8.5 позволит использовать статические замыкания (анонимные функции) в константных выражениях PHP. Чтобы понять, что изменилось, давайте сначала разберёмся, что такое константные выражения.
Константное выражение в PHP — это значение, которое должно быть полностью определено во время компиляции. Примеры:
- Значения по умолчанию для параметров функций.
- Аргументы атрибутов.
- Константы классов.
- Значения по умолчанию свойств.
До PHP 8.5 в константах нельзя было использовать замыкания (анонимные функции). В PHP 8.5 это изменилось. Теперь можно делать что-то вроде этого.
function my_array_filter(
array $array,
Closure $callback = static function ($item) { return !empty($item); }
) {
// ...
}Или используйте замыкания в качестве аргументов атрибутов, значений свойств по умолчанию или констант классов.
Существует несколько ограничений:
- Должно быть статическим: замыкание не может использовать
$thisили захватывать переменные из окружающей области видимости (нельзя использоватьuse($foo)и стрелочные функции). - Запрет на захват переменных: допускаются только чистые статические замыкания, поскольку константные выражения не могут зависеть от runtime-значений.
- Проверка во время компиляции: PHP выдаст ошибку, если попытаться использовать нестатическое замыкание или замыкание, захватывающее переменные.
Возможность использовать замыкания в константных выражениях позволяет писать более чистый код, поскольку можно предоставлять стандартные обратные вызовы или валидаторы непосредственно в сигнатурах функций, атрибутах или константах.
Кроме того, библиотеки могут использовать замыкания в атрибутах для таких целей, как валидация, форматирование или генерация наборов тестов, как показано ниже.
final class Locale
{
#[Validator\Custom(static function (string $languageCode): bool {
return preg_match('/^[a-z][a-z]$/', $languageCode);
})]
public string $languageCode;
}final class CalculatorTest
{
#[Test\CaseGenerator(static function (): iterable {
for ($i = -10; $i <= 10; $i++) {
yield [$i, $i, 0];
yield [$i, 0, $i];
yield [0, $i, ($i * -1)];
}
})]
public function testSubtraction(int $minuend, int $subtrahend, int $result)
{
\assert(Calculator::subtract($minuend, $subtrahend) === $result);
}
}final class LogEntry
{
public string $message;
#[Serialize\Custom(static function (string $severity): string {
return \strtoupper($severity);
})]
public string $severity;
}Это улучшение повышает выразительность и лаконичность кода.
Функции обработки ошибок и исключений
В PHP 8.5 добавлены функции get_error_handler() и get_exception_handler(), позволяющие напрямую получать текущие обработчики ошибок и исключений.
В PHP можно настроить собственные обработчики ошибок и исключений с помощью функций set_error_handler() и set_exception_handler(). Однако не было прямого способа проверить, какой обработчик используется в данный момент.
Разработчикам приходилось использовать обходной путь: устанавливать новый обработчик, сохранять старый, а затем восстанавливать его, что было неудобным и чреватым ошибками процессом.
В PHP 8.5 добавлены две новые функции:
get_error_handler(): ?callableget_exception_handler(): ?callable
Эти функции возвращают текущий зарегистрированный обработчик ошибок или исключений, либо null, если ни один из них не установлен.
set_error_handler(null);
get_error_handler(); // null
$handler = [$this, 'error_handler'];
set_error_handler($handler);
get_error_handler() === $handler; // true
$new_handler = $this->error_handler(...);
$old_handler = set_error_handler($new_handler);
get_error_handler() === $new_handler; // true
restore_error_handler();
get_error_handler() === $old_handler; // trueКак можно догадаться, это делает процесс настройки обработчиков надёжным, поскольку вы получаете именно тот обработчик, который настроили, что упрощает отладку и управление обработчиками.
Это особенно полезно для библиотек, которым необходимо временно подменять глобальные обработчики.
Асимметричная видимость для статических свойств
PHP 8.5 позволяет статическим свойствам в PHP иметь асимметричную видимость, то есть их доступ для чтения и записи может иметь разные уровни видимости.
В качестве примера можно привести асимметричную видимость, введённую в PHP 8.4, позволяющую устанавливать разные уровни доступа для чтения и записи свойства. Например, свойство может быть public для чтения, но private или protected для записи.
Ранее асимметричная видимость была доступна только для свойств объектов (экземпляров). С PHP 8.5 её можно использовать и для статических свойств.
class Example
{
public private(set) static string $classTitle = 'Example class';
// Все могут прочитать Example::$classTitle, но изменить его может только сам класс.
protected(set) static int $counter = 0;
// Любой может прочитать Example::$counter, но только класс или дочерние классы могут его изменить.
public static function changeName(string $name): void
{
self::$classTitle = $name; // Разрешено (внутри класса)
}
}
echo Example::$classTitle; // Разрешено (public чтение)
Example::$classTitle = 'Nope'; // Запрещено (private запись)Это делает функцию асимметричной видимости более последовательной во всем языке.
Новая функция Левенштейна для группы графем
В PHP 8.5 добавлена новая функция grapheme_levenshtein(), позволяющая точно сравнивать строки по группам графем (символам, воспринимаемым пользователем), а не по байтам или кодовым точкам.
По сути, расстояние Левенштейна измеряет, сколько односимвольных изменений (вставок, удалений, замен) необходимо для преобразования одной строки в другую. Существующие в PHP функции levenshtein() и mb_levenshtein() работают с байтами или кодовыми точками Unicode, что может давать неверные результаты для сложных символов Unicode (таких как эмодзи или буквы с диакритическими знаками).
Группа графем — это то, что пользователь видит как один символ, даже если он состоит из нескольких кодовых точек Unicode (например, «é» может быть одной кодовой точкой или «e» плюс комбинируемый акцент).
Чтобы это учесть, в PHP 8.5 введена функция grapheme_levenshtein(), вычисляющая расстояние Левенштейна на основе групп графем. Это означает, что она рассматривает визуально идентичные символы как одинаковые, даже если их базовое кодирование различается.
var_dump(
grapheme_levenshtein(
"\u{0065}\u{0301}",
"\u{00e9}"
)
); // Результат: 0Здесь обе строки отображаются пользователю как «é», но кодируются по-разному. Новая функция правильно распознает их как идентичные.
Новая опция INI diff
В PHP 8.5 добавлена опция INI diff, отображающая все настройки INI, отличающиеся от встроенных настроек PHP по умолчанию.
php --ini=diffЭта команда выводит список всех директив конфигурации INI, изменённых по сравнению с их встроенными значениями по умолчанию. Это быстрый способ увидеть, какие настройки были настроены в вашей среде, что особенно удобно для отладки, устранения неполадок или обмена информацией о различиях в конфигурации.
Non-default INI settings:
memory_limit: "128M" -> "512M"
display_errors: "1" -> ""
date.timezone: "UTC" -> "Europe/Amsterdam"Здесь левое значение является значением по умолчанию, а правое — текущей настройкой. Кроме того, отображаются только настройки, отличающиеся от значений по умолчанию.
Это позволяет быстро обнаружить изменения в конфигурации, влияющие на поведение приложения, и легко сравнивать различные среды (dev, stage, prod).
Также это позволяет увидеть, что изменилось в контейнерных или автоматизированных настройках.
Клонирование объектов со свойствами
Клонирование объектов с readonly-свойствами стало проще благодаря новому синтаксису. В PHP 8.5 оператор clone получил новую функциональность: теперь он может принимать второй аргумент с новыми значениями свойств.
В PHP существуют readonly свойства и клонирование объектов, но они не очень хорошо работают вместе. Обычно используется шаблон с иммутабельными объектами и методами, такими как withProperty(), возвращающими новый объект с одним изменённым свойством. С readonly свойствами сложно создать новый объект с одним изменённым свойством, потому что readonly свойства нельзя изменять после создания.
Для смягчения этой проблемы в PHP 8.5 clone работает как функция, принимающая второй аргумент: массив свойств, подлежащих изменению в новом объекте.
clone($object, $withProperties)Например:
$newObj = clone($oldObj, ['property' => $newValue]);Это создаст копию $oldObj, но со свойством, установленным в $newValue в клоне, даже если оно readonly.
Новую функцию clone() можно использовать следующим образом:
clone $obj;(старый способ, по-прежнему работает)clone($obj);(новый стиль функции)clone($obj, ['foo' => 42]);(клонирование и изменение свойств)
Вы также можете использовать clone как callable, например, в array_map('clone', $arrayOfObjects);. Правила видимости свойств соблюдаются: можно изменять только те свойства, к которым есть доступ.
Следует иметь в виду, что магический метод __clone() вызывается до того, как будут установлены новые свойства.
Ещё один пример использования нового метода клонирования объекта и изменения его свойств.
class Foo {
public function __construct(
private readonly int $c = 1,
) {}
public function clone_with($newC) {
return clone($this, ["c" => $newC]);
}
}
$x = new Foo();
$y = $x->clone_with(5);
// $y->c 5Так же можно использовать его как callable.
$objects = [new stdClass, new stdClass];
$clones = array_map('clone', $objects);
foreach ($clones as $clone) {
// Работаем с каждым клоном
}Клонирование с одновременным изменением readonly-свойства:
class Config {
public function __construct(public readonly string $env) {}
}
$prod = new Config('production');
$dev = clone($prod, ['env' => 'development']); // Создаёт клон с новым значением 'env'.В целом, новый метод clone значительно упрощает работу с иммутабельными объектами и readonly свойствами. Также сокращается количество шаблонного кода для создания модифицированных копий объектов.
Всегда загруженное расширение OPCache
OPcache — встроенное расширение PHP, ускоряющее работу PHP за счёт кэширования скомпилированного байт-кода скрипта в памяти. Оно является частью PHP с версии 5.5, но до сих пор было опциональным, то есть PHP можно было запускать и без него.
Запуск PHP без OPcache считается ошибкой для продакшена, но всё ещё возможен (особенно в Docker или пользовательских сборках). Поддержка PHP с OPcache и без него означает, что разработчики должны поддерживать два разных пути кода, что может привести к возникновению багов и затрудняет поддержку PHP.
PHP 8.5 интегрирует OPcache в ядро, делая его неотключаемым компонентом сборки. Это означает, что расширение будет присутствовать в любой сборке PHP 8.5, однако его функциональность по-прежнему управляется INI-настройками (opcache.enable, opcache.enable_cli). Запустить PHP без возможности задействовать OPcache больше нельзя, но сам кеш можно отключать.
Как разработчику, вам не нужно делать ничего особенного. Начиная с PHP 8.5, нельзя запускать PHP без загруженного OPcache, но по-прежнему можно контролировать поведение OPcache с помощью настроек конфигурации. Однако никаких изменений в использовании OPcache в вашем коде не произойдёт.
Новый флаг расширения filter для генерации исключений при ошибке валидации
PHP 8.5 улучшает PHP расширение filter, используемое для проверки и очистки входящих данных (например, для валидации адреса электронной почты). В настоящее время, если валидация не проходит, функции фильтра возвращают false или null, и разработчики должны вручную проверять эти значения и при необходимости генерировать исключения.
Введён новый флаг: FILTER_THROW_ON_FAILURE. При его использовании функции расширения filter в случае неудачной валидации выбрасывают исключение типа \Filter\FilterFailedException (наследник \ValueError) вместо возврата false или null
Так обычно проверяется адрес электронной почты без использования нового флага:
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
return false;
}Как видите, вам необходимо вручную проверять возвращаемое значение и обрабатывать ошибку.
С помощью нового флага FILTER_THROW_ON_FAILURE задача упрощается:
try {
filter_var($email, FILTER_VALIDATE_EMAIL, FILTER_THROW_ON_FAILURE);
return true;
} catch (\Filter\FilterFailedException $e) {
return false;
}В данном случае, если адрес электронной почты недопустим, генерируется исключение FilterFailedException, которое можно перехватить и обработать. Это делает код более чистым и снижает вероятность забыть проверить возвращаемое значение.
Следует помнить, что нельзя использовать FILTER_THROW_ON_FAILURE вместе с FILTER_NULL_ON_FAILURE, так как это приведёт к возникновению ошибки ValueError.
Положение об отметке трейтов как устаревших
PHP 8.5 позволяет пометить трейты как устаревшие с помощью атрибута #[\Deprecated], предупреждая разработчиков при их использовании.
Если класс использует этот трейт, PHP выдаст предупреждение об устаревании. Это поможет разработчикам узнать, что трейт больше не следует использовать, и даст им время обновить код до того, как трейт будет удалён.
Пример, как пометить трейт как устаревший:
#[\Deprecated]
trait DemoTrait {}
class DemoClass {
use DemoTrait;
}
// Output:
// Deprecated: Trait DemoTrait used by DemoClass
// is deprecated in [file] on line [number]Как видите, когда DemoClass использует устаревший DemoTrait, PHP выдаёт предупреждение, указывающее, что этот трейт устарел.
Важно отметить, что предупреждение отображается только в том случае, если класс напрямую использует устаревший трейт. Если родительский класс использует этот трейт, дочерние классы не получают дополнительных предупреждений, если они не используют этот трейт напрямую.
Добавление локали для нечувствительных к регистру функций графем
PHP 8.5 улучшает несколько функций строк графем, добавляя параметр $locale. Функции графем работают с воспринимаемыми пользователем символами ( графемами), важными для правильной обработки Unicode текста.
Ранее эти функции не учитывали локаль, то есть обрабатывали весь текст одинаково, независимо от языка или региона. Однако в некоторых языках существуют специальные правила сопоставления, не учитывающие регистр. Например, турецкая буква «İ» (заглавная точечная «I») обрабатывается иначе, чем в английском языке.
Пример работы нового параметра $locale с grapheme_stripos():
var_dump(grapheme_stripos("i", "\u{0130}", 0, "tr_TR")); // Возвращает 0 (совпадает в турецком языке)
var_dump(grapheme_stripos("i", "\u{0130}", 0, "en_US")); // Возвращает false (не совпадает в английском языке)Следующие функции стали принимать параметр $locale:
grapheme_strposgrapheme_striposgrapheme_strrposgrapheme_strriposgrapheme_substrgrapheme_strstrgrapheme_stristrgrapheme_levenshtein
Кроме того, можно указать «силу» (степень строгости сравнения символов) с помощью строки locale, что особенно полезно для CJK (Chinese, Japanese, Korean) символов.
$nabe = '邊';
$nabe_E0101 = "邊\u{E0101}";
var_dump(
grapheme_levenshtein(
$nabe,
$nabe_E0101
)
); // Результат: 0
var_dump(
grapheme_levenshtein(
$nabe,
$nabe_E0101,
locale: "ja_JP-u-ks-identic"
)
); //Результат: 1Другие значимые улучшения:
Помимо новых функций и синтаксиса, PHP 8.5 вносит ряд важных изменений в устаревшие механизмы и улучшает обработку локалей.
Мягкое устаревание магических методов __sleep() и __wakeup()
В PHP 8.5 магические методы __sleep() и __wakeup() помечены как плавно устаревшие
(soft-deprecated). Это означает, что они продолжают работать без каких-либо предупреждений времени выполнения, но официальная документация теперь будет указывать на их устаревание в пользу __serialize()/__unserialize(). Фактическое удаление или генерация предупреждений E_DEPRECATED может быть рассмотрена в будущих мажорных версиях PHP.
Пример:
class User {
public function __construct(
public readonly string $id,
public readonly string $email,
public readonly DateTime $createdAt
) {}
public function __sleep(): array {
return ['id', 'email', 'createdAt'];
}
public function __wakeup(): void {
$this->validate();
}
private function validate(): void {
if (!filter_var($this->email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Invalid email');
}
}
}После миграции на __serialize()/__unserialize():
public function __serialize(): array {
return [
'id' => $this->id,
'email' => $this->email,
'createdAt' => $this->createdAt,
];
}
public function __unserialize(array $data): void {
$this->validateData($data);
$this->id = $data['id'];
$this->email = $data['email'];
$this->createdAt = $data['createdAt'];
}Авторы этого RFC утверждают, что жёсткое прекращение поддержки не учитывает сложность миграции, особенно в случае больших классов, изменения имён приватных свойств, совместимости сохранённых данных и наследования. Они демонстрируют, что миграция часто добавляет шаблонный код, создаёт риск поломки и даёт мало преимуществ в простых случаях.
Планируется обновить документацию в пользу нового API, избежать принудительной переделки кода сейчас и рассмотреть возможность реального прекращения поддержки позже, когда пути миграции станут более ясными.
Итак, суть в том, что, поскольку это плавное прекращение поддержки, __sleep() и __wakeup() продолжают работать. Изменение в документации: теперь PHP рекомендует в дальнейшем использовать __serialize()/__unserialize(). Можно оставить существующий код без изменений и мигрировать в удобный для вас момент.
Итог:
PHP 8.5 делает акцент на эргономике, безопасности и выразительности. Основные усилия направлены на:
- Синтаксический сахар (
|>,array_first/array_last), сокращающий шаблонный код. - Контроль за корректностью (
#[\NoDiscard],final-промоушен, улучшенныйDirectory). - Расширение возможностей атрибутов и выражений (константы, callable, замыкания).
Вместе эти изменения помогают писать более плотный, надёжный и простой в поддержке код, последовательно устраняя исторические недочёты языка.