LoginSignup
16
8

More than 3 years have passed since last update.

Attributes とは

紆余曲折1ありましたが、 PHP 8 で Attributes という機能が提供されることが決まりました。

この Attributes とは、

  • Java で言う Annotations
  • C#, C++, Rust, Hack で言う Attributes
  • Python, JavaScript で言う Decorators

を指します。つまりどんなコードかというと、下記です。

return
// ↓コレ
#[Get('/')]
fn() => ['Hello' => 'World'];

記法はPHP7以下では行コメントとみなされるので、シンタックスハイライトで強調はされませんがエラーにもなりません。地味にうれしい。

似たようなものは既に見たことがあると思います。

BlogController.php

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 での利用方法を見てみましょう。

Product.php
<?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 をどう使えば便利になるかなーと研究すると良いと思います。


  1. PHP7.1 で導入しようとしたけど採択されなかったり、 @@Attr, <<Attr>> など様々な記法が入れ代わり立ち代わり採択されて、alpha/beta 段階ではそれぞれのバージョンで異なる実装がなされていました。 

16
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
8