RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

PHP 8.5の新機能「パイプ演算子」

こんにちは、配配メール開発チームの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を参照してください。

wiki.php.net

日本語訳 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"

イテレータ用の標準関数セット
この記事で紹介したmapfilterを、PHPの標準関数として用意する構想もあります。毎回自前で定義しなくてよくなるとありがたいですね!

まとめ

パイプ演算子は「一時変数でつなぐコード」を「読みやすいパイプライン」に置き換えてくれます。小さな関数を作って組み合わせる関数型的なスタイルも書きやすくなりますので、PHP 8.5がリリースされたらぜひ試してみてください。

Copyright © RAKUS Co., Ltd. All rights reserved.