ホームページ >バックエンド開発 >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
  • 属性::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 など)。 (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 が設定されていない場合、ルート 重複使用はできません。

上で述べたように、スコープが指定されていない場合、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 として動的に定義されます。 一言で言えば、変更された場合、そのスコープは

スコープを設定した後、コンパイル段階で組み込みのスコープに加えて、アノテーション クラス #[属性]、カスタム アノテーション クラスはスコープを自動的にチェックしません。リフレクション クラス ReflectionAttributenewInstance メソッドを使用しない限り。

例:

<?php

#[Attribute]
function foo()
{

}

ここでエラーが報告されます致命的エラー: 属性 "属性" は関数 (許可されたターゲット: クラス) をターゲットにできません 、組み込みアノテーション クラスの役割 スコープは 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 のアノテーションも継承します。また、C2C1foo メソッドをカバーしているため、アノテーションは存在しません。

推奨学習: 「

PHP8 チュートリアル

以上がphp8 アノテーションについてどれくらい知っていますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。