アノテーション構文
#[Route] #[Route()] #[Route("/path", ["get"])] #[Route(path: "/path", methods: ["get"])]
実際、構文はインスタンス化されたクラスとよく似ていますが、new
キーワードが欠落している点が異なります。 。
注釈名は変数にすることはできませんが、定数または定数式のみにすることができることに注意してください。
//实例化类 $route = new Route(path: "/path", methods: ["get"]);
(path: "/path ",methods : ["get"])
は php8
の新しい構文です。パラメータを渡すときに、仮パラメータの順序でパラメータを渡さずに、パラメータ名を指定できます。
アノテーション クラスのスコープ
アノテーション クラスを定義する場合、組み込みのアノテーション クラス#[Attribute]
を使用してアノテーション クラスのスコープを定義できます。または、省略できます。スコープは、使用シナリオに基づいて PHP によって動的に自動的に定義されます。
アノテーション スコープ リスト:
- Attribute::TARGET_CLASS
- Attribute::TARGET_FUNCTION
- Attribute::TARGET_METHOD
- 属性::TARGET_PROPERTY
- 属性::TARGET_CLASS_CONSTANT
- 属性::TARGET_PARAMETER
- 属性::TARGET_ALL
- 属性::IS_REPEATABLE
使用すると、#[Attribute]
は#[Attribute(Attribute::TARGET_ALL)]
と同等になります。便宜上、通常は前者が使用されます。
1~7 はそれぞれクラス、関数、クラスメソッド、クラス属性、クラス定数、パラメータなどに対応しており、最初の 6 項目は | で自由に組み合わせることができます。
または演算子 (Attribute::TARGET_CLASS | Attribute::TARGET_FUNCTION
など)。 (Attribute::TARGET_ALL
には最初の 6 項目が含まれますが、Attribute::IS_REPEATABLE
は含まれません)。
Attribute::IS_REPEATABLE
注釈を繰り返すことができるかどうかを設定します。例:
class IndexController { #[Route('/index')] #[Route('/index_alias')] public function index() { echo "hello!world" . PHP_EOL; } }
Attribute::IS_REPEATABLE
が設定されていない場合、ルート
重複使用はできません。
上で述べたように、スコープが指定されていない場合、PHP が動的にスコープを決定します。例:
<?php class Deprecated { } class NewLogger { public function newLogAction(): void { //do something } #[Deprecated('oldLogAction已废弃,请使用newLogAction代替')] public function oldLogAction(): void { } } #[Deprecated('OldLogger已废弃,请使用NewLogger代替')] class OldLogger { }
上記のカスタム アノテーション クラス Deprecated
は、スコープの定義に組み込みアノテーション クラス #[Attribute]
を使用しません。クラス OldLogger
を変更し、そのスコープは TARGET_CLASS
として動的に定義されます。メソッド oldLogAction
を変更すると、そのスコープは TARGET_METHOD
として動的に定義されます。 一言で言えば、変更された場合、そのスコープは
スコープを設定した後、コンパイル段階で組み込みのスコープに加えて、アノテーション クラス #[属性]
、カスタム アノテーション クラスはスコープを自動的にチェックしません。リフレクション クラス ReflectionAttribute
の newInstance
メソッドを使用しない限り。
例:
<?php #[Attribute] function foo() { }
ここでエラーが報告されます致命的エラー: 属性 "属性" は関数 (許可されたターゲット: クラス) をターゲットにできません
、組み込みアノテーション クラスの役割 スコープは TARGET_CLASS
で、関数ではなくクラスの変更にのみ使用できます。 組み込みアノテーション クラスのスコープは TARGET_CLASS のみであるため
であるため、 を繰り返し変更することはできません。
カスタム アノテーション クラスのスコープはコンパイル時にチェックされません。
<?php #[Attribute(Attribute::TARGET_CLASS)] class A1 { } #[A1] function foo() {}
この方法では、エラーは報告されません。では、スコープを定義する意味は何でしょうか?包括的な例を見てみましょう。
<?php #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION | Attribute::IS_REPEATABLE)] class Route { protected $handler; public function __construct( public string $path = '', public array $methods = [] ) {} public function setHandler($handler): self { $this->handler = $handler; return $this; } public function run() { call_user_func([new $this->handler->class, $this->handler->name]); } } class IndexController { #[Route(path: "/index_alias", methods: ["get"])] #[Route(path: "/index", methods: ["get"])] public function index(): void { echo "hello!world" . PHP_EOL; } #[Route("/test")] public function test(): void { echo "test" . PHP_EOL; } } class CLIRouter { protected static array $routes = []; public static function setRoutes(array $routes): void { self::$routes = $routes; } public static function match($path) { foreach (self::$routes as $route) { if ($route->path == $path) { return $route; } } die('404' . PHP_EOL); } } $controller = new ReflectionClass(IndexController::class); $methods = $controller->getMethods(ReflectionMethod::IS_PUBLIC); $routes = []; foreach ($methods as $method) { $attributes = $method->getAttributes(Route::class); foreach ($attributes as $attribute) { $routes[] = $attribute->newInstance()->setHandler($method); } } CLIRouter::setRoutes($routes); CLIRouter::match($argv[1])->run();
php test.php /index php test.php /index_alias php test.php /test
定義されたスコープは、newInstance
を使用する場合にのみ有効になります。アノテーション クラスによって定義されたスコープが、実際に変更されたスコープと一致するかどうかがチェックされます。他のシナリオはテストされていません。
アノテーション名前空間
<?php namespace { function dump_attributes($attributes) { $arr = []; foreach ($attributes as $attribute) { $arr[] = ['name' => $attribute->getName(), 'args' => $attribute->getArguments()]; } var_dump($arr); } } namespace Doctrine\ORM\Mapping { class Entity { } } namespace Doctrine\ORM\Attributes { class Table { } } namespace Foo { use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Attributes; #[Entity("imported class")] #[ORM\Entity("imported namespace")] #[\Doctrine\ORM\Mapping\Entity("absolute from namespace")] #[\Entity("import absolute from global")] #[Attributes\Table()] function foo() { } } namespace { class Entity {} dump_attributes((new ReflectionFunction('Foo\foo'))->getAttributes()); } //输出: array(5) { [0]=> array(2) { ["name"]=> string(27) "Doctrine\ORM\Mapping\Entity" ["args"]=> array(1) { [0]=> string(14) "imported class" } } [1]=> array(2) { ["name"]=> string(27) "Doctrine\ORM\Mapping\Entity" ["args"]=> array(1) { [0]=> string(18) "imported namespace" } } [2]=> array(2) { ["name"]=> string(27) "Doctrine\ORM\Mapping\Entity" ["args"]=> array(1) { [0]=> string(23) "absolute from namespace" } } [3]=> array(2) { ["name"]=> string(6) "Entity" ["args"]=> array(1) { [0]=> string(27) "import absolute from global" } } [4]=> array(2) { ["name"]=> string(29) "Doctrine\ORM\Attributes\Table" ["args"]=> array(0) { } } }
は、通常のクラスの名前空間と一致します。
注意すべきその他の問題
-
アノテーション クラス パラメーター リストで unpack
構文を使用することはできません。
<?php class IndexController { #[Route(...["/index", ["get"]])] public function index() { } }
- 注釈を使用するときに行を折り返すことができます
<?php class IndexController { #[Route( "/index", ["get"] )] public function index() { } }
- 注釈はグループ内で使用できます
<?php class IndexController { #[Route( "/index", ["get"] ), Other, Another] public function index() { } }
- 継承注釈
<?php class C1 { #[A1] public function foo() { } } class C2 extends C1 { public function foo() { } } class C3 extends C1 { #[A1] public function bar() { } } $ref = new \ReflectionClass(C1::class); print_r(array_map(fn ($a) => $a->getName(), $ref->getMethod('foo')->getAttributes())); $ref = new \ReflectionClass(C2::class); print_r(array_map(fn ($a) => $a->getName(), $ref->getMethod('foo')->getAttributes())); $ref = new \ReflectionClass(C3::class); print_r(array_map(fn ($a) => $a->getName(), $ref->getMethod('foo')->getAttributes()));
C3 は、
C1 の
foo メソッドを継承し、
foo のアノテーションも継承します。また、
C2 は
C1 の
foo メソッドをカバーしているため、アノテーションは存在しません。