2児のパパ系Webエンジニア
の開発日記
A Dad-of-Two Engineer's Development Diary

PHP 8.6の部分関数適用でパイプ演算子のfn包みが消えた!RC3で試したclamp()とハマりどころ

PHP 8.6の部分関数適用でパイプ演算子のfn包みが消えた!RC3で試したclamp()とハマりどころ

🙌 結論から

PHP 8.6 の部分関数適用(Partial Function Application)は、パイプ演算子と組み合わせたときに一番うれしい機能でした!

PHP 8.5 のパイプ演算子の記事で「引数が2つ以上ある関数はアロー関数で包む」と書いたのですが、8.6 ではその fn がほぼ要らなくなります。

PHP 8.6 の正式版は2026年11月19日の予定で、今は10月8日に出た RC3 が最新です🚀

次の RC4 は10月22日の予定なので、正式版の前に手元で触っておくなら今がちょうどいいタイミングかも・・・!

あわせて入る clamp() も、ページ件数の上限チェックみたいな「地味だけど毎回書くやつ」がスッキリしたのでまとめておきます(^^)

💡 パイプ演算子のfn包みが消えた

部分関数適用は、関数の引数の一部だけ先に埋めて、残りを受け取るクロージャを作る書き方です。

使う記号は2つだけです。

  • ? … ここに引数を1つあとで入れる
  • ... … 残りの引数をまとめてあとで入れる

前の記事で書いた検索キーワードの整形は、8.5 だと最後の preg_replace() だけアロー関数で包んでいました。

// PHP 8.5
$keyword = $request->input('q', '')
    |> trim(...)
    |> mb_strtolower(...)
    |> (fn (string $s) => preg_replace('/\s+/u', ' ', $s));

8.6 では、3番目の引数の位置に ? を置くだけです!

// PHP 8.6
$keyword = $request->input('q', '')
    |> trim(...)
    |> mb_strtolower(...)
    |> preg_replace('/\s+/u', ' ', ?);

括弧で囲むルールも、$s という仮の名前を考える必要もなくなりました✨

「パイプの右側にそのまま関数を書ける」だけで、読みやすさがかなり変わります。

RFC によると、パイプの右側が foo(1, ?) のように引数1つで済む形なら、クロージャを作らずに普通の関数呼び出しへ最適化されるそうです。

なので速さを気にして fn に戻す必要もないんですよね👍

array_map() に渡すときも同じで、str_replace() のような引数の多い関数をそのまま渡せます。

$titles = array_map(str_replace('【PR】', '', ?), $titles);

いままでは fn ($t) => str_replace('【PR】', '', $t) と書いていたので、1行が短くなってレビューのときに目が滑りにくい・・!

✨ clamp() でminとmaxの入れ子をやめた

一覧 API の per_page みたいに、受け取った数値を範囲に収める処理はどのプロジェクトにもあると思います。

私はずっとこう書いていました( .. )

$perPage = min(max($request->integer('per_page', 20), 1), 100);

動くのは動くのですが、min と max のどっちが上限なのか、毎回一瞬考えるんですよね(・_・;)

PHP 8.6 の clamp() だと、名前付き引数でそのまま書けます。

$perPage = clamp($request->integer('per_page', 20), min: 1, max: 100);

読んだ瞬間に「1〜100に収めてる」と分かるのがありがたいです!

RFC を見ると、min が max より大きいと ValueError になり、min か max に NAN を渡しても ValueError です。

逆に、$value が NAN のときはそのまま NAN が返ってきます。

入力をそのまま渡すなら、先に integer() などで型をそろえてから clamp() に入れるのが安心です。

ちなみに Laravel には前から Number::clamp() があるので、Laravel のアプリの中なら今すぐはこちらで十分です💡

素の PHP のスクリプトやパッケージ側でも同じ書き方ができるようになる、くらいの位置づけで見ています。

🔥 ハマった点と回避策

