首頁 >後端開發 >PHP8 >php8的註解你了解多少?

php8的註解你了解多少?

藏色散人
藏色散人轉載
2021-09-15 14:43:104948瀏覽

註解語法

#[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_REPEATABLELE
#在使用時,
#[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 方法,因此註解也就不存在了。

推薦學習:《

PHP8教學
#

以上是php8的註解你了解多少?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除