概述
php8 中新增了註解特性,官方文件地址如下:
https://www。php。net/manual/zh/language。attributes。php
本文將使用這個新特性實現一個路由註解
實現過程
一、宣告一個註解類
<?
php
namespace
App
\
Route
\
Attributes
;
use
Attribute
;
// 宣告該Route註解可以重複使用,只能繫結在類方法上
#[Attribute
(
Attribute
::
IS_REPEATABLE
|
Attribute
::
TARGET_METHOD
)
]
class
Route
{
public
static
$all
=
[];
public
string
$path
=
“”
;
// 路由地址
public
string
$method
=
“GET”
;
// 路由請求方法
public
string
$controller
=
“”
;
// 路由指向的控制器
public
string
$function
=
“”
;
// 路由指向的控制器的方法
public
function
__construct
(
)
{
}
/**
*
@description
: 設定路由地址
*
@param
string $path
*
@return
self
*/
public
function
setPath
(
string
$path
)
:
self
{
$this
->
path
=
$path
;
return
$this
;
}
/**
*
@description
: 設定控制器
*
@param
string $controller
*
@return
self
*/
public
function
setController
(
string
$controller
)
:
self
{
$this
->
controller
=
$controller
;
return
$this
;
}
/**
*
@description
: 設定請求方法
*
@param
string $method
*
@return
self
*/
public
function
setMethod
(
string
$method
)
:
self
{
$this
->
method
=
$method
;
return
$this
;
}
/**
*
@description
: 設定繫結的function
*
@param
string $function
*
@return
self
*/
public
function
setFunction
(
string
$function
)
:
self
{
$this
->
function
=
$
function
;
return
$this
;
}
/**
*
@description
: 新增路由
*
@return
void
*/
public
function
addRoute
(
)
:
void
{
self
::
$all
[]
=
$this
;
}
}
二、使用註解類
<?
php
namespace
App
\
Controller
;
use
App
\
Route
\
Attributes
\
Route
;
class
TestController
{
#[Route
(
‘/test/route’
,
‘GET’
)
]
#[Route
(
‘/route/test’
,
‘POST’
)
]
public
function
testRoute
(
)
{
// do something here
}
}
三、使註解類生效
利用反射讀取註解的引數,並執行註解類裡面的方法
<?
php
function
getAttributeData
(
$reflection
)
{
$controller
=
$reflection
->
getName
();
// 如果註解是直接繫結在類上面,那麼直接從類的反射獲取註解
// $attributes = $reflection->getAttributes(App\Route\Attributes\Route::class);
$methods
=
$reflection
->
getMethods
();
// 因為註解是繫結在方法上的,因此迴圈方法,獲取方法的註解
foreach
(
$methods
as
$method
){
// 方法名
$function
=
$method
->
getName
();
// 指定獲取某個註解的資料
$attributes
=
$method
->
getAttributes
(
App
\Route\Attributes\Route
::
class
);
foreach
(
$attributes
as
$attribute
)
{
// 拿到一個新的 Route 例項
$route
=
$attribute
->
newInstance
();
// 拿到註解上的引數
$params
=
$attribute
->
getArguments
();
/**
* [
* [‘/test/route’,‘GET’],
* [‘/route/test’,‘POST’]
* ]
*/
// 執行路由新增
$route
->
setController
(
$controller
)->
setFunction
(
$function
)
->
setPath
(
$params
[
0
])->
setMethod
(
$params
[
1
])->
addRoute
();
}
}
}
// 對 TestController 使用反射,並執行註解生效的方法
getAttributeData
(
new
ReflectionClass
(
App
\Controller\TestController
::
class
));
var_dump
(
App
\Route\Attributes\Route
::
$all
);
// 執行結果 :
// array(2) {
// [0]=>
// object(App\Route\Attributes\Route)#5 (4) {
// [“path”]=>
// string(11) “/test/route”
// [“method”]=>
// string(3) “GET”
// [“controller”]=>
// string(29) “App\Controller\TestController”
// [“function”]=>
// string(9) “testRoute”
// }
// [1]=>
// object(App\Route\Attributes\Route)#6 (4) {
// [“path”]=>
// string(11) “/route/test”
// [“method”]=>
// string(4) “POST”
// [“controller”]=>
// string(29) “App\Controller\TestController”
// [“function”]=>
// string(9) “testRoute”
// }
// }
注意事項
註解的引數是用反射讀取的,可以直接反射類,而沒必要使用例項
註解可以在很多個位置,類、方法、屬性等,在哪裡使用註解就反射到哪裡再使用
getAttributes()
方法
註解使用
newInstance()
方法獲得註解類的例項
註解使用
getArguments()
方法獲得註解上的引數
註解語法參考官方文件,不外乎幾種形式,用於生成註解類的引數
零基礎學PHP
陳浩
計算機
免費閱讀
https://www。php。net/manual/zh/language。attributes。syntax。php
問題/總結
在 使註解類生效 一節中,在另一個檔案中寫了一個方法,用於讓註解生效,但是在實際使用過程中它應該放到什麼位置呢?
PHP 入門經典
李慧
計算機
免費閱讀
可以看到,在該方法中,對目標類進行了反射,也就是說,要使註解生效,我們至少要提前知曉我們要反射的類(或方法)是哪一個。
以本文中的路由註解為例,一般的PHP框架中都會有顯式路由的使用方式,例如:
<?
php
// laravel
Route
::
any
(
‘/test/route’
,
‘TestController::testRoute’
);
也就是說,一般情況下,路由表是提前生成的,或者在執行之前就能拿到的。
那麼至少可以使用兩種方式使路由註解生效:
直接使用command的遍歷所有控制器,生成這個路由檔案,再在執行程式碼中引入,並快取路由
在路由生效之前,遍歷所有的控制器,將路由表
收集
出來,動態地註冊路由
** 如果有在路由之後生效的註解,可以在路由執行到方法之前進行反射,得到引數並執行 **
篇幅有限,未對各種實現方式的執行效率進行測試,想來php8中有JIT生效的時候執行效率不會低
以上僅為一些簡單的實現和思考,如果有更好的實現方式歡迎交流