Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】PHP 8.5 nos trae closures en constantes de clases.

2025年09月24日に「Dev.to」が公開したITニュース「PHP 8.5 nos trae closures en constantes de clases.」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

PHP 8.5でクラス定数に無名関数を定義できる機能が加わった。しかし筆者は、この新機能の活用場面が不明瞭だとし、ファンクタやトレイトなど既存の設計パターンの方が適切だと意見を述べる。

ITニュース解説

PHP 8.5で導入される新しい機能について解説する。この新機能は「クラス定数にクロージャを定義できる」というもので、コード開発において新たな可能性をもたらすかもしれないが、同時にその有用性については疑問の声も上がっている。

まず、この新機能がどのようなものかを理解するために、いくつかの基本的な概念から説明する。

「クロージャ」または「匿名関数」とは、名前を持たない関数だ。PHPではfunction($value) { return !empty($value); }のように記述される。通常、関数は何らかの処理を実行し、結果を返す。匿名関数も同様だが、その場で定義され、変数に代入して後から呼び出したり、別の関数に引数として渡したりすることがよくある。これにより、特定の場所で一度だけ使うような小さな処理を簡潔に書ける。

次に、「クラス定数」についてだ。クラス定数とは、クラス内部で定義される、値が変更されることのない「固定された値」のことだ。例えば、const MAX_VALUE = 100;のように定義される。これらはクラスのすべてのインスタンスで共有され、プログラム実行中に値が変わることはない。データべース接続情報や、アプリケーションの設定値など、不変の値を定義するのに使われる。

PHP 8.5の新機能は、この「クラス定数」に「クロージャ(匿名関数)」を定義できるようになる、というものだ。つまり、これまで数値や文字列などの固定値しか入れられなかったクラス定数に、関数そのものを格納できるようになった。

具体例を見てみよう。記事にも示されているコードは以下の通りだ。

1class Example {
2    public const VALIDATOR = static function($value) {
3        return !empty($value);
4    };
5}

このコードでは、Exampleというクラスの中にVALIDATORという名前の定数が定義されている。しかし、その値は普通の文字列や数値ではなく、static function($value) { return !empty($value); }という匿名関数になっている。このstaticキーワードは、この匿名関数がクラスのインスタンスではなく、クラス自体に関連付けられていることを示す。

このVALIDATOR定数は、Example::VALIDATORとしてアクセスできる。そして、格納されているのが関数なので、Example::VALIDATOR('テスト値')のように、まるでクラスの静的メソッドのように直接呼び出すことができる。この例では、渡された$valueが空でないかどうかをチェックするバリデーター(検証器)として機能する。

この機能は、特定のクラスに関連する検証ロジックや、シンプルな処理をクラス定数としてまとめておきたい場合に便利そうに見える。しかし、記事の筆者はこの新機能に対して懐疑的な見方をしている。「この新しい機能は私を冷めた気持ちにさせる」「匿名関数を実行できる定数を追加しても、私には納得がいかない」と述べている。その理由は、同じような目的を達成するための、より適切だと考えられる既存の代替手段がいくつかあるからだ。

筆者が代替手段として挙げているのは、主に「Functor(ファンクター)」、「Trait(トレイト)」、そして「静的メソッドを持つクラス」だ。

Functor(ファンクター)

Functorとは、オブジェクトがまるで関数であるかのように振る舞う設計パターンのことだ。通常、関数は入力に対して出力を返す。Functorは、それをクラスのメソッドとして実現し、さらにそのオブジェクト自体を別の関数に適用したり、連鎖的に処理したりできるような設計になっている。

記事のFunctorの例を見てみよう。

1interface Functor
2{
3    public function fmap(callable $fn): Functor;
4}
5
6class Arr implements Functor
7{
8    protected $items;
9
10    public function __construct(array $items)
11    {
12        $this->items = $items;
13    }
14
15    public function fmap(callable $fn): Functor
16    {
17        return new static(array_map($fn, $this->items));
18    }
19}

この例では、Functorインターフェースがfmapというメソッドを定義している。ArrクラスはFunctorインターフェースを実装しており、配列を扱う。fmapメソッドは、渡された匿名関数$fnarray_map関数を使って$items配列の各要素に適用し、新しいArrインスタンスを返す。これにより、配列の変換処理をオブジェクト指向的に、かつ連鎖的に記述できる。

筆者は、Functorや通常の「サービス」と呼ばれるクラス(特定の機能を提供するクラス)は、データベースアクセスや外部サービスとの連携など、システムの「インフラストラクチャ層」(基盤となる部分)との接続が必要な場合に適していると考えている。これらのクラスは、他のクラスに「依存性の注入」という手法で渡すことができ、プログラム全体の柔軟性やテストのしやすさを向上させる。

Trait(トレイト)

Traitは、PHPにおけるコードの再利用メカニズムの一つだ。クラス継承のように親クラスと子クラスの関係を作るのではなく、複数のクラスに同じメソッドやプロパティを「組み込む」ことができる。これにより、異なる継承階層にあるクラス間でもコードを共有できる。

例えば、複数のクラスで共通のバリデーションロジックやユーティリティ関数を使いたい場合に、そのロジックをTraitとして定義し、必要なクラスでuse MyTrait;のように記述して組み込むことができる。

筆者は、Traitはデータを操作する「ドメイン層」(ビジネスロジックを扱う部分)に関連する場合に実装することを推奨している。例えば、特定のエンティティ(データモデル)に共通の処理を追加したい場合などだ。

静的メソッドを持つクラス

これは最もシンプルな代替手段の一つだ。特定の機能だけを提供するクラスを作り、その中に静的メソッド(staticキーワードで定義されたメソッド)を定義する方法だ。Example::VALIDATOR($value)のような呼び出し方は、まさにこの静的メソッドの呼び出し方に近い。

例えば、Validatorというクラスを作り、その中にstatic function isValid($value)のようなメソッドを定義する。これにより、Validator::isValid('データ')のように、クラスをインスタンス化せずに直接メソッドを呼び出して機能を利用できる。

筆者は、これらの既存の手段を比較検討した結果、クラス定数に匿名関数を置くという新機能の必要性に疑問を感じている。特に、Functorやサービス、Traitがそれぞれ特定の目的や層(インフラストラクチャ層、ドメイン層)に適していることを考慮すると、単に「関数を実行できる定数」がどのような状況で最適な選択肢となるのか、明確な利点が見当たらない、というのが筆者の主張だ。

最終的に筆者は、この新しい機能がどのような場面で「良いアイデア」または「興味深い」選択肢となるのか、依然として見通せないと結論付けている。PHPの進化は常に新しい可能性をもたらすが、その全ての機能が全ての状況で最適であるわけではない、ということを示唆していると言えるだろう。新機能を学ぶ際には、既存の機能との比較や、それぞれの利点・欠点を理解することが重要である。

関連コンテンツ

関連IT用語