PHP 8.5: Оператор pipe (|>)
Что такое оператор pipe в PHP 8.5
Оператор конвейера pipe (|>) не добавляет новых возможностей в язык. Тем не менее, он может помочь «связать» несколько вызываемых объектов более наглядным способом, вместо того, чтобы вкладывать их друг в друга или использовать переменную для хранения возвращаемых значений и их передачи.
$result = "Hello World"
|> strtoupper(...)
|> str_shuffle(...)
|> trim(...);
// "LWHO LDLROE"В приведённом выше фрагменте кода используется новый оператор pipe (|>) для указания того, что возвращаемое значение предыдущего вызываемого объекта должно быть передано вызываемому объекту справа. Часть (...) рядом с именем функции делает её вызываемым объектом первого класса.
Следующие два фрагмента кода показывают, как достичь того же результата без использования оператора pipe:
Использование вложенных вызовов
$result = trim(str_shuffle(strtoupper("Hello World")));Использование переменной
$result = "Hello World";
$result = strtoupper($result);
$result = str_shuffle($result);
$result = trim($result);Хотя это может снизить ясность кода, оператор |> не обязательно должен находиться в новой строке. Следующий пример является полностью допустимым синтаксисом:
$result = strtoupper("Hello World") |> str_shuffle(...) |> trim(...);Использование оператора pipe
Оператор pipe передаёт возвращаемое значение левой функции правой функции. При компиляции скрипта PHP цепочки вызовов |> оптимизируются и приводят к появлению опкодов, аналогичных типичным вложенным вызовам.
Однако оператор конвейера pipe имеет ряд ограничений. Он подходит для случаев, когда все функции в цепочке требуют только один параметр, имеют возвращаемые значения и не принимают параметры по ссылке.
Оператор pipe принимает любой callable объект
Вызываемые объекты в цепочке операторов pipe могут быть любыми callable объектами. Например, можно смешивать пользовательские функции, встроенные функции, статические методы классов, лямбда-функции, стрелочные функции, классы, реализующие магический метод __invoke, и вызываемые объекты первого класса.
$result = "Hello World"
|> 'strtoupper'
|> str_shuffle(...)
|> fn($x) => trim($x)
|> function(string $x): string {return strtolower($x);}
|> new MyClass()
|> [MyClass::class, 'myStaticMethod']
|> new MyClass()->myInstanceMethod(...)
|> my_function(...);
echo $result;
function my_function(string $x): string {
return substr($x, 0, 10);
}
class MyClass {
public function __invoke(string $x): string {
return str_rot13($x);
}
public function myInstanceMethod(string $x): string {
return hash('sha256', $x);
}
public static function myStaticmethod(string $x): string {
return str_replace('E', 'O', $x);
}
}Кроме того, callable объект может быть выражением, возвращающим вызываемый объект:
$result = "Hello World"
|> 'strtoupper'
|> get_callable();
echo $result; // 787ec76dcafd20c1908eb0936a12f91edd105ab5cd7ecc2b1ae2032648345dff
function get_callable(): callable {
return fn($x) => hash('sha256', $x);
}Принимает только вызываемые объекты, требующие только первый параметр
Основным ограничением оператора pipe является то, что все вызываемые объекты в цепочке должны принимать только один обязательный параметр.
Для встроенных функций, если функция не принимает никаких параметров, она не может быть использована в цепочке преобразования. Для пользовательских PHP функций передача параметра функции, не принимающей никаких параметров, не вызывает ошибку и игнорируется без предупреждения.
При использовании оператора pipe возвращаемое значение предыдущего выражения или вызываемого объекта всегда передаётся в качестве первого параметра следующему вызываемому объекту. Изменить положение параметра невозможно.
Принудительное преобразование типов
Оператор pipe не изменяет способ принудительного преобразования типов в PHP при передаче значения следующему вызываемому объекту.
Когда strict_mode включён, он требует строгого соответствия типов.
declare(strict_types = 1);
$result = 1 |> strlen(...);
// TypeError: strlen(): Argument #1 ($string) must be of type string, int given in ...Вызываемые объекты с возвращаемым типом void
Функции/вызываемые объекты PHP с возвращаемым типом void могут использоваться в цепочке преобразования, но возвращаемое значение принудительно преобразуется в null. Возвращающая void функция обычно используется в качестве последнего callable объекта цепочки, а не в середине, поскольку остальная часть цепочки с этого момента будет получать только null.
Функции с параметрами по ссылке
Поскольку промежуточные значения оператора pipe не хранятся в типичной переменной, использование функций, ожидающих передачу параметра по ссылке, не допускается.
explode("-", 'a-b-c') |> array_pop(...);
//Error: array_pop(): Argument #1 ($array) could not be passed by reference inОднако из этого правила есть исключение: встроенные PHP функции могут объявлять функции с параметрами @prefer-ref. Передача прямого значения в эти функции не вызывает ошибку.
Начиная с PHP 8.5, в ядре PHP есть только две такие функции. Класс FFI из расширения FFI содержит несколько методов, поддерживающих эту функцию, но они вряд ли будут использоваться с оператором pipe.
explode("-", 'a-b-c') |> array_multisort(...);
// true['foo' => 'hello', 'bar' => 2] |> extract(...);
echo $foo; // helloВ приведённых выше примерах оператора pipe используются array_multisort и extract — единственные две функции в ядре PHP, использующие ссылки @prefer-ref.
Приоритет операторов
Вызываемые функции всегда вызываются слева направо, если их порядок не изменён с помощью () круглых скобок.
echo 10 + 6 |> dechex(...) |> hexdec(...);
// 16В приведённом выше примере оператора pipe сначала вычисляется 10 + 6, а затем значения передаются по цепочке.
Как и в случае с другими операторами в PHP, с помощью () круглых скобок можно явно задать порядок вычисления:
echo 10 + (6 |> dechex(...)) |> hexdec(...);
// 22При использовании операторов объединения с null (??) и тернарных операторов (?:) они также вычисляются слева направо:
echo 10 ?? 6 |> dechex(...) |> hexdec(...);
// 10
echo null ?? 6 |> dechex(...) |> hexdec(...);
// 6echo true ? 5 : 6 |> dechex(...) |> hexdec(...);
// 5Когда оператор конвейера pipe используется в качестве левого выражения тернарного оператора или оператора объединения с null, сначала выполняется оператор pipe:
echo 10 + 6 |> dechex(...) |> hexdec(...) === 16
? 'is sixteen'
: 'not sixteen';
// is sixteenОператоры равенства (== и ===) имеют более низкий приоритет и, следовательно, работают как ожидается:
10 + 6 |> dechex(...) |> hexdec(...) === 16
// true
16 = 10 + 6 |> dechex(...) |> hexdec(...)
// trueВлияние на обратную совместимость
Оператор pipe является новым синтаксисом и не совместим с PHP 8.4 и более старыми версиями.
Попытка запустить PHP-код, использующий оператор pipe, приведёт к ошибке парсинга в более старых версиях PHP:
Parse error: syntax error, unexpected token ">" in ...