ここは実際につまずきやすい所なので、そのまま真似できる形で書いておきます(`・ω・´)

1. 引数は「呼んだとき」ではなく「作ったとき」に評価される

これが一番ハマりやすいと思います🤔

$log = sprintf('[%s] %s', now()->toDateTimeString(), ?);

// 10分後に呼んでも、時刻は $log を作った瞬間のまま
Log::info($log('同期が終わりました'));

RFC にも「引数の式は部分関数を作った時点で評価される」と書かれています。

Octane やキューのワーカーのように、プロセスが長く生きる環境でクロージャを使い回すと、時刻やリクエストの値が古いまま残ります(T_T)

毎回評価したい値があるなら、そこだけはアロー関数で書きます。

$log = fn (string $msg) => sprintf('[%s] %s', now()->toDateTimeString(), $msg);

2. ... は余った引数まで渡してしまう

Laravel の Collection::map() は、コールバックに値とキーの2つを渡します。

なので intval(...) をそのまま渡すと、キーが2番目の引数の $base(基数)に入ってしまいます。

// キーが $base に入るので、思った数値にならない
collect(['10', '20', '30'])->map(intval(...));

// ? なら値だけ受け取って、余った引数は捨てられる
collect(['10', '20', '30'])->map(intval(?));

RFC でも intval(...) の例でこの注意が書かれていました。

**... は「全部渡す」、? は「1つだけ受け取る」**と覚えておくと、Collection と組み合わせたときに事故りにくいです👀

3. new には使えない

new Money(100, ?) のようにクラスの生成で使うとコンパイルエラーになります。

値オブジェクトを作る所は、今までどおり fn か static なファクトリメソッドで書く必要があります(´・ω・`)

4. ? の数が合わないと ArgumentCountError

必須の引数が3つある関数に ? を1つだけ置くと、足りない分で ArgumentCountError が出ます。

残りを全部あとで渡したいときは、最後に ... を付ければ大丈夫です( ˘ω˘ )

👀 RC3を試す前に確認しておきたいこと

まず、RC は本番では使わない前提です。

php.net の告知でも「本番では使わないで」とはっきり書かれています。

ちょっと面白かったのが、RC1 は配布物の作成ミスで実際には出ておらず、RC2 がその代わりになったことです。

「RC1 どこ・・・?」と探した人がいたら、それが理由です笑

試すときは、公式の Docker イメージに 8.6-rc-cli-alpine3.23 のような RC のタグがあるので、CI のマトリックスに1行足すのが一番手軽だと思います。

アプリ側で ? を書き始めるなら、composer.json の PHP 要件も ^8.6 に上げておかないと、8.5 以前のサーバーで構文エラーになります。

Laravel 13 の要件は PHP 8.3 以上なので、フレームワークに合わせて上げる必要はありません💪

8.6 で非推奨になるもののうち、私が上げる前に grep で探しておこうと決めたのは次のあたりです。

  • spl_object_hash() → spl_object_id() へ
  • is_integer() / is_long() / is_double() / doubleval() → is_int() / is_float() / floatval() へ
  • mb_ereg 系(Mbregex)の関数
  • finally ブロックの中の return

あと、新しくインストールした環境では session.use_strict_mode が 1、session.cookie_httponly が 1、session.cookie_samesite が Lax になります。

Laravel はセッションを自前で扱っているので大きな影響はなさそうですが、素の PHP で動いている古い管理画面があるなら一度見ておきたいところです👀

trim() が改ページ文字(\f)も消すようになったのも、テキストを厳密に比べるテストがある人は気にしておくといいかもしれません。

パパ系Webエンジニア〜愛用ガジェット一覧 🛍️

この記事を書いている作業環境のガジェットを楽天ROOMにまとめています ✨ 在宅エンジニアのデスク作りの参考にどうぞ 🙌

👍 まとめ

PHP 8.6 の部分関数適用は、8.5 のパイプ演算子に残っていた「fn で包む手間」を片付けてくれる機能でした。

clamp() も含めて派手さはないですが、毎日書くコードの1行が短くなるタイプの変更なので、正直かなりうれしいです(^o^)/

ただ、引数が作った時点で評価されることと、... が余った引数まで渡すことだけは、Laravel のコードに入れる前に押さえておきたいです。

11月19日の正式版の前に、CI に RC を1行足して非推奨の警告が出ないか見ておくと、上げるときにかなり楽になると思います🙌


※ バージョン番号・リリース予定日・機能は今後も変わる可能性があります。最新情報は PHP 公式サイト をご確認ください👀

参考になったらシェアしてもらえるとうれしいです🙌

Xでポスト Threads はてブ LINE