Open2

[Symfony] アノテーションと属性の書き方について

tanaka_tarotanaka_taro

symfony ではphpのアノテーションや属性をよく使う。資料ではアノテーションで書かれているけれど、属性の方の書き方をしたい事がある。

そう言う場合に、このサンプルのアノテーションを属性として書く場合、どういう感じに書くのが良いのかという事を調べてる。

ChatGPT に php の アノテーションと属性の違いについて聞いた、それが次。

PHPにおける「アノテーション」と「属性(Attribute)」は、メタデータをコードに付加するための2つの異なるアプローチです。両者は似ている機能を提供しますが、実装方法と使用される文脈が異なります。

アノテーション

  • 定義: アノテーションは、PHPのコメント内に特定のフォーマットで記述されるメタデータです。通常は@記号に続いてキーワードが来る形式をとります。アノテーションは、PHP自体によって解釈されるのではなく、フレームワークやライブラリが独自に解析し利用します。
  • 使用例: SymfonyやDoctrineなどのフレームワークやライブラリで広く使用されています。これらのツールはアノテーションを解析し、ルーティングの定義、ORMマッピング、セキュリティチェックなどに使用します。
  • 特徴: コメント内に記述されるため、PHPの実行時には無視されます。アノテーションを解析するためには、フレームワークやライブラリが提供する特定のパーサーが必要です。

属性(Attribute)

  • 定義: 属性は、PHP 8.0で導入された新しい機能です。コードの宣言(クラス、プロパティ、メソッド、関数など)にメタデータを付与するための構文です。属性は#[AttributeName]の形式をとり、PHPによってネイティブにサポートされています。
  • 使用例: PHP 8.0以降のバージョンで使用でき、フレームワークやライブラリだけでなく、PHP自体によっても認識されます。属性は、アノテーションと同様に、ルーティング定義、ORMマッピングなどに使用されますが、PHPのランタイムによって直接サポートされるため、使用と解析がより効率的です。
  • 特徴: PHPのコードとして解釈されるため、コメントではなく実際のコードの一部として扱われます。これにより、タイプセーフであり、リフレクションAPIを通じてプログラムから直接アクセスできます。

違いの要点

  • 文法と解釈: アノテーションはコメントとして記述され、専用のパーサーによって解析される必要があります。一方、属性はPHPの構文として認識され、PHPランタイムによって直接解釈されます。
  • タイプセーフ: 属性はPHPの型システムの一部として機能するため、よりタイプセーフです。アノテーションは文字列ベースであり、間違いが起きやすい場合があります。
  • パフォーマンス: 属性はPHPによってネイティブにサポートされているため、アノテーションを解析する必要がなく、パフォーマンスが向上する可能性があります。
  • バージョン: 属性はPHP 8.0以降でのみ使用可能です。アノテーションは以前のバージョンのPHPでも使用されてきました。
tanaka_tarotanaka_taro

Entity にEntity Listener を指定する際、次のようにするとあった。

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\EntityListeners({"App\EventEntityListener\LifeListener"})
 */
class LifeActivityLog
{
    // エンティティのプロパティとメソッド
}

上記の @ORM\EntityListeners({"App\EventEntityListener\LifeListener"}) の所のアノテーションの記述を、属性として書きたいときは次。

#[ORM\EntityListeners(["App\EventEntityListener\LifeListener"])]

chatGPTに尋ねた所、次の様に返された。

PHPの属性を使用する場合、クラス名は::class構文を用いて指定するのが一般的です。

確かに <class_name>::class としたほうが良い感じな書き方な気がする。という事で次の書き方がいいなあと思った。

use Doctrine\ORM\Mapping as ORM;
use App\Repository\LifeActivityLogRepository;
use App\EventEntityListener\LifeListener;

#[ORM\Entity(repositoryClass: LifeActivityLogRepository::class)]
#[ORM\EntityListeners([LifeListener::class])]
class LifeActivityLog
{
}