こんにちは、配配メール開発チームのid:takaramです。
毎年11月ごろに新しいメジャーバージョンがリリースされるPHPですが、今年2025年11月(予定)にリリースのPHP 8.5に新機能「パイプ演算子」が導入されることが決まりました🎉
リリースはまだ先ですが、8.5の目玉となるであろうこの機能を一足早く紹介していきます!
⚠️この記事の内容は、2025年6月現在開発中の仕様に基づいています。PHP 8.5リリースまでに変更になる可能性がありますのでご了承ください。
パイプ演算子の基本
新しい演算子|>
が導入されます。左辺の値を引数として右辺のcallableを実行します。
<?php // この2行は同じ意味 $result = "Hello World" |> strlen(...); $result = strlen("Hello World");
右辺は引数1つのcallableであれば何でもOKです。
- 関数名
"strtoupper"
- 第一級 callable
strtoupper(...)
- 無名関数
fn($x) => ...
- オブジェクトとメソッド名の配列
[$obj, 'method']
__invoke
メソッドを持つクラスのインスタンス
など
2つ以上の引数が必要な関数は、無名関数でラップして呼び出します。
<?php // NG: str_replace は引数 3 つ 'foo' |> str_replace('o', 'a', ...); // Error // OK: 無名関数でラップ 'foo' |> fn($s) => str_replace('o', 'a', $s);
その他細かい仕様については、RFCを参照してください。
日本語訳 qiita.com
未リリースの機能ですが、今すぐ試したい場合は 3v4l.org で実行バージョン
git.master
を選択すると実行することができます。
実行例:https://3v4l.org/NeE79/rfc#vgit.master
パイプ演算子の活用法を考えてみる
ここまでパイプ演算子の機能を紹介しましたが、では実際我々がコーディングを行うにあたって、どのように活用できそうでしょうか?
高階関数を利用した活用例
パイプを生かすために、まず高階関数を4つ作ります。 これらは全て「関数 (callable) を受け取って関数を返す」関数になっています。
<?php /** * `$pred`が`true`を返す要素だけを抽出 */ function filter(callable $pred): Closure { return function (iterable $it) use ($pred): Generator { foreach ($it as $k => $v) { if ($pred($v, $k)) yield $k => $v; } }; } /** * `$f`を各要素に適用した結果を返す */ function map(callable $f): Closure { return function (iterable $it) use ($f): Generator { foreach ($it as $k => $v) { yield $k => $f($v, $k); } }; } /** * 副作用を実行し、値はそのまま次へ */ function tap(callable $side): Closure { return function (iterable $it) use ($side): Generator { foreach ($it as $k => $v) { $side($v, $k); yield $k => $v; } }; } /** * `$f`を使って要素を累積的に処理し、最終結果を返す */ function reduce(callable $f, mixed $initial = null): Closure { return function (iterable $it) use ($f, $initial) { $acc = $initial; foreach ($it as $v) { $acc = $f($acc, $v); } return $acc; }; }
上記の関数を使って、「請求済み注文の平均金額を出しつつ高額注文を拾う」というのを実装してみます。
<?php // ダミーデータ $orders = [ ['id' => 101, 'status' => 'paid', 'total' => 1200], ['id' => 102, 'status' => 'cancelled', 'total' => 800], ['id' => 103, 'status' => 'paid', 'total' => 1500], ['id' => 104, 'status' => 'pending', 'total' => 600], ['id' => 105, 'status' => 'paid', 'total' => 900], ]; $paidCount = 0; $bigOrders = []; $sum = $orders |> filter(fn($o) => $o['status'] === 'paid') // 請求済み注文のみ |> tap(function ($o) use (&$paidCount, &$bigOrders) { // 高額注文だけ保存 $paidCount++; if ($o['total'] > 1000) { $bigOrders[] = $o; } }) |> map(fn($o) => $o['total']) // 金額を取り出す |> reduce(fn($acc, $yen) => $acc + $yen, 0); // 合計金額を計算 $average = $paidCount ? $sum / $paidCount : 0; printf("請求済み注文数: %d 件\n", $paidCount); printf("平均金額: ¥%d\n", (int)$average); echo "---- 高額注文一覧 ----\n"; foreach ($bigOrders as $o) { printf("注文ID=%d 金額=¥%d\n", $o['id'], $o['total']); }
実行結果: https://3v4l.org/V8Ttj/rfc#vgit.master
請求済み注文数: 3 件 平均金額: ¥1200 ---- 高額注文一覧 ---- 注文ID=101 金額=¥1200 注文ID=103 金額=¥1500
このように、パイプ演算子と高階関数を組み合わせるととてもスッキリとした実装ができそうですね。
今後の展望(Future scope)
RFC の中には、今回はまだ実現しないが将来やりたい機能拡張のアイデアがいくつか記載されています。
これらが実現するかは未知数ですが、どんなことが検討されているのか見てみましょう。
関数合成演算子(候補は +
)
|>
が「その場で実行」なのに対し、合成演算子は
<?php $upperThenReverse = strtoupper(...) + strrev(...); // 実行はまだしない echo 'hello' |> $upperThenReverse; // => OLLEH
のように “クロージャ同士をくっ付けて、あとで呼べる 1 つの関数を返す” イメージです。
部分関数適用(Partial Function Application)
関数の引数の一部を?
にすることで、その部分を引数で受け取るクロージャになります。
<?php // 'foo' |> fn($s) => str_replace('o', 'a', $s) と同じ echo 'foo' |> str_replace('o', 'a', ?);
オブジェクト呼び出しの短縮記法
パイプ左辺のオブジェクトのメソッドを呼ぶ$$->foo(123)
のような簡易シンタックス
<?php new DateTime('2025-06-11') |> $$->modify('+1 day') |> $$->format('Y-m-d'); // => "2025-06-12"
イテレータ用の標準関数セット
この記事で紹介したmap
やfilter
を、PHPの標準関数として用意する構想もあります。毎回自前で定義しなくてよくなるとありがたいですね!
まとめ
パイプ演算子は「一時変数でつなぐコード」を「読みやすいパイプライン」に置き換えてくれます。小さな関数を作って組み合わせる関数型的なスタイルも書きやすくなりますので、PHP 8.5がリリースされたらぜひ試してみてください。