0

0

php8的注解你了解多少?

藏色散人

藏色散人

发布时间:2021-09-15 14:43:10

|

5144人浏览过

|

来源于segmentfault

转载

注解语法

#[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 动态地根据使用场景自动定义范围

注解作用范围列表:

立即学习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 动态地确定范围,如何理解?举例:

<?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一句话概括,就是修饰哪,它的作用范围就在哪

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

吐槽大师
吐槽大师

吐槽大师(Roast Master) - 终极 AI 吐槽生成器,适用于 Instagram,Facebook,Twitter,Threads 和 Linkedin

下载

举例:

<?php

#[Attribute]
function foo()
{

}

这里会报错 Fatal error: Attribute "Attribute" cannot target function (allowed targets: class),因为内置注解类的作用范围是 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 继承了 C1foo 方法,也继承了 foo 的注解。而 C2 覆盖了 C1foo 方法,因此注解也就不存在了。

推荐学习:《PHP8教程

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

46

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

178

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

51

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

92

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

227

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

532

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

171

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP8,究竟有啥野心..!?
PHP8,究竟有啥野心..!?

共4课时 | 0.6万人学习

php8,我来也
php8,我来也

共35课时 | 32.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号