EventDispatcher组件
编辑本页EventDispatcher组件
EventDispatcher组件提供了一些工具,允许应用程序组件通过分派事件和监听事件来相互通信。
简介
面向对象的代码在确保代码可扩展性方面已经走了很长的路。通过创建具有良好定义的职责的类,您的代码变得更加灵活,开发人员可以使用子类来扩展它们以修改它们的行为。但是如果他们想要与其他已经创建了自己的子类的开发人员共享这些更改,那么代码继承就不再是解决方案了。
考虑一个现实世界的例子,你想为你的项目提供一个插件系统。一个插件应该能够添加方法,或者在一个方法执行之前或之后做一些事情,而不干扰其他插件。用单一继承解决这个问题并不容易,即使PHP可以实现多重继承,它也有自己的缺点。
Symfob娱乐下载ony EventDispatcher组件实现了中介而且观察者设计模式使所有这些事情成为可能,并使您的项目真正可扩展。
举个例子HttpKernel组件.一次响应
对象已经创建,在实际使用它之前,允许系统中的其他元素修改它(例如添加一些缓存头)可能是有用的。为了实现这一点,Symfony内核抛出一个事件-ob娱乐下载kernel.response
.下面是它的工作原理:
- 一个侦听器(PHP对象)告诉一个中心调度程序对象所要侦听的对象
kernel.response
事件; - 在某些时候,Symfony内核会告诉ob娱乐下载调度程序对象来分派
kernel.response
事件,和它一起传递事件
对象,该对象可以访问响应
对象; - 调度程序通知(即在上调用一个方法)所有侦听器
kernel.response
事件,允许它们中的每一个对响应
对象。
安装
1
$作曲家需要symfony/事件调度ob娱乐下载程序
请注意
如果在Symfony应用程序外部安装此组件,则必须要求ob娱乐下载供应商/ autoload.php
文件,以启用Composer提供的类自动加载机制。读这篇文章欲知详情。
使用
另请参阅
本文解释了如何在任何PHP应用程序中使用EventDispatcher特性作为独立组件。读了事件和事件监听器文章,以了解如何在Symfony应用程序中使用它。ob娱乐下载
事件
当一个事件被分派时,它被一个唯一的名称标识(例如;kernel.response
),任何数量的听众都可能在听。一个事件实例也被创建并传递给所有侦听器。稍后您将看到事件
对象本身通常包含关于正在分派的事件的数据。
命名约定
唯一的事件名称可以是任何字符串,但可以选择遵循一些命名约定:
- 只使用小写字母、数字、点(
.
)和下划线(_
); - 前缀名称,名称空间后面跟着一个点(例如:
订单。*
,用户。*
); - 用动词来结束名称,表明采取了什么行动(例如;
order.placed
).
事件名称和事件对象
当调度程序通知侦听器时,它传递一个实际的事件
对象指向这些侦听器。基地事件
类包含用于停止的方法事件传播,但仅此而已。
另请参阅
读作“通用事件对象,以获取有关此基本事件对象的更多信息。
通常,有关特定事件的数据需要与事件
对象,以便侦听器拥有所需的信息。在这种情况下,可以在分派事件时传递一个特殊的子类,该类具有用于检索和覆盖信息的其他方法。例如,kernel.response
事件使用ResponseEvent的方法,其中包含获取甚至替换响应
对象。
分配器
调度程序是事件调度程序系统的中心对象。通常,只创建一个分派器,它维护侦听器的注册表。当一个事件通过dispatcher被分派时,它会通知所有用该事件注册的侦听器:
1 2 3
使用ob娱乐下载\组件\EventDispatcher\EventDispatcher;$调度程序=新EventDispatcher ();
连接监听器
要利用现有事件,需要将侦听器连接到调度程序,以便在事件被调度时通知它。打给调度员的电话addListener ()
方法将任何可调用的有效PHP关联到一个事件:
1 2
$侦听器=新AcmeListener ();$调度程序->addListener (“acme.foo.action”, ($侦听器,“onFooAction”]);
的addListener ()
方法最多包含三个参数:
- 监听器想要监听的事件名称(字符串);
- 一个PHP可调用对象,当指定的事件被分派时执行;
- 可选优先级,定义为正整数或负整数(默认为
0
).该数字越高,侦听器被调用的时间越早。如果两个侦听器具有相同的优先级,它们将按照添加到调度程序的顺序执行。
请注意
一个PHP调用是PHP变量,可以由call_user_func ()
函数和返回值真正的
当传递给is_callable ()
函数。它可以是关闭\
实例,一个实现__invoke ()
方法(实际上就是闭包),表示函数的字符串或表示对象方法或类方法的数组。
到目前为止,您已经看到了如何将PHP对象注册为侦听器。你也可以注册PHP闭包作为事件监听器:
1 2 3 4 5
使用ob娱乐下载\合同\EventDispatcher\事件;$调度程序->addListener (“acme.foo.action”,函数(事件$事件){//将在acme.foo.action事件被分派时执行});
侦听器一旦向调度程序注册,它就会等待,直到通知事件为止。在上面的例子中,当acme.foo.action
事件被分派时,调度程序调用AcmeListener: onFooAction ()
方法,并将事件
对象作为单个参数:
1 2 3 4 5 6 7 8 9 10 11
使用ob娱乐下载\合同\EventDispatcher\事件;类AcmeListener{/ /……公共函数onFooAction(事件$事件){/ /……做某事}}
的美元的事件
参数是分派事件时传递的事件对象。在许多情况下,传递带有额外信息的特殊事件子类。您可以检查每个事件的文档或实现,以确欧宝官网下载app定传递的是哪个实例。
在服务容器中注册事件监听器和订阅者
方法注册服务定义并为其标记kernel.event_listener
而且kernel.event_subscriber
标记不足以启用事件侦听器和事件订阅者。您还必须注册一个名为RegisterListenersPass ()
在容器构建器中:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
使用ob娱乐下载\组件\DependencyInjection\ContainerBuilder;使用ob娱乐下载\组件\DependencyInjection\ParameterBag\ParameterBag;使用ob娱乐下载\组件\DependencyInjection\参考;使用ob娱乐下载\组件\EventDispatcher\DependencyInjection\RegisterListenersPass;使用ob娱乐下载\组件\EventDispatcher\EventDispatcher;$containerBuilder=新ContainerBuilder (新ParameterBag ());//注册处理'kernel.event_listener'的编译器//和'内核。事件_订阅者' service tags$containerBuilder->addCompilerPass (新RegisterListenersPass ());$containerBuilder->注册(“event_dispatch”, EventDispatcher::类);//注册一个事件监听器$containerBuilder->注册(“listener_service_id”, \ AcmeListener::类)->addTag (“kernel.event_listener”, (“事件”= >“acme.foo.action”,“方法”= >“onFooAction”]);//注册事件订阅者$containerBuilder->注册(“subscriber_service_id”, \ AcmeSubscriber::类)->addTag (“kernel.event_subscriber”);
RegisterListenersPass
解析别名类名,例如允许通过事件类的全限定类名(FQCN)引用事件。该传递将从专用容器参数读取别名映射。该参数可以通过注册另一个编译器通道来扩展,AddEventAliasesPass
:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
使用ob娱乐下载\组件\DependencyInjection\编译器\PassConfig;使用ob娱乐下载\组件\DependencyInjection\ContainerBuilder;使用ob娱乐下载\组件\DependencyInjection\ParameterBag\ParameterBag;使用ob娱乐下载\组件\DependencyInjection\参考;使用ob娱乐下载\组件\EventDispatcher\DependencyInjection\AddEventAliasesPass;使用ob娱乐下载\组件\EventDispatcher\DependencyInjection\RegisterListenersPass;使用ob娱乐下载\组件\EventDispatcher\EventDispatcher;$containerBuilder=新ContainerBuilder (新ParameterBag ());$containerBuilder->addCompilerPass (新AddEventAliasesPass ([\ AcmeFooActionEvent::类= >“acme.foo.action”)));$containerBuilder->addCompilerPass (新PassConfig RegisterListenersPass ()::TYPE_BEFORE_REMOVING);$containerBuilder->注册(“event_dispatch”, EventDispatcher::类);//注册一个事件监听器$containerBuilder->注册(“listener_service_id”, \ AcmeListener::类)->addTag (“kernel.event_listener”, (//将被翻译成'acme.foo。action'由RegisterListenersPass。“事件”= > \ AcmeFooActionEvent::类,“方法”= >“onFooAction”]);
请注意
请注意,AddEventAliasesPass
必须处理之前RegisterListenersPass
.
监听器传递假设事件调度程序的服务id为event_dispatch
时,事件监听器被标记为kernel.event_listener
标记,则事件订阅者将被标记为kernel.event_subscriber
标记,并且别名映射存储为参数event_dispatcher.event_aliases
.
创建和分派事件
除了用现有事件注册侦听器外,还可以创建和分派自己的事件。这在创建第三方库时很有用,也在您希望保持自己系统的不同组件的灵活性和去耦时很有用。
创建事件类
假设您想要创建一个新的事件-order.placed
—每次客户使用您的应用程序订购产品时,都会发送该命令。调度此事件时,您将传递一个自定义事件实例,该实例可以访问所放置的订单。首先创建这个自定义事件类并记录它:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
名称空间Acme\商店\事件;使用Acme\商店\订单;使用ob娱乐下载\合同\EventDispatcher\事件;/** *订单。每次在系统中创建订单*时,都会分派放置事件。* /类OrderPlacedEvent扩展事件{公共常量NAME =“order.placed”;受保护的$订单;公共函数__construct(订单$订单){$这->订单=$订单;}公共函数getOrder():订单{返回$这->秩序;}}
命令,每个侦听器现在都可以访问该订单getOrder ()
方法。
请注意
如果不需要向事件侦听器传递任何额外数据,也可以使用默认值事件类。在这种情况下,您可以在泛型中记录事件及其名称StoreEvents
类,类似于KernelEvents类。
分派事件
的调度()方法通知给定事件的所有侦听器。它需要两个参数:事件
实例传递给该事件的每个侦听器,以及要分派的事件的名称:
1 2 3 4 5 6 7 8 9 10
使用Acme\商店\事件\OrderPlacedEvent;使用Acme\商店\订单;//以某种方式创建或检索订单$订单=新订单();/ /……//创建OrderPlacedEvent并分派它$事件=新OrderPlacedEvent ($订单);$调度程序->调度($事件, OrderPlacedEvent::的名字);
注意特殊的OrderPlacedEvent
对象创建并传递给调度()
方法。的监听器order.placed
事件将接收OrderPlacedEvent
.
使用事件订阅者
监听事件最常见的方法是注册事件监听器和调度员通话。此侦听器可以侦听一个或多个事件,并在每次分派这些事件时得到通知。
监听事件的另一种方法是通过事件订阅者.事件订阅者是一个PHP类,它能够准确地告诉调度程序应该订阅哪些事件。它实现了EventSubscriberInterface接口,它需要调用一个静态方法getSubscribedEvents ().属性的订阅方的示例如下kernel.response
而且order.placed
事件:
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
名称空间Acme\商店\事件;使用Acme\商店\事件\OrderPlacedEvent;使用ob娱乐下载\组件\EventDispatcher\EventSubscriberInterface;使用ob娱乐下载\组件\HttpKernel\事件\ResponseEvent;使用ob娱乐下载\组件\HttpKernel\KernelEvents;类StoreSubscriber实现了EventSubscriberInterface{公共静态函数getSubscribedEvents(){返回[KernelEvents::Response => [[“onKernelResponsePre”,10]、[“onKernelResponsePost”,-10],], OrderPlacedEvent::NAME = >“onStoreOrder”,);}公共函数onKernelResponsePre(ResponseEvent$事件){/ /……}公共函数onKernelResponsePost(ResponseEvent$事件){/ /……}公共函数onStoreOrder(OrderPlacedEvent$事件){/ /……}}
这与侦听器类非常相似,只是类本身可以告诉调度程序它应该侦听哪些事件。要向调度程序注册订阅者,请使用addSubscriber ()方法:
1 2 3 4 5
使用Acme\商店\事件\StoreSubscriber;/ /……$订阅者=新StoreSubscriber ();$调度程序->addSubscriber ($订阅者);
类返回的每个事件,调度程序将自动注册订阅者getSubscribedEvents ()
方法。该方法返回一个由事件名称索引的数组,其值要么是要调用的方法名,要么是由要调用的方法名和优先级(默认为的正整数或负整数)组成的数组0
).
上面的例子展示了如何为订阅者中的同一个事件注册多个侦听器方法,还展示了如何传递每个侦听器方法的优先级。数值越高,方法就越早被调用。在上面的例子中,当kernel.response
事件触发时,方法onKernelResponsePre ()
而且onKernelResponsePost ()
按此顺序调用。
停止事件流/传播
在某些情况下,侦听器阻止任何其他侦听器被调用可能是有意义的。换句话说,侦听器需要能够告诉调度程序停止向未来的侦听器传播事件(即不再通知任何侦听器)。可以在侦听器内部通过stopPropagation ()方法:
1 2 3 4 5 6 7 8
使用Acme\商店\事件\OrderPlacedEvent;公共函数onStoreOrder(OrderPlacedEvent$事件){/ /……$事件->stopPropagation ();}
现在,有听众order.placed
还没有被称为意志不被称为。
方法可以检测事件是否已停止isPropagationStopped ()方法,返回一个布尔值:
1 2 3 4 5
/ /……$调度程序->调度($事件,“foo.event”);如果($事件->isPropagationStopped ()) {/ /……}
EventDispatcher感知事件和监听器
的EventDispatcher
始终将已分派的事件、事件名称和对自身的引用传递给侦听器。这可以导致一些高级的应用程序EventDispatcher
包括在侦听器中调度其他事件,将事件链接,甚至将侦听器延迟加载到调度器对象中。
事件名称自检
的EventDispatcher
实例,以及被分派的事件的名称,作为参数传递给监听器:
1 2 3 4 5 6 7 8 9 10
使用ob娱乐下载\合同\EventDispatcher\事件;使用ob娱乐下载\合同\EventDispatcher\EventDispatcherInterface;类MyListener{公共函数myEventListener(事件$事件、字符串$eventName, EventDispatcherInterface$调度程序){/ /……对事件名称做些什么}}