php8的注解你了解多少?

广告:宝塔Linux面板高效运维的服务器管理软件 点击【 https://www.bt.cn/p/uNLv1L 】立即购买

php8的注解你了解多少?

注解语法
#[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_CLASSAttribute::TARGET_FUNCTIONAttribute::TARGET_METHODAttribute::TARGET_PROPERTYAttribute::TARGET_CLASS_CONSTANTAttribute::TARGET_PARAMETERAttribute::TARGET_ALLAttribute::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 动态地确定范围,如何理解?举例:

<?phpclass 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 方法。

举例:

<?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 /indexphp test.php /index_aliasphp test.php /test
登录后复制

在使用 newInstance 时,定义的作用范围才会生效,检测注解类定义的作用范围和实际修饰的范围是否一致,其它场景并不检测。

注解命名空间
<?phpnamespace {    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 语法。
<?phpclass IndexController{    #[Route(...["/index", ["get"]])]    public function index()    {    }}
登录后复制

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

在使用注解时可以换行
//实例化类$route = new Route(path: "/path", methods: ["get"]);0
登录后复制注解可以成组使用
//实例化类$route = new Route(path: "/path", methods: ["get"]);1
登录后复制注解的继承

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

//实例化类$route = new Route(path: "/path", methods: ["get"]);2
登录后复制

C3 继承了 C1foo 方法,也继承了 foo 的注解。而 C2 覆盖了 C1foo 方法,因此注解也就不存在了。

推荐学习:《PHP8教程》

以上就是php8的注解你了解多少?的详细内容,更多请关注9543建站博客其它相关文章!

广告:SSL证书一年128.66元起,点击购买~~~

9543建站博客
一个专注于网站开发、微信开发的技术类纯净博客。
作者头像
admin创始人

肥猫,知名SEO博客站长,14年SEO经验。

上一篇:详解PHP7语言基础
下一篇:聊聊Laravel中字段转换的基本知识

相关推荐

发表评论

关闭广告
关闭广告