php8的注解你了解多少?

藏色散人
发布: 2023-02-17 12:08:01
转载
4718 人浏览过

注解语法

#[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
  • Attribute::TARGET_PROPERTY
  • Attribute::TARGET_CLASS_CONSTANT
  • Attribute::TARGET_PARAMETER
  • Attribute::TARGET_ALL
  • Attribute::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_REPEATABLERoute不允许使用两次。

上述提到的,如果没有指定作用范围,会由 PHP 动态地确定范围,如何理解?举例:


         
登录后复制

上述的自定义注解类Deprecated并没有使用内置注解类#[Attribute]定义作用范围,因此当它修饰类OldLogger时,它的作用范围被动态地定义为TARGET_CLASS。当它修饰方法oldLogAction时,它的作用范围被动态地定义为TARGET_METHOD一句话概括,就是修饰哪,它的作用范围就在哪

需要注意的是, 在设置了作用范围之后,在编译阶段,除了内置注解类#[Attribute],自定义的注解类是不会自动检查作用范围的。除非你使用反射类ReflectionAttributenewInstance方法。

举例:


         
登录后复制

这里会报错Fatal error: Attribute "Attribute" cannot target function (allowed targets: class),因为内置注解类的作用范围是TARGET_CLASS,只能用于修饰类而不能是函数,因为内置注解类的作用范围仅仅是TARGET_CLASS,所以也不能重复修饰

而自定义的注解类,在编译时是不会检查作用范围的。


         
登录后复制

这样是不会报错的。那定义作用范围有什么意义呢?看一个综合实例。

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时,定义的作用范围才会生效,检测注解类定义的作用范围和实际修饰的范围是否一致,其它场景并不检测。

注解命名空间

 $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语法。

         
登录后复制

虽然在词法解析阶段是通过的,但是在编译阶段会抛出错误。

  • 在使用注解时可以换行

         
登录后复制
  • 注解可以成组使用

         
登录后复制
  • 注解的继承

注解是可以继承的,也可以覆盖。

 $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继承了C1foo方法,也继承了foo的注解。而C2覆盖了C1foo方法,因此注解也就不存在了。

推荐学习:《PHP8教程

以上是php8的注解你了解多少?的详细内容。更多信息请关注PHP中文网其他相关文章!

相关标签:
来源:segmentfault.com
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!