Всё, что появится в PHP 8.5

Как и каждый год, в этом году выйдет новая версия PHP, а именно PHP 8.5. Это минорная версия в линейке PHP 8, и она выйдет в конце этого года. Давайте поговорим обо всём, что было добавлено в PHP 8.5 на данный момент.

Оператор конвейера |>

В PHP 8.5 будет введён оператор pipe/конвейера (|>), позволяющий писать код в безточечном стиле, ограничивающем использование необязательных промежуточных переменных.

Итак, следующий код…

$value = "hello world";
$result1 = function3($value);
$result2 = function2($result1);
$result = function1($result2);

…можно переписать с помощью оператора конвейера так:

$value = "hello world";

$result = $value
|> function3(...)
|> function2(...)
|> function1(...);

Оператор |>, или "pipe", принимает вызываемый объект с одним параметром справа и передаёт ему значение с левой стороны, вычисляя результат вызываемого объекта. Конвейер (|>) вычисляется слева направо, передавая значение (или результат выражения) слева в качестве первого и единственного параметра вызываемому объекту справа.

Вот более реальный пример:

$fullName = 'Fred Flintstone';

$result = $fullName
|> fn($x) => explode(' ', $x) // Создаёт массив отдельных слов
|> fn($x) => implode('_', $x) // Соединяет эти слова с помощью символа _
|> strtolower(...) // Преобразует всё в нижний регистр
;

// $result === 'fred_flintstone'

Новые методы массива

В 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 вместо выброса ошибки.

$first = array_first([]); // null
$last = array_last([]); // null

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

В PHP 8.5 добавлен новый атрибут #[\NoDiscard], позволяющий разработчикам указывать, что возвращаемое значение функции или метода не должно игнорироваться. Если разработчик вызывает функцию, помеченную #[\NoDiscard], и не использует возвращаемое значение, PHP выдаст предупреждение.

#[\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();

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

Продвижение final свойства

class Example {
public function __construct(
final string $id
) {}
}

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

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

Как вы, возможно, знаете, атрибуты — это способ добавления метаданных к элементам кода (таким как классы, методы, свойства и т. д.) с помощью специального синтаксиса. Например:

#[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;
}

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

В PHP 8.5 класс Directory изменён так, что теперь он ведёт себя как строгий, немодифицируемый ресурсный объект. По сути, класс Directory в PHP используется для представления дескрипторов каталогов, обычно создаваемых функцией dir(). Исторически он вёл себя как обычный класс, но это могло приводить к ошибкам и неправильному использованию (например, созданию недопустимых объектов Directory с помощью new Directory()).

Чтобы исправить это, в PHP 8.5 класс Directory ведёт себя как настоящий "ресурсный объект" (иногда называемый "непрозрачным объектом").

Это означает:

Теперь, при попытке создать объект Directory с помощью new Directory(), будет выброшена ошибка:

$dir = new Directory(); // Выбрасывает ошибку
$dir = dir('/tmp'); // Корректный способ создания объекта Directory

Трассировка фатальных ошибок

PHP 8.5 появится автоматическая трассировка стека к сообщениям о фатальных ошибках PHP, что значительно упростит отладку.

Сейчас в PHP, когда происходит фатальная ошибка (например, истечение времени выполнения или памяти), сообщение об ошибке не содержит трассировку (список вызовов функций, которые привели к ошибке). Без трассировки трудно понять, что вызвало ошибку, особенно в больших или сложных кодовых базах.

Когда эта опция включена (а она может быть включена по умолчанию), 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}

Ключевые моменты о новой трассировке ошибок:

Улучшения постоянных дескрипторов 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.

Использование замыканий в константных выражениях

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); }
) {
// ...
}

Или используйте замыкания в качестве аргументов атрибутов, значений свойств по умолчанию или констант классов.

Существует несколько ограничений:

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

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

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 добавлены две новые функции:

Эти функции возвращают текущий зарегистрированный обработчик ошибок или исключений, либо 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

Как можно догадаться, это делает процесс настройки обработчиков надёжным, поскольку вы получаете именно тот обработчик, который настроили, что упрощает отладку и управление обработчиками.

Кроме того, фреймворки и библиотеки теперь могут безопасно проверять или восстанавливать обработчики без побочных эффектов.

Новая опция 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).

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

Комментарии


Дополнительные материалы

Предыдущая Статья

Понимание рекурсии в JavaScript