搜尋
首頁後端開發PHP8php8的註解你了解多少?

php8的註解你了解多少?

Sep 15, 2021 pm 02:43 PM
phpphp8

註解語法

#[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。如有侵權,請聯絡admin@php.cn刪除

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),