Attributes とは
紆余曲折1ありましたが、 PHP 8 で Attributes
という機能が提供されることが決まりました。
この Attributes
とは、
- Java で言う
Annotations
- C#, C++, Rust, Hack で言う
Attributes
- Python, JavaScript で言う
Decorators
を指します。つまりどんなコードかというと、下記です。
return
// ↓コレ
#[Get('/')]
fn() => ['Hello' => 'World'];
記法はPHP7以下では行コメントとみなされるので、シンタックスハイライトで強調はされませんがエラーにもなりません。地味にうれしい。
似たようなものは既に見たことがあると思います。
use Symfony\Component\Routing\Annotation\Route;
class BlogController
{
// ↓コレ
/**
* @Route("/blog", name="blog_list")
*/
public function list()
{
return [];
}
}
これは現在 「アノテーション」 と呼ばれ、 doctrine/annotations を実装として使われることが非常に多いです(依存パッケージが約1,500個)。既に展開されているアノテーションと混同しないようにするため、アトリビュートと名付けられたようです。
簡単に言えば、 「クラスや関数、メソッドやオブジェクトなどにメタ情報を付加する機能」 です。
これで何を実現出来るかというと、 「フレームワークやライブラリが、アプリケーションコードに付加されたメタ情報を読み取ることで、アプリケーションコードのロジックを調整する」 ことが出来るようになります。
サンプルでは Symfony のルーティングで「/blog パスは BlogController::list メソッドを利用するよ」というメタ情報が付加されています。
また、別のサンプルとして doctrine/orm での利用方法を見てみましょう。
<?php
use Doctrine\ORM\Annotation as ORM;
/**
* @ORM\Entity @ORM\Table(name="products")
**/
class Product
{
/** @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 をアプリケーションコードに落とし込むことが出来るようになります。
// 例えば
use Php\Attributes\Deprecated;
#[Deprecated("Use bar() instead")]
function foo() {}
foo();
// PHP Deprecated: Function foo is deprecated in test.php on line 5
このように、以前は Doc コメントで拘束性のなかった機能を、実際にランタイムでエラー出力出来るような形式に変えることが出来ます。
// 現状 Attributes なしで記述した場合
class RequestSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
// 分かりづらい static 実装です
return [RequestEvent::class => 'onKernelRequest'];
}
public function onKernelRequest(RequestEvent $event)
{
}
}
// Attributes を使うと
#[Attribute]
class Listener
{
public $event;
public function __construct(string $event)
{
$this->event = $event;
}
}
class RequestSubscriber
{
// このメソッドが RequestEvent を listen していることが明白です
#[Listener(RequestEvent::class)]
public function onKernelRequest(RequestEvent $event)
{
}
}
このように、 Attributes を活用することにより、実装に型安全な拡張性を持たせることが出来るようになります。
誰が Attributes を実装するの?
主なユースケースとして提案されているのが、
- PHP Core 実装として
- PHP extension 実装として
- 各種ライブラリ実装として
- 各種フレームワーク実装として
です。つまりアプリケーションコード側は Attributes を実装する機会はあまりないかなと思います(もちろん利用するケースは増えていくと思います)。
なのでアプリケーションコードを実装する人は、 Attributes の細かい実装などに詳しくなる必要はありません。
ライブラリを実装している人などは、 PHP 8 時代に向けて Attributes をどう使えば便利になるかなーと研究すると良いと思います。
-
PHP7.1 で導入しようとしたけど採択されなかったり、
@@Attr
,<<Attr>>
など様々な記法が入れ代わり立ち代わり採択されて、alpha/beta 段階ではそれぞれのバージョンで異なる実装がなされていました。 ↩