您好,欢迎访问一九零五行业门户网

php8的注解你了解多少?

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_repeatable,route不允许使用两次。
上述提到的,如果没有指定作用范围,会由 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],自定义的注解类是不会自动检查作用范围的。除非你使用反射类 reflectionattribute 的 newinstance 方法。
举例:
<?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() { }}
虽然在词法解析阶段是通过的,但是在编译阶段会抛出错误。
在使用注解时可以换行<?php class indexcontroller{ #[route( "/index", ["get"] )] public function index() { }}
注解可以成组使用<?phpclass indexcontroller{ #[route( "/index", ["get"] ), other, another] public function index() { }}
注解的继承注解是可以继承的,也可以覆盖。
<?phpclass 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 方法,因此注解也就不存在了。
推荐学习:《php8教程》
以上就是php8的注解你了解多少?的详细内容。
其它类似信息

推荐信息