Laravel 中的 PHP 注解 attributes
Attributes 提供了在代码的声明中添加结构化、机器可读的元数据信息的能力:类、方法、函数、参数、属性和类常量可以是属性的目标。
我相信这个定义是正确的,我相信阅读本文的大多数开发人员都至少遇到过一次 attributes 。如果没有,它们本质上是添加到类中的元数据。
在这一点上,你可能想知道它们与 PHPDOCs 有何不同?好吧,他们是一流的公民,他们是真正的 PHP 类,是的,我知道,现在它改变了整个游戏;您不必编写正则表达式来从 PHPDocs 提取内容,你甚至可以在属注解性中维护某种形式的状态。
使路由可切换
与团队合作时,我经常收到来自其他开发人员(前端人员)的消息,通知我某个路由没有如预期生效。有时,我希望可以很容易地为特定的环境禁用路由,比如临时环境,同时在本地维护其功能。这样,我和我的后端开发人员同事就可以处理它,推送代码,并维护我们的通常的工作流程,而不用担心意外使用。有时候,它仅仅是一个新的路由,需要保持测试环境的专用。
所以,在思考这个问题时,我想如果我能将一个动作标记为禁用或忽略,那就太酷了。你猜怎么着?有了注解,这变得超级容易,也超级干净。
让我们从创建一个注解开始。我将其命名为 Ignore
,它将有一个名为 in
的属性
<?php
namespace App\Attributes;
use Attribute;
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class Ignore
{
public function __construct(
public array $in = ['production']
) {
}
}
就这样,你刚刚创建了一个注解,你还会注意到,我们将其范围限制为类和方法,允许将该注解专门放置在这两个实体上。
现在,我们可以这样使用它
namespace App\Http\Controllers;
use App\Attributes\Ignore;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Symfony\Component\HttpFoundation\Response
class TwoFactorQrCodeController extends Controller
{
#[Ignore(in: ['production', 'staging'])]
public function show(Request $request): Response
{
if (is_null($request->user()->two_factor_secret)) {
return [];
}
return response()->json([
'svg' => $request->user()->twoFactorQrCodeSvg(),
'url' => $request->user()->twoFactorQrCodeUrl(),
]);
}
}
come可以从字面上理解,在生产和阶段中忽略它。尽管如此,我们还是需要使其发挥作用,而且实现这一点的方法很少,最简单的是使用中间件。
让我们创建一个中间件,我将其命名为 IsRouteIgnored
,可以随意选择您喜欢的任何名称
php artisan make:middleware IsRouteIgnored
现在我们可以实现逻辑了,想法很简单:我们拦截使用该中间件的路由的请求,然后检查该操作是否具有 Ignore
属性,如果有,则检查当前环境是否允许具有该路由。
为此,我们将使用反射 API 的魔力,让我们深入代码
<?php
namespace App\Http\Middleware;
use Closure;
use ReflectionMethod;
use App\Attributes\Ignore;
use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use Symfony\Component\HttpFoundation\Response;
class IsRouteIgnored
{
public function handle(Request $request, Closure $next): Response
{
$route = $request->route();
if (!($route instanceof Route) || $route->action['uses'] instanceof Closure) {
return $next($request);
}
$reflection = new ReflectionMethod($route->getControllerClass(), $route->getActionMethod());
$attributes = $reflection->getAttributes(Ignore::class);
if (!empty($attributes) && in_array(config('app.env'), $attributes[0]->newInstance()->in)) {
abort(404);
}
return $next($request);
}
}
我们正在创建路由指向的方法的反射,因此检索 Ignore
注解。默认情况下,注解是不可重复的,这意味着每个实体只能使用一次注解。由于我们只在 Ignore
注解中指定我们感兴趣的内容,因此我们最终会得到一个元素数组。
现在,我们可以通过调用 newInstance()
来实例化注解,返回到常规类的领域。然后,我们可以在 in
注解中检查应该忽略此路由的环境。在这种情况下,该路由将为生产和阶段环境返回 404 响应,但将在本地和测试环境中发挥作用。
之后,您可以全局注册中间件或在 AP I路由中注册中间件,就像你常做的那样,并且您可以通过用注解标记路由来开始忽略路由。
结论
只需几行代码,我们就启用了可切换的路由。虽然实现是相对基本的,但该示例旨在展示注解的力量。在我们选择的特定环境中切换路由,您甚至可以调整 Ignore
属性,将路由从除指定环境之外的所有环境中排除,而且选项是无穷无尽的。
下次当你考虑把一个类标记为特定的东西时,考虑给 Attributes 一个机会!