Attributes とは
紆余曲折1ありましたが、 PHP 8 で Attributes
という機能が提供されることが決まりました。
この Attributes
とは、
- Java で言う
Annotations
- C#, C++, Rust, Hack で言う
Attributes
- Python, JavaScript で言う
Decorators
を指します。つまりどんなコードかというと、下記です。
return// ↓コレ#[Get('/')]fn()=>['Hello'=>'World'];
記法はPHP7以下では行コメントとみなされるので、シンタックスハイライトで強調はされませんがエラーにもなりません。地味にうれしい。
似たようなものは既に見たことがあると思います。
useSymfony\Component\Routing\Annotation\Route;classBlogController{// ↓コレ/**
* @Route("/blog", name="blog_list")
*/publicfunctionlist(){return[];}}
これは現在 「アノテーション」と呼ばれ、 doctrine/annotationsを実装として使われることが非常に多いです(依存パッケージが約1,500個)。既に展開されているアノテーションと混同しないようにするため、アトリビュートと名付けられたようです。
簡単に言えば、 「クラスや関数、メソッドやオブジェクトなどにメタ情報を付加する機能」です。
これで何を実現出来るかというと、 「フレームワークやライブラリが、アプリケーションコードに付加されたメタ情報を読み取ることで、アプリケーションコードのロジックを調整する」ことが出来るようになります。
サンプルでは Symfony のルーティングで「/blog パスは BlogController::list メソッドを利用するよ」というメタ情報が付加されています。
また、別のサンプルとして doctrine/ormでの利用方法を見てみましょう。
<?phpuseDoctrine\ORM\AnnotationasORM;/**
* @ORM\Entity @ORM\Table(name="products")
**/classProduct{/** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue **/private$id;/** @ORM\Column(type="string") **/private$name;// .. (other code)}
このクラスがエンティティであること、DBのカラムとプロパティを紐づけることなどがメタ情報(コメントアノテーション)として書かれています。
Attributes が実装されて何がうれしい?
既にコメントアノテーションが多くの PHP プロジェクトで利用されていて、機能的にはほとんど同じに見えます。では、何故今さら言語機能として Attributes が採用されたのでしょうか。
パフォーマンスの向上
アノテーションの Doc コメントは文字列で取得することしか出来ず、パースするのに時間がかかります。そのため doctrine/annotations では一度取得したアノテーションをキャッシュすることが可能になっています。
Attributes の形式だと Reflection
から取得出来るようになるので、パースの時間を省略することが出来ます。
取得容易性の向上
ReflectionClass::getAttributes()
などのメソッドで、 Reflection
から簡単に Attributes を取り出せるようになったので、 Doc コメントアノテーションに比べ取得が格段に簡単になります。
明確な型インスタンス
Attribute::newInstance()
メソッドを使って Attribute
クラスのインスタンスを生成することが出来るため、明確な型を使って利用ロジックを実装することが出来ます。そのため、静的解析やIDE補完などが容易に行えるようになります。
PHP Core や Extensions での利用
PHP拡張の実装時に Attributes を使うことによって、拡張機能により実装された Attributes をアプリケーションコードに落とし込むことが出来るようになります。
// 例えばusePhp\Attributes\Deprecated;#[Deprecated("Use bar() instead")]functionfoo(){}foo();// PHP Deprecated: Function foo is deprecated in test.php on line 5
このように、以前は Doc コメントで拘束性のなかった機能を、実際にランタイムでエラー出力出来るような形式に変えることが出来ます。
// 現状 Attributes なしで記述した場合classRequestSubscriberimplementsEventSubscriberInterface{publicstaticfunctiongetSubscribedEvents():array{// 分かりづらい static 実装ですreturn[RequestEvent::class=>'onKernelRequest'];}publicfunctiononKernelRequest(RequestEvent$event){}}// Attributes を使うと#[Attribute]classListener{public$event;publicfunction__construct(string$event){$this->event=$event;}}classRequestSubscriber{// このメソッドが RequestEvent を listen していることが明白です#[Listener(RequestEvent::class)]publicfunctiononKernelRequest(RequestEvent$event){}}
このように、 Attributes を活用することにより、実装に型安全な拡張性を持たせることが出来るようになります。
誰が Attributes を実装するの?
主なユースケースとして提案されているのが、
- PHP Core 実装として
- PHP extension 実装として
- 各種ライブラリ実装として
- 各種フレームワーク実装として
です。つまりアプリケーションコード側は Attributes を実装する機会はあまりないかなと思います(もちろん利用するケースは増えていくと思います)。
なのでアプリケーションコードを実装する人は、 Attributes の細かい実装などに詳しくなる必要はありません。
ライブラリを実装している人などは、 PHP 8 時代に向けて Attributes をどう使えば便利になるかなーと研究すると良いと思います。
PHP7.1 で導入しようとしたけど採択されなかったり、
@@Attr
,<<Attr>>
など様々な記法が入れ代わり立ち代わり採択されて、alpha/beta 段階ではそれぞれのバージョンで異なる実装がなされていました。 ↩