EventDispatcher组件

编辑本页

警告:您正在浏览的文档欧宝官网下载appob娱乐下载Symfony 2.3,现已不再维护。

本页的更新版本用于Syob娱乐下载mfony 6.2(当前稳定版本)。

EventDispatcher组件

EventDispatcher组件提供了一些工具,允许应用程序组件通过分派事件和监听事件来相互通信。

简介

面向对象的代码在确保代码可扩展性方面已经走了很长的路。通过创建具有良好定义的职责的类,您的代码变得更加灵活,开发人员可以使用子类来扩展它们以修改它们的行为。但是如果他们想要与其他已经创建了自己的子类的开发人员共享这些更改,那么代码继承就不再是解决方案了。

考虑一个现实世界的例子,你想为你的项目提供一个插件系统。一个插件应该能够添加方法,或者在一个方法执行之前或之后做一些事情,而不干扰其他插件。这不是一个简单的问题来解决单继承,即使
虽然多重继承在PHP中是可能的,但它也有自己的缺点。

Symfob娱乐下载ony EventDispatcher组件实现了中介以一种简单而有效的方式进行模式设计,使所有这些事情成为可能,并使您的项目真正可扩展。

举一个简单的例子HttpKernel组件.一次响应对象已经创建,在实际使用它之前,允许系统中的其他元素修改它(例如添加一些缓存头)可能是有用的。为了实现这一点,Symfony内核抛出一个事件-ob娱乐下载kernel.response.下面是它的工作原理:

  • 一个侦听器(PHP对象)告诉一个中心调度程序对象所要侦听的对象kernel.response事件;
  • 在某些时候,Symfony内核会告诉ob娱乐下载调度程序对象来分派kernel.response事件,和它一起传递事件对象,该对象可以访问响应对象;
  • 调度程序通知(即在上调用一个方法)所有侦听器kernel.response事件,允许它们中的每一个对响应对象。

安装

你可以用两种不同的方式安装组件:

然后,要求供应商/ autoload.php文件以启用Composer提供的自动加载机制。否则,您的应用程序将无法找到这个Symfony组件的类。ob娱乐下载

使用

事件

当一个事件被分派时,它被一个唯一的名称标识(例如;kernel.response),任何数量的听众都可能在听。一个事件实例也被创建并传递给所有侦听器。稍后您将看到事件对象本身通常包含关于正在分派的事件的数据。

命名约定

唯一的事件名称可以是任何字符串,但可以选择遵循一些简单的命名约定:

  • 只使用小写字母、数字、点()和下划线(_);
  • 前缀名称,名称空间后面跟着一个点(例如:秩序。用户。*);
  • 用动词来结束名称,表明采取了什么行动(例如;order.placed).

事件名称和事件对象

当调度程序通知侦听器时,它传递一个实际的事件对象指向这些侦听器。基地事件类非常简单:它包含一个用于停止的方法事件传播,但仅此而已。

另请参阅

读作“通用事件对象,以获取有关此基本事件对象的更多信息。

通常,有关特定事件的数据需要与事件对象,以便侦听器拥有所需的信息。在这种情况下,可以在分派事件时传递一个特殊的子类,该类具有用于检索和覆盖信息的其他方法。例如,kernel.response事件使用FilterResponseEvent的方法,其中包含获取甚至替换响应对象。

分配器

调度程序是事件调度程序系统的中心对象。通常,只创建一个分派器,它维护侦听器的注册表。当一个事件通过dispatcher被分派时,它会通知所有用该事件注册的侦听器:

1 2 3
使用ob娱乐下载组件EventDispatcherEventDispatcher调度程序EventDispatcher ();

连接监听器

要利用现有事件,需要将侦听器连接到调度程序,以便在事件被调度时通知它。打给调度员的电话addListener ()方法将任何可调用的有效PHP关联到一个事件:

1 2
侦听器AcmeListener ();调度程序->addListener (“acme.action”数组侦听器“onFooAction”));

addListener ()方法最多包含三个参数:

  1. 监听器想要监听的事件名称(字符串);
  2. 一个PHP可调用对象,当指定的事件被分派时执行;
  3. 一个可选的优先级整数(更高的等于更重要的,因此侦听器将更早被触发),它决定何时触发一个侦听器而不是其他侦听器(默认为0).如果两个侦听器具有相同的优先级,它们将按照添加到调度程序的顺序执行。

请注意

一个PHP调用是PHP变量,可以由call_user_func ()函数和返回值真正的当传递给is_callable ()函数。它可以是关闭\实例,一个实现__invoke方法(实际上就是闭包),表示函数的字符串或表示对象方法或类方法的数组。

到目前为止,您已经看到了如何将PHP对象注册为侦听器。你也可以注册PHP闭包作为事件监听器:

1 2 3 4 5
使用ob娱乐下载组件EventDispatcher事件调度程序->addListener (“foo.action”函数(事件事件//当foo. //分派动作事件。});

侦听器一旦向调度程序注册,它就会等待,直到通知事件为止。在上面的例子中,当foo.action事件被分派时,调度程序调用AcmeListener: onFooAction ()方法,并将事件对象作为单个参数:

1 2 3 4 5 6 7 8 9 10 11
使用ob娱乐下载组件EventDispatcher事件AcmeListener/ /……公共函数onFooAction(事件事件/ /……做某事}}

美元的事件参数是分派事件时传递的事件类。在许多情况下,传递带有额外信息的特殊事件子类。您可以检查每个事件的文档或实现,以确欧宝官网下载app定传递的是哪个实例。

当你使用ContainerAwareEventDispatcherDependencyInjection组件,你可以使用RegisterListenersPass从HttpKernel组件中将服务标记为事件监听器:

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
使用ob娱乐下载组件DependencyInjectionContainerBuilder使用ob娱乐下载组件DependencyInjection定义使用ob娱乐下载组件DependencyInjectionParameterBagParameterBag使用ob娱乐下载组件DependencyInjection参考使用ob娱乐下载组件HttpKernelDependencyInjectionRegisterListenersPasscontainerBuilderContainerBuilder (ParameterBag ());containerBuilder->addCompilerPass (RegisterListenersPass ());//注册事件调度程序服务containerBuilder->setDefinition (“event_dispatch”定义(“ob娱乐下载Symfony \ \ EventDispatcher \ ContainerAwareEventDispatcher组件”数组引用(“service_container”))));//注册你的事件监听器服务侦听器定义(“AcmeListener”);侦听器->addTag (“kernel.event_listener”数组“事件”= >“foo.action”“方法”= >“onFooAction”));containerBuilder->setDefinition (“listener_service_id”侦听器);//注册事件订阅者订阅者定义(“AcmeSubscriber”);订阅者->addTag (“kernel.event_subscriber”);containerBuilder->setDefinition (“subscriber_service_id”订阅者);

默认情况下,监听器传递假设事件调度程序的服务id为event_dispatch时,事件监听器被标记为kernel.event_listener标记,并且事件订阅者被标记为kernel.event_subscriber标签。的构造函数传递自定义值,可以更改这些默认值RegisterListenersPass

创建和分派事件

除了用现有事件注册侦听器外,还可以创建和分派自己的事件。这在创建第三方库时很有用,也在您希望保持自己系统的不同组件的灵活性和去耦时很有用。

创建事件类

假设您想要创建一个新的事件-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商店事件使用ob娱乐下载组件EventDispatcher事件使用Acme商店订单/** *订单。每次在系统中创建订单*时,都会分派放置事件。* /OrderPlacedEvent扩展事件常量NAME =“order.placed”受保护的订单公共函数__construct(订单订单->订单=订单;}公共函数getOrder()返回->秩序;}}

命令,每个侦听器现在都可以访问该订单getOrder ()方法。

请注意

如果不需要向事件侦听器传递任何额外数据,也可以使用默认值事件类。在这种情况下,您可以在泛型中记录事件及其名称StoreEvents类,类似于KernelEvents类。

分派事件

调度()方法通知给定事件的所有侦听器。它有两个参数:要分派的事件的名称和事件实例传递给该事件的每个侦听器:

1 2 3 4 5 6 7 8 9 10
使用Acme商店订单使用Acme商店事件OrderPlacedEvent//以某种方式创建或检索订单订单订单();/ /……//创建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商店事件使用ob娱乐下载组件EventDispatcherEventSubscriberInterface使用ob娱乐下载组件HttpKernel事件FilterResponseEvent使用ob娱乐下载组件HttpKernelKernelEvents使用Acme商店事件OrderPlacedEventStoreSubscriber实现了EventSubscriberInterface公共静态函数getSubscribedEvents()返回数组(KernelEvents::响应= >数组数组“onKernelResponsePre”10),数组“onKernelResponsePost”-10),), OrderPlacedEvent::NAME = >“onStoreOrder”,);}公共函数onKernelResponsePre(FilterResponseEvent事件/ /……公共函数onKernelResponsePost(FilterResponseEvent事件/ /……公共函数onStoreOrder(OrderPlacedEvent事件/ /……}}

这与侦听器类非常相似,只是类本身可以告诉调度程序它应该侦听哪些事件。要向调度程序注册订阅者,请使用addSubscriber ()方法:

1 2 3 4 5
使用Acme商店事件StoreSubscriber/ /……订阅者StoreSubscriber ();调度程序->addSubscriber (订阅者);

类返回的每个事件,调度程序将自动注册订阅者getSubscribedEvents ()方法。该方法返回一个由事件名称索引的数组,其值要么是要调用的方法名,要么是由要调用的方法名和优先级组成的数组。上面的例子展示了如何为订阅者中的同一个事件注册多个侦听器方法,还展示了如何传递每个侦听器方法的优先级。优先级越高,就越早调用方法。在上面的例子中,当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对象,该对象通过传入的事件对象的getDispatcher ()方法。

这可以导致一些高级的应用程序EventDispatcher包括在侦听器中调度其他事件,将事件链接,甚至将侦听器延迟加载到调度器对象中。

调度程序快捷方式

如果不需要自定义事件对象,则可以简单地依赖于plain事件对象。你甚至不需要把它传递给分派器,因为它会默认创建一个,除非你特别传递一个:

1
调度程序->调度(“order.placed”);

此外,事件分派器总是返回被分派的事件对象,即传递的事件或由分派器内部创建的事件。这允许很好的快捷方式:

1 2 3
如果(!调度程序->调度(“foo.event”->isPropagationStopped ()) {/ /……

或者:

1 2
事件OrderPlacedEvent (订单);订单调度程序->调度(“bar.event”事件->getOrder ();

等等。

事件名称自检

EventDispatcher调度时已经知道事件的名称,则事件名称也会被注入到事件对象,使事件侦听器可以通过getName ()方法。

事件名称(与自定义事件对象中的任何其他数据一样)可以用作侦听器处理逻辑的一部分:

1 2 3 4 5 6 7 8 9
使用ob娱乐下载组件EventDispatcher事件喷火公共函数myEventListener(事件事件/ /……对事件名称做些什么}}

其他调度程序

除了常用的EventDispatcher,该组件附带了一些其他的调度程序:

此工作,包括代码示例,是根据创作共用BY-SA 3.0许可证。