>  기사  >  백엔드 개발  >  php8 주석에 대해 얼마나 알고 있나요?

php8 주석에 대해 얼마나 알고 있나요?

藏色散人
藏色散人앞으로
2021-09-15 14:43:104897검색

주석 구문

#[Route]
#[Route()]
#[Route("/path", ["get"])]
#[Route(path: "/path", methods: ["get"])]

사실 구문은 new 키워드가 없다는 점을 제외하면 인스턴스화된 클래스와 매우 유사합니다. 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 动态地确定范围,如何理解?举例:

<?php

class Deprecated
{

}

class NewLogger
{
    public function newLogAction(): void
    {
        //do something
    }

    #[Deprecated(&#39;oldLogAction已废弃,请使用newLogAction代替&#39;)]
    public function oldLogAction(): void 
    {

    }
}

#[Deprecated(&#39;OldLogger已废弃,请使用NewLogger代替&#39;)]
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 = &#39;&#39;,
        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[] = [&#39;name&#39; => $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

주석 이름은 변수가 될 수 없으며 상수 또는 상수 표현식만 가능하다는 점에 유의해야 합니다.
rrreee

(path: "/path", method: ["get "]) php8의 새로운 구문입니다. 매개변수 전달 시 형식적인 매개변수 순서대로 매개변수를 전달하는 대신 매개변수 이름을 지정할 수 있습니다. 주석 클래스 범위

주석 클래스를 정의할 때 내장된 주석 클래스 #[속성]을 사용하여 주석 클래스의 범위를 정의할 수도 있고 을 생략할 수도 있습니다. >PHP는 자동으로 범위를 정의하기 위해 시나리오 사용에 따라 이를 동적으로 조정합니다. 🎜🎜주석 범위 목록: 🎜
  • 속성::TARGET_CLASS
  • 속성::TARGET_FUNCTION
  • 속성::TARGET_METHOD
  • 속성:: TARGET_PROPERTY
  • 속성::TARGET_CLASS_CONSTANT
  • 속성::TARGET_PARAMETER
  • 속성::TARGET_ALL
  • 속성::IS_REPEATABLE
  • 사용 시 #[Attribute]#[Attribute(Attribute::TARGET_ALL)]와 동일하며 편의상 일반적으로 전자가 사용됩니다.
    🎜1~7은 모두 이해하기 쉽고 각각 클래스, 함수, 클래스 메소드, 클래스 속성, 클래스 상수, 매개변수 및 모든 것에 해당합니다. 처음 6개 항목은 |를 사용하여 마음대로 결합할 수 있습니다. 또는 연산자(예: Attribute::TARGET_CLASS Attribute::TARGET_FUNCTION) (속성::TARGET_ALL에는 처음 6개 항목이 포함되지만 속성::IS_REPEATABLE은 포함되지 않습니다). 🎜🎜속성::IS_REPEATABLE 주석을 반복할 수 있는지 여부를 설정합니다. 예: 🎜rrreee🎜속성::IS_REPEATABLE이 설정되지 않은 경우 경로 중복 사용은 허용되지 않습니다. 🎜🎜위에서 언급한 것처럼 범위를 지정하지 않으면 PHP가 범위를 동적으로 결정합니다. 어떻게 이해해야 할까요? 예: 🎜rrreee🎜위에 언급된 사용자 정의 주석 클래스 Deprecated는 범위를 정의하기 위해 내장 주석 클래스 #[Attribute]를 사용하지 않으므로 클래스 OldLogger, 해당 범위는 TARGET_CLASS로 동적으로 정의됩니다. oldLogAction 메서드를 수정하면 해당 범위가 TARGET_METHOD로 동적으로 정의됩니다. 한 문장에서는 수정되는 위치마다 해당 범위가 적용됩니다.🎜🎜범위를 설정한 후 컴파일 단계에서 내장된 주석 클래스 외에도 #[속성], 사용자 정의 주석 클래스는 자동으로 범위를 확인하지 않습니다. 리플렉션 클래스 ReflectionAttributenewInstance 메서드를 사용하지 않는 한. 🎜🎜예: 🎜rrreee🎜오류 치명적인 오류: 속성 "속성"이 함수(허용된 대상: 클래스)를 대상으로 할 수 없습니다.가 여기에 보고됩니다. 내장된 주석의 범위가 클래스는 TARGET_CLASS이며 함수가 아닌 클래스 수정에만 사용할 수 있습니다. 내장 주석 클래스의 범위는 TARGET_CLASS이므로 수정할 수 없습니다. 반복적으로. 🎜🎜사용자 정의된 주석 클래스는 컴파일 중에 범위를 확인하지 않습니다. 🎜rrreee🎜 이렇게 하면 오류가 보고되지 않습니다. 그렇다면 범위를 정의하는 요점은 무엇입니까? 포괄적인 예를 살펴보겠습니다. 🎜rrreeerrreee🎜 정의된 범위는 newInstance를 사용할 때만 적용됩니다. 주석 클래스에서 정의한 범위가 실제 수정된 범위와 일치하는지 확인합니다. 다른 시나리오는 확인하지 않습니다. 🎜🎜주석 네임스페이스 🎜rrreee🎜는 일반 클래스의 네임스페이스와 동일합니다. 🎜🎜주의해야 할 다른 문제🎜
    • 주석 클래스 매개변수 목록에서 unpack 구문을 사용할 수 없습니다.
    rrreee🎜어휘파싱 단계에서는 통과하지만 컴파일 단계에서는 오류가 발생합니다. 🎜
    • 주석을 사용할 때 줄 바꿈 가능
    rrreee
    • 주석은 그룹으로 사용할 수 있음
    rrreee
    • 주석 상속
    🎜주석은 상속되고 재정의될 수 있습니다. 🎜rrreee🎜C3C1foo 메서드를 상속하고 foo의 주석도 상속합니다. 그리고 C2C1foo 메서드를 포함하므로 주석이 존재하지 않습니다. 🎜🎜🎜추천 학습: "🎜PHP8 Tutorial🎜"🎜🎜

위 내용은 php8 주석에 대해 얼마나 알고 있나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제