Annotation syntax
#[Route] #[Route()] #[Route("/path", ["get"])] #[Route(path: "/path", methods: ["get"])]
In fact, the syntax is very similar to the instantiated class, except that thenew
keyword is missing.
It should be noted that the annotation name cannot be a variable, but can only be a constant or constant expression
//实例化类 $route = new Route(path: "/path", methods: ["get"]);
(path: "/path", methods : ["get"])
is the new syntax ofphp8
. When passing parameters, you can specify the parameter name and do not pass the parameters in the order of the formal parameters.
Annotation class scope
When defining an annotation class, you can use the built-in annotation class#[Attribute]
to define the scope of the annotation class, or you can omit it,The scope is automatically defined by PHP dynamically based on usage scenarios.
Annotation scope list:
- Attribute::TARGET_CLASS
- Attribute::TARGET_FUNCTION
- Attribute::TARGET_METHOD
- Attribute::TARGET_PROPERTY
- Attribute::TARGET_CLASS_CONSTANT
- Attribute::TARGET_PARAMETER
- Attribute::TARGET_ALL
- Attribute::IS_REPEATABLE
When used,#[Attribute]
is equivalent to#[Attribute(Attribute::TARGET_ALL)]
. For convenience, the former is generally used.
1~7 are all easy to understand and correspond to classes, functions, class methods, class attributes, class constants, parameters, and everything respectively. The first 6 items can be combined at will using|
or operators , such asAttribute::TARGET_CLASS | Attribute::TARGET_FUNCTION
. (Attribute::TARGET_ALL
contains the first 6 items, but does not containAttribute::IS_REPEATABLE
).
Attribute::IS_REPEATABLE
Set whether the annotation can be repeated, for example:
class IndexController { #[Route('/index')] #[Route('/index_alias')] public function index() { echo "hello!world" . PHP_EOL; } }
IfAttribute::IS_REPEATABLE
is not set,Route
Two uses are not allowed.
As mentioned above, if the scope is not specified, PHP will dynamically determine the scope. How to understand? Example:
The above-mentioned custom annotation class
Deprecated
does not use the built-in annotation class#[Attribute]
to define the scope, so when it modifies the classOldLogger
, its scope is dynamically defined asTARGET_CLASS
. When it modifies methodoldLogAction
, its scope is dynamically defined asTARGET_METHOD
.In one sentence, where it is modified, its scope will beIt should be noted that after setting the scope, during the compilation phase, in addition to the built-in annotation class
#[Attribute]
, custom annotation classes will not automatically check the scope. Unless you use thenewInstance
method of the reflection classReflectionAttribute
.Example:
An error will be reported here
Fatal error: Attribute "Attribute" cannot target function (allowed targets: class)
, because of the role of the built-in annotation class The scope isTARGET_CLASS
, which can only be used to modify classes and not functions.Because the scope of the built-in annotation class is onlyTARGET_CLASS
, socannot be modified repeatedly.The scope of custom annotation classes will not be checked at compile time.
This way no error will be reported. So what’s the point of defining scope? Let’s look at a comprehensive example.
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 /testThe defined scope will only take effect when using
newInstance
. It is checked whether the scope defined by the annotation class is consistent with the actual modified scope. Other scenarios are not tested.Annotation namespace
$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) { } } }is consistent with the namespace of ordinary classes.
Some other issues to pay attention to
- You cannotuse the
unpack
syntax in the annotation class parameter list.
Although the lexical parsing stage is passed, an error will be thrown during the compilation stage.
- You can wrap lines when using annotations
- Annotations can be used in groups
- Inheritance of annotations
Annotations can be inherited or overridden.
$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
inherits thefoo
method ofC1
and also inherits the annotations offoo
. AndC2
covers thefoo
method ofC1
, so the annotation does not exist.