Sintaks anotasi
#[Route] #[Route()] #[Route("/path", ["get"])] #[Route(path: "/path", methods: ["get"])]
Malah, sintaks sangat serupa dengan kelas instantiated, kecuali kata kunci new
tiada .
Perlu diambil perhatian bahawa nama anotasi tidak boleh menjadi pembolehubah, tetapi hanya boleh menjadi ungkapan malar atau malar
//实例化类 $route = new Route(path: "/path", methods: ["get"]);
(path: "/path", methods: ["get"])
ialah sintaks baharu daripada php8
, anda boleh menentukan nama parameter apabila menghantar parameter, dan jangan lulus parameter dalam susunan parameter formal.
Skop kelas anotasi
Apabila mentakrifkan kelas anotasi, anda boleh menggunakan kelas anotasi terbina dalam #[Attribute]
untuk menentukan skop kelas anotasi atau anda boleh meninggalkannya, ditentukan secara dinamik oleh PHP Gunakan senario untuk menentukan julat secara automatik .
Senarai skop anotasi:
- Atribut::TARGET_CLASS
- Atribut::TARGET_FUNCTION
- Atribut::TARGET_METHOD>
- Atribut::TARGET_CLASS_CONSTANT
- Attribute::TARGET_PARAMETER
- Atribut::TARGET_ALL
- IS_REPETribut: digunakan,
- bersamaan dengan Untuk kemudahan, yang pertama biasanya digunakan.
1~7 mudah difahami dan sepadan dengan kelas, fungsi, kaedah kelas, atribut kelas, pemalar kelas, parameter, dan semuanya masing-masing 6 item pertama boleh digabungkan sesuka hati menggunakan#[Attribute]
atau operator , seperti#[Attribute(Attribute::TARGET_ALL)]
. (
termasuk 6 item pertama, tetapi tidak termasuk |
Attribute::TARGET_CLASS | Attribute::TARGET_FUNCTION
). Attribute::TARGET_ALL
Attribute::IS_REPEATABLE
Tetapkan sama ada anotasi boleh diulang, contohnya:
Jika Attribute::IS_REPEATABLE
tidak ditetapkan,
class IndexController { #[Route('/index')] #[Route('/index_alias')] public function index() { echo "hello!world" . PHP_EOL; } }
Seperti yang dinyatakan di atas, jika skop tidak dinyatakan, PHP akan menentukan skop secara dinamik. Bagaimana anda memahaminya? Contoh: Attribute::IS_REPEATABLE
Route
tidak menggunakan kelas anotasi terbina dalam
untuk mentakrifkan skop, jadi apabila ia mengubah suai kelas<?php class Deprecated { } class NewLogger { public function newLogAction(): void { //do something } #[Deprecated('oldLogAction已废弃,请使用newLogAction代替')] public function oldLogAction(): void { } } #[Deprecated('OldLogger已废弃,请使用NewLogger代替')] class OldLogger { }
. Apabila ia mengubah suai kaedah Deprecated
, skopnya ditakrifkan secara dinamik sebagai #[Attribute]
. OldLogger
Dalam satu ayat, di mana sahaja ia diubah suai, skopnya juga adaTARGET_CLASS
oldLogAction
TARGET_METHOD
Perlu diingatkan bahawa selepas menetapkan skop, semasa fasa penyusunan, sebagai tambahan kepada bina- dalam kelas anotasi , kelas anotasi tersuai tidak akan menyemak skop secara automatik. Melainkan anda menggunakan kaedah
. #[Attribute]
ReflectionAttribute
Contoh: newInstance
, kerana skop kelas anotasi terbina dalam ialah
dan hanya boleh digunakan untuk ubah suai kelas dan bukan fungsi<?php #[Attribute] function foo() { }
, jadi Fatal error: Attribute "Attribute" cannot target function (allowed targets: class)
tidak boleh diubah suai berulang kali. TARGET_CLASS
Kelas anotasi tersuai tidak akan menyemak skop semasa penyusunan. TARGET_CLASS
<?php #[Attribute(Attribute::TARGET_CLASS)] class A1 { } #[A1] function foo() {}
Apabila menggunakan
, skop yang ditentukan akan berkuat kuasa sama ada skop yang ditakrifkan oleh kelas anotasi adalah konsisten dengan skop yang diubah suai sebenar tidak akan diuji.<?php #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION | Attribute::IS_REPEATABLE)] class Route { protected $handler; public function __construct( public string $path = '', 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[] = ['name' => $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) { } } }
Anda tidak boleh
menggunakan sintaks- dalam senarai parameter kelas anotasi.
-
unpack
Walaupun ia lulus dalam peringkat penghuraian leksikal, ralat akan dilemparkan dalam peringkat penyusunan.
<?php class IndexController { #[Route(...["/index", ["get"]])] public function index() { } }
- Anotasi boleh digunakan dalam kumpulan
<?php class IndexController { #[Route( "/index", ["get"] )] public function index() { } }
- Warisan anotasi
<?php class IndexController { #[Route( "/index", ["get"] ), Other, Another] public function index() { } }
- Anotasi boleh diwarisi atau ditindih.
mewarisi kaedah
<?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()));
. Dan C3
merangkumi kaedah C1
foo
, jadi anotasi tidak wujud. foo
C2
C1
Pembelajaran yang disyorkan: "foo
Tutorial PHP8