EventDispatcher组件
编辑本页警告:您正在浏览的文档欧宝官网下载appob娱乐下载Symfony 5.2,现已不再维护。
读本页的更新版本用于Syob娱乐下载mfony 6.2(当前稳定版本)。
EventDispatcher组件
我们的框架仍然缺少一个好的框架的主要特征:可扩展性.可扩展意味着开发人员应该能够与框架生命周期挂钩,以修改处理请求的方式。
我们说的是什么样的钩子?例如身份验证或缓存。为了灵活,钩子必须是即插即用的;根据您的具体需求,您为应用程序“注册”的对象与下一个对象不同。许多软件都有类似的概念,如Drupal或Wordpress。在一些语言中,甚至有一个标准的likeWSGI使用Python或架在Ruby中。
由于PHP没有标准,我们将使用一种众所周知的设计模式中介,允许任何类型的行为附加到我们的框架;Symfob娱乐下载ony EventDispatcher组件实现了这个模式的轻量级版本:
1
$作曲家需要symfony/事件调度ob娱乐下载程序
它是如何工作的?的调度程序事件分派器系统的中心对象听众一个事件派出去了。换句话说:代码将事件分派给分派器,分派器通知所有已注册的侦听器该事件,每个侦听器对该事件做任何想做的事情。
例如,让我们创建一个侦听器,透明地将谷歌Analytics代码添加到所有响应中。
为了让它工作,框架必须在返回Response实例之前分派一个事件:
12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
/ / example.com/src/Simplex/Framework.php名称空间单纯形;使用ob娱乐下载\组件\EventDispatcher\EventDispatcher;使用ob娱乐下载\组件\HttpFoundation\请求;使用ob娱乐下载\组件\HttpFoundation\响应;使用ob娱乐下载\组件\HttpKernel\控制器\ArgumentResolverInterface;使用ob娱乐下载\组件\HttpKernel\控制器\ControllerResolverInterface;使用ob娱乐下载\组件\路由\异常\ResourceNotFoundException;使用ob娱乐下载\组件\路由\匹配器\UrlMatcherInterface;类框架{私人$调度程序;私人$匹配器;私人$controllerResolver;私人$argumentResolver;公共函数__construct(EventDispatcher$调度程序, UrlMatcherInterface$匹配器, ControllerResolverInterface$controllerResolver, ArgumentResolverInterface$argumentResolver){$这->调度程序=$调度程序;$这->匹配器=$匹配器;$这->controllerResolver =$controllerResolver;$这->argumentResolver =$argumentResolver;}公共函数处理(请求$请求){$这->匹配器->getContext ()->fromRequest ($请求);试一试{$请求->属性->add ($这->匹配器->匹配($请求->getPathInfo ()));$控制器=$这->controllerResolver->getController ($请求);$参数=$这->argumentResolver->getArguments ($请求,$控制器);$响应=中的call_user_func_array ($控制器,$参数);}抓(ResourceNotFoundException$异常) {$响应=新响应(“没有找到”,404);}抓(\异常$异常) {$响应=新响应(“发生错误”,500);}//发送一个响应事件$这->调度程序->调度(新ResponseEvent ($响应,$请求),“响应”);返回$响应;}}
每当框架处理一个Request时,一个ResponseEvent
事件现在被分派:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/ / example.com/src/Simplex/ResponseEvent.php名称空间单纯形;使用ob娱乐下载\组件\HttpFoundation\请求;使用ob娱乐下载\组件\HttpFoundation\响应;使用ob娱乐下载\合同\EventDispatcher\事件;类ResponseEvent扩展事件{私人$请求;私人$响应;公共函数__construct(反应$响应,请求$请求){$这->响应=$响应;$这->请求=$请求;}公共函数getResponse(){返回$这->反应;}公共函数getRequest(){返回$这->请求;}}
对象的侦听器的注册和调度程序的创建是最后一步响应
事件:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/ / example.com/web/front.phprequire_once__DIR__.“/ . . /供应商/ autoload.php”;/ /……使用ob娱乐下载\组件\EventDispatcher\EventDispatcher;$调度程序=新EventDispatcher ();$调度程序->addListener (“响应”,函数(单纯形\ ResponseEvent$事件){$响应=$事件->getResponse ();如果($响应->isRedirection() ||$响应->头->有(“内容类型”) & &假= = =(大小写敏感$响应->头->get (“内容类型”),“html”) | |“html”= = !$事件->getRequest ()->getRequestFormat()) {返回;}$响应->setContent ($响应->getContent()。“GA代码”);});$controllerResolver=新ControllerResolver ();$argumentResolver=新ArgumentResolver ();$框架=新单纯形\框架($调度程序,$匹配器,$controllerResolver,$argumentResolver);$响应=$框架->处理($请求);$响应->send ();
请注意
侦听器只是一个概念的证明,您应该在body标记之前添加谷歌Analytics代码。
如你所见,addListener ()
将有效的PHP回调关联到命名事件(响应
);事件名称必须与调度()
调用。
在侦听器中,只有当响应不是重定向、请求格式是HTML且响应内容类型是HTML(这些条件说明了从代码中操作请求和响应数据的容易程度)时,我们才添加谷歌Analytics代码。
到目前为止一切顺利,但是让我们在同一事件上添加另一个侦听器。假设我们想要设置内容长度
如果尚未设置,则为:
1 2 3 4 5 6 7 8
$调度程序->addListener (“响应”,函数(单纯形\ ResponseEvent$事件){$响应=$事件->getResponse ();$头=$响应->头;如果(!$头->有(内容长度的) && !$头->有(“传输编码”)) {$头->集(内容长度的strlen ($响应->getContent ()));}});
的值是正确的还是错误的,这取决于您是在上一个侦听器注册之前还是之后添加了这段代码内容长度
头。有时,侦听器的顺序很重要,但默认情况下,所有侦听器都以相同的优先级注册,0
.要告诉调度程序尽早运行侦听器,请将优先级更改为正数;负数可以用于低优先级的侦听器。这里,我们想要内容长度
侦听器最后执行,因此将优先级更改为-255年
:
1 2 3 4 5 6 7 8
$调度程序->addListener (“响应”,函数(单纯形\ ResponseEvent$事件){$响应=$事件->getResponse ();$头=$响应->头;如果(!$头->有(内容长度的) && !$头->有(“传输编码”)) {$头->集(内容长度的strlen ($响应->getContent ()));}},-255年);
提示
在创建框架时,考虑优先级(例如,为内部侦听器保留一些数字)并彻底记录它们。
让我们通过移动谷歌监听器到它自己的类来重构代码:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/ / example.com/src/Simplex/GoogleListener.php名称空间单纯形;类GoogleListener{公共函数onResponse(ResponseEvent$事件){$响应=$事件->getResponse ();如果($响应->isRedirection() ||$响应->头->有(“内容类型”) & &假= = =(大小写敏感$响应->头->get (“内容类型”),“html”) | |“html”= = !$事件->getRequest ()->getRequestFormat()) {返回;}$响应->setContent ($响应->getContent()。“GA代码”);}}
对另一个听众做同样的事情:
12 3 4 5 6 7 8 9 10 11 12 13 14 15
/ / example.com/src/Simplex/ContentLengthListener.php名称空间单纯形;类ContentLengthListener{公共函数onResponse(ResponseEvent$事件){$响应=$事件->getResponse ();$头=$响应->头;如果(!$头->有(内容长度的) && !$头->有(“传输编码”)) {$头->集(内容长度的strlen ($响应->getContent ()));}}}
我们的前端控制器现在看起来应该如下所示:
1 2 3
$调度程序=新EventDispatcher ();$调度程序->addListener (“响应”, (新单纯形\ ContentLengthListener (),“onResponse”),-255年);$调度程序->addListener (“响应”, (新单纯形\ GoogleListener (),“onResponse”]);
即使代码现在很好地包装在类中,仍然存在一个小问题:优先级的知识是“硬编码”在前端控制器中,而不是在侦听器本身中。对于每个应用程序,您必须记住设置适当的优先级。此外,侦听器方法名也暴露在这里,这意味着重构侦听器将意味着更改依赖于这些侦听器的所有应用程序。解决这一困境的方法是使用订阅者而不是监听者:
1 2 3
$调度程序=新EventDispatcher ();$调度程序->addSubscriber (新单纯形\ ContentLengthListener ());$调度程序->addSubscriber (新单纯形\ GoogleListener ());
订阅程序知道它感兴趣的所有事件,并将此信息通过getSubscribedEvents ()
方法。看看新版本的GoogleListener
:
12 3 4 5 6 7 8 9 10 11 12 13 14
/ / example.com/src/Simplex/GoogleListener.php名称空间单纯形;使用ob娱乐下载\组件\EventDispatcher\EventSubscriberInterface;类GoogleListener实现了EventSubscriberInterface{/ /……公共静态函数getSubscribedEvents(){返回[“响应”= >“onResponse”];}}
这是新版的ContentLengthListener
:
12 3 4 5 6 7 8 9 10 11 12 13 14
/ / example.com/src/Simplex/ContentLengthListener.php名称空间单纯形;使用ob娱乐下载\组件\EventDispatcher\EventSubscriberInterface;类ContentLengthListener实现了EventSubscriberInterface{/ /……公共静态函数getSubscribedEvents(){返回[“响应”= > [“onResponse”,-255年]];}}
提示
单个订阅者可以在所需的任意事件上托管任意数量的侦听器。
为了使你的框架真正灵活,不要犹豫添加更多的事件;为了让它更棒,添加更多的监听器。同样,这本书不是关于创建一个通用的框架,而是根据您的需要量身定制的框架。在您认为合适的时候停止,并从那里进一步发展代码。