php8 アノテーションについてどれくらい知っていますか?

藏色散人
リリース: 2023-02-17 12:08:01
転載
4906 人が閲覧しました

アノテーション構文

#[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 までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート