文档/事件
事件
HLEB2 框架中有几个预定义的常规事件,每个事件都分配给特定的动作类型。
所有事件类都位于 /app/Bootstrap/Events/ 文件夹中,可以进行修改。从技术上讲,它们替代了配置,消除了项目中不必要的“魔法”。
由于这些类与全局事件相关联,建议将依赖于私有实现的代码分离到单独的类中。
未优化的事件中的代码可能会导致项目整体性能下降。
#ControllerEvent
此类的 before() 方法在框架的每次控制器调用之前执行。它允许您确定涉及哪个类和方法,并在需要时更改作为命名数组提供的参数,并将它们返回到被调用的控制器方法。
例如,如果使用第三方库验证传入请求,可以通过 ControllerEvent 事件实现此检查。
如果存在,after() 方法允许重定义控制器的响应,并在控制器之后立即执行。该方法通过引用在 'result' 参数中接收此结果,允许您更改特定类和方法的返回数据。
全局上,这可能涉及将返回的数组从默认的 JSON 更改为另一个格式,如 XML。
以下示例演示了在调用特定类和控制器方法之前附加额外操作:
<?php
declare(strict_types=1);
namespace App\Bootstrap\Events;
use Hleb\Base\Event;
final class ControllerEvent extends Event
{
public function before(string $class, string $method, array $arguments): array|false
{
switch([$class, $method]) {
case [ExampleController::class, 'index']:
return (new ExampleControllerEvent())->beforeIndex($arguments);
default:
}
return $arguments;
}
public function after(string $class, string $method, mixed &$result): void
{
}
}
#MiddlewareEvent
这个中间件类的 before() 方法在框架的每次 middleware 调用之前执行。该方法的参数允许您确定涉及哪个类和方法,以及此 middleware 是否在主要操作之后执行。
如有必要,可以更改目标 middleware 方法的参数,进行更改,并从当前方法返回。如果是这种情况,需要定义条件以便在结果输出后终止脚本执行,可以通过从 after() 方法返回 false 来实现。
中间件的执行顺序可以通过路由改变,这在为其分配事件时必须考虑到,如有必要,可将依赖于执行顺序的事件元素替换为相应的独立中间件。
#ModuleEvent
由于模块是独立存在的,每个模块的控制器都有自己的事件。
ModuleEvent 类的 before() 方法在框架中任何模块的每次控制器调用之前执行。
与 ControllerEvent 不同,增加了一个额外的参数 $module 来确定模块名称。
与控制器事件相似,此事件也可以有一个 after() 方法。
#PageEvent
这是另一个类似于 ControllerEvent 的事件,绑定到特殊“页面控制器”的调用。
此类页面用于框架的注册库中的管理面板以及此文档网站上。
#KernelEvent
KernelEvent 事件不一定需要存在于与其他事件相同的文件夹中,但如果创建了具有此类名的文件,框架将会使用它。其独特功能是能够在最高级别拦截所有 Web 请求并为其创建全局操作。例如,这可以用于记录用户请求日志(框架中最初并未包含此功能)。
<?php
declare(strict_types=1);
namespace App\Bootstrap\Events;
use Hleb\Base\Event;
use Hleb\Reference\Interface\Log;
use Hleb\Reference\Interface\Request;
class KernelEvent extends Event
{
#[\Override]
public function __construct(
private readonly Log $log,
private readonly Request $request,
#[\SensitiveParameter] array $config = [],
) {
parent::__construct($config);
}
public function before(): bool
{
$data = [
'url' => $this->request->getAddress() . $this->request->getUri()->getQuery(),
'method' => $this->request->getMethod(),
];
$this->log->info('Request log for the site, url: {url} method: {method}', $data);
return true;
}
}
#TaskEvent
在每次框架命令启动之前执行,排除了那些默认内置的命令。
还可以确定被调用的类和调用的来源(来自代码或来自控制台)。
TaskEvent 接收并返回最终方法的参数的最终数据,因此可以在此处连接第三方库。
例如,这可以是来自 Symfony 的标准控制台处理程序。
这个事件的 after() 方法的不同之处在于它可以访问任务中设置为 setResult() 的数据。
这些数据通过引用传递给 'result' 参数,并且可以被修改。
如有必要,可以通过使用 statusCode() 方法以相同的方式更改返回的响应状态。
演示示例,展示了一种组织响应到不同任务的执行(使用一个通用接口)的方法:
<?php
declare(strict_types=1);
namespace App\Bootstrap\Events;
use Hleb\Base\Event;
final class TaskEvent extends Event
{
private ?TaskEventInterface $action = null;
public function before(string $class, string $method, array $args): array
{
switch ($class) {
case FirstTask::class:
$this->action = new FirstTaskEvent($method);
break;
case SecondTask::class:
$this->action = new SecondTaskEvent($method);
break;
default:
}
return $this->action ? $this->action->getBeforeAction($args) : $args;
}
public function after(string $class, string $method, mixed &$result): void
{
$this->action and $result = $this->action->updateAfterAction($result);
}
public function statusCode(string $class, string $method, int $code): int
{
return $this->action ? $this->action->getCode($code) : $code;
}
}
此原则不仅可以应用于任务事件,也可以应用于其他事件。
为事件选择 switch 操作符是因为它能够将一个结果与多个 case 块匹配。
#扩展条件
可以根据其他条件分配关联的动作,例如,通过 namespace 中的一般组:
if (str_starts_with($class, 'App\\Controllers\\Api\\')) {
}
此外,事件类继承自 Hleb\Base\Container,这使得它们可以使用容器中的服务。
在事件类的构造函数中也可以通过 Dependency Injection 获取这些服务。
使用的可能性没有限制,当然要保持代码的可读性和优化。
下面是如何为特定的类和方法设置基于 HTTP 请求方法的条件:
if ([$class, $method] === [MainController::class, 'index'] && $this->request()->isMethod('GET')) {
}
← 控制台命令
介绍 →
页面翻译:chatgpt 4-o