この記事はラクス Advent Calendar 2025の17日目の記事です
はじめに
PHPのRFCを読んだことがなかったので勉強も兼ねて読んでみました。
PHPのRFCの読み方
初めて読むので、読み方から軽く調べました。(詳細な説明はここでは省きます)
以下のお馴染みのページからRFCの情報が確認できます。
RFCのテンプレートも公開されています。これを見ておけばとりあえずどんな内容が書かれているかの前提知識は十分得られそうです。
Introductionで概要をおおざっぱに理解できるので、「今どんな議論が行われているのか?」や「Rejectされた理由は何か?」などを調べるだけでも勉強になりそうですね。
気になったRFCをいくつか読んだ
1. PHP8.5のRFC
先月リリースされたバージョン8.5のRFCに絞っていくつか見てみます。
Pipe operator v3
パイプ演算子です。冗長な中間変数を減らせることや、関数的に書けることで可読性が上がるので個人的には待望の機能でした。
RFCではユースケースが丁寧に説明されていますが、特に気になった点は以下です。
1. パフォーマンス
関数スタイル、メソッドスタイル、または静的メソッドスタイルでパイプ演算子を使う場合は、直接呼び出しに最適化されてコンパイルされるためオーバーヘッドはないという説明がされていました。
// 書いたコード
$result = "hello"
|> strtoupper(...) // 関数スタイル
|> $obj->process(...) // メソッドスタイル
|> StringHelper::format(...); // 静的メソッドスタイル
// コンパイル後(直接呼び出しに変換されるためオーバーヘッドなし)
$temp = strtoupper("hello");
$temp = $obj->process($temp);
$result = StringHelper::format($temp);
クロージャの場合はわずかながらオーバーヘッドが発生するようです。以下のように、コンパイルが終わった後もクロージャが展開されずに残ってしまいます。
$temp = fn($x) => array_map(strtoupper(...), $x)($temp);
$temp = fn($x) => array_filter($x, fn($v) => $v != 'O')($temp);
ただし、RFCにも書かれていましたがこの問題はPartial Function Applicationという機能があれば解決できるようです。
※以下のようにクロージャを使わずに?をプレースホルダとして使用できる機能のこと
// Partial Function Applicationの例
$underscore = str_replace(' ', '_', ?);
// クロージャを使った場合と同等
$underscore = fn(string $s): string => str_replace(' ' , '_', $s);
Partial Function ApplicationはPHP8.6で無事にapproveされたので、パイプ演算子のうまみがより活かせそうですね。
2. 言語機能としてパイプ演算子を提供する価値
パイプ演算子は外部ライブラリとして以前から提供されています。例えばLaravelではilluminate/pipelineがパッケージに含まれています。
RFCではCrell/fpの使用例が紹介されていました。
pipe($someVal,
htmlentities(...),
str_split(...),
fn($x) => array_map(strtoupper(...), $x),
fn($x) => array_filter($x, fn($v) => $v != 'O'),
);
上記の実装は以下の点でパフォーマンスに懸念があります。
| オーバーヘッド | 内容 |
|---|---|
| クロージャ生成 |
htmlentities(...)やstr_split(...)がクロージャオブジェクトを作る |
pipe()の呼び出し |
関数呼び出し自体のコスト |
| 内部のループ処理 |
pipe()内のforeachで各関数を順番に実行 |
言語機能としてパイプ演算子が提供される場合は上記の問題は解決されます。
RFCを読む限りだと完全に上位互換のような印象を受けました。
pipe()だと直感的に書けないので8.5で無事リリースされてよかったです。積極的に使っていこうと思います。
// Crell/fpの場合
// - クロージャ生成あり
// - pipe()関数の呼び出しあり
// - foreachループあり
pipe($val, htmlentities(...), str_split(...), strtoupper(...));
// ネイティブ演算子(コンパイル後)
// - 直接呼び出しに変換され、クロージャは生成されない
// - 演算子なので関数呼び出しがない
// - ループは展開され、存在しない
$temp = htmlentities($val);
$temp = str_split($temp);
$result = strtoupper($temp);
Crell/fpの実際のpipe()の実装はこちら
内部でループ処理が実装されています。
Deprecate semicolon after case in switch statement
case文の終端に使われるセミコロンを非推奨とするものです。
// 通常の書き方
switch ($value) {
case 1:
break;
}
// 非推奨になる書き方(セミコロン)
switch ($value) {
case 1;
break;
}
PHP/FI 2で使われていた古い記法が残っているだけで、もはや存在する必要もないので消すべきという意図のようです。
議論の内容はこちらから確認できました。メーリングリストの議論内容をwebに展開してくれているサイトのようです。
提案理由は以下
There isn't a need for this syntax to exist anymore, and very few PHP developers are even aware of its existence. In the top 1000 Composer packages, zero out of 35,777 total case statements are using the alternate syntax (as of 2024-11-27). Edit: as of 2025-07-07 there are still zero usages of the alternate syntax even in the top 1500 packages (out of 45,771 total case statements).
Composer上位1500パッケージの45,771件のcase文を調査しても使用例が0件だったと書かれていました。
上位1000パッケージをチェックするスクリプトが用意されているようなので、おそらくそれを使って調べていそうです。
投票結果は賛成22反対10だったので可決されています。(2/3以上の賛成票で可決のルール)
意外と反対票が多いですね。
「実害がないため非推奨にするのは過剰では?」という反対意見がありました。
2. Under DiscussionのRFC
RFCのページでUnder Discussionのステータスになっているものをいくつか読みました。
ちなみにUnder Discussionの定義は以下です。メーリングリストでアナウンス済みのRFCということですね。
This section is for RFCs that have been announced on the PHP “internals” mail list.
Data Class
PHPクラスにデータ修飾子を導入してデータクラスを定義できるようにしようというRFCです。
Pythonの @dataclass やKotlinのデータクラスに相当するものとして位置付けられています。
Data Classというものの存在をそもそも知らなかったのですが、「Value Objectとは違うもの?」という感想にまずなりました。
Value Objectは設計思想であるのに対し、Data Classは equals() 等を自動生成する言語機能という理解をしました。機能としてこれがあればより堅牢なクラス設計ができそうです。
RFCを読んだ感想としては、Copy-on-Write Triggerという機能が気になりました。変数の書き換えが発生するまではオブジェクトの複製を行わないようにして、メモリを節約しようという意図のようです。
PHPの配列操作の挙動はどうやらこのCoWになっているようです。
data class Rectangle {
public function __construct(public int $width, public int $height) {}
}
$rectangle1 = new Rectangle(10, 20);
$rectangle2 = $rectangle1; // ← この時点ではコピーは作られない(同じデータを共有)
$rectangle2->width = 30; // ← ここで初めてコピーが作られる
個人的にはKotlinのData Classesのように堅牢な設計にするためにはいい思想だと思うので今後注目したいと思います。
Type Aliases
型エイリアスを定義できるようにするというRFCです。
他の言語を普段あまり書かないのですが、TypeScriptみたいなイメージです。
<?php
use type int|float as Number;
use type Number|string as Scalar;
function add(Number $a, Number $b): Number {
return $a + $b;
}
function process(Scalar $value): Scalar {
return $value;
}
?>
コンパイル時にNumber型はint|float型に置き換えられるため、エラーメッセージにはエイリアス名が出ないようです。(TypeScriptも同じ)
<?php
use type int|float as Number;
function add(Number $a): Number {
return $a;
}
add("string");
// TypeError: add(): Argument #1 ($a) must be of type int|float, string given
?>
個人的にはあまり馴染みがないので、もし可決されたとしてもすぐに使いこなせる自信はあまりありません。ただ、機能として使えるようになったら地味に嬉しいなと思いました。
参考:
最後に
今回はPHPのRFCをいくつか読んでみました。
しっかり理解しようとするとかなり難しいなという印象を持ちました。これまでのPHPの歴史を理解した上で、低レベルの領域もしっかり理解し、メリット・デメリットを判断するのは、正直なところ当初想定していたよりハードルが高かったです。
ただ、一つのRFCを読むだけでもPHPの設計思想や他言語との違い、知らなかった言語機能やPHPに限らない一般的なプログラミングの概念を勉強できるのでとてもいい経験ができました。
今後はRFCを読んで、ただの感想で終わらせるのではなく「自分の意見と比較してどうか?」を考えられるように勉強していきたいと思います。