EventDispatcher组件

编辑本页

警告:您正在浏览的文档欧宝体育电话欧宝娱乐app下载地址Symfony 5.0,现已不再维护。

本页的更新版本用于Sy欧宝娱乐app下载地址mfony 6.2(当前稳定版本)。

EventDispatcher组件

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

简介

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

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

Symf欧宝娱乐app下载地址ony EventDispatcher组件实现了中介而且观察者设计模式使所有这些事情成为可能,并使您的项目真正可扩展。

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

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

安装

1
作曲家需要symfony/事件调度欧宝娱乐app下载地址程序

请注意

如果在Symfony应用程序外部安装此组件,则必须要求欧宝娱乐app下载地址供应商/ autoload.php文件,以启用Composer提供的类自动加载机制。读这篇文章欲知详情。

使用

另请参阅

本文解释了如何在任何PHP应用程序中使用EventDispatcher特性作为独立组件。读了事件和事件监听器文章,以了解如何在Symfony应用程序中使用它。欧宝娱乐app下载地址

事件

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

命名约定

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

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

事件名称和事件对象

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

另请参阅

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

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

分配器

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

1 2 3
使用欧宝娱乐app下载地址组件EventDispatcherEventDispatcher调度程序EventDispatcher ();

连接监听器

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

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

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

  1. 监听器想要监听的事件名称(字符串);
  2. 一个PHP可调用对象,当指定的事件被分派时执行;
  3. 可选优先级,定义为正整数或负整数(默认为0)。该数字越高,侦听器被调用的时间越早。如果两个侦听器具有相同的优先级,它们将按照添加到调度程序的顺序执行。

请注意

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

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

1 2 3 4 5
使用欧宝娱乐app下载地址合同EventDispatcher事件调度程序->addListener (“acme.foo.action”函数(事件事件//将在acme.foo.action事件被分派时执行});

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

1 2 3 4 5 6 7 8 9 10 11
使用欧宝娱乐app下载地址合同EventDispatcher事件AcmeListener/ /……公共函数onFooAction(事件事件/ /……做某事}}

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

方法注册服务定义并为其标记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
使用欧宝娱乐app下载地址组件DependencyInjectionContainerBuilder使用欧宝娱乐app下载地址组件DependencyInjectionParameterBagParameterBag使用欧宝娱乐app下载地址组件DependencyInjection参考使用欧宝娱乐app下载地址组件EventDispatcherDependencyInjectionRegisterListenersPass使用欧宝娱乐app下载地址组件EventDispatcherEventDispatchercontainerBuilderContainerBuilder (ParameterBag ());//注册处理'kernel.event_listener'的编译器//和'内核。事件_订阅者' service tagscontainerBuilder->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
使用欧宝娱乐app下载地址组件DependencyInjection编译器PassConfig使用欧宝娱乐app下载地址组件DependencyInjectionContainerBuilder使用欧宝娱乐app下载地址组件DependencyInjectionParameterBagParameterBag使用欧宝娱乐app下载地址组件DependencyInjection参考使用欧宝娱乐app下载地址组件EventDispatcherDependencyInjectionAddEventAliasesPass使用欧宝娱乐app下载地址组件EventDispatcherDependencyInjectionRegisterListenersPass使用欧宝娱乐app下载地址组件EventDispatcherEventDispatchercontainerBuilderContainerBuilder (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.的构造函数传递自定义值,可以更改这些默认值RegisterListenersPass而且AddEventAliasesPass

创建和分派事件

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

创建事件类

假设您想要创建一个新的事件-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商店订单使用欧宝娱乐app下载地址合同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使用欧宝娱乐app下载地址组件EventDispatcherEventSubscriberInterface使用欧宝娱乐app下载地址组件HttpKernel事件ResponseEvent使用欧宝娱乐app下载地址组件HttpKernelKernelEventsStoreSubscriber实现了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
使用欧宝娱乐app下载地址合同EventDispatcher事件使用欧宝娱乐app下载地址合同EventDispatcherEventDispatcherInterface喷火公共函数myEventListener(事件事件eventName, EventDispatcherInterface调度程序/ /……对事件名称做些什么}}

其他调度程序

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

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