问候 PHP 属性
属性能够为代码中的声明(类、方法、函数、参数、属性和类常量)添加结构化、机器可读的元数据信息。
我认为这个定义很准确,并且我确信大多数阅读这篇文章的开发者至少遇到过一次属性。如果您还没有,它们本质上是为类添加的元数据。
到目前为止,您可能想知道它们与 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(),
]);
}
}
你可以看到这已经很容易理解了,在生产环境和测试环境中忽略。然而,我们还需要使这具有功能性,有几个方法可以实现这一点,最简单的是使用中间件。
让我们创建一个中间件,我将命名为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
响应,但在本地和测试环境中将正常工作。
之后,你可以在全局范围内或API路由中注册这个中间件,就像你通常做的那样,并且你可以通过标记属性来忽略路由。
结论
仅仅几行代码,我们就启用了可切换的路由。虽然实现相对简单,但这个示例旨在展示属性的力量。我的意思是,这有多酷?在你们选择的特定环境中切换路由的开关,你可以将Ignore
属性调整为仅从除你指定的环境外的所有环境中排除该路由,选项是无限的。
下次你考虑标记一个类为特定某物时,考虑试一下属性!🪄
driesvints, mohaaosman, muetze, ya27cine, josemalcher 喜欢这篇文章