EventDispatcher组件

编辑该页面

警告:你浏览的文档欧宝官网下载appob娱乐下载Symfony 2.4,不再维护。

这个页面的更新版本Symfob娱乐下载ony 6.2(当前的稳定版本)。

EventDispatcher组件

EventDispatcher组件提供了工具,让你的应用程序组件相互通信调度事件,听他们。

介绍

面向对象代码已经很长一段路要保证代码的可扩展性。通过创建类,有明确的责任,你的代码变得更加灵活,开发人员可以使用子类扩展他们修改他们的行为。但是如果他们想要与其他开发人员共享的变化也有自己的子类,代码继承不再是答案。

考虑到现实世界的例子,你想为你的项目提供一个插件系统。插件可以添加方法,或做一些之前或之后执行一个方法,而不干扰其他插件。这不是一个容易解决的问题与单继承和多重继承与PHP(如果可能)都有自己的缺点。

Symfob娱乐下载ony的EventDispatcher组件实现中介模式在一个简单的和有效的方式使这一切成为可能,才能真正地让你的项目的可扩展性。

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

  • 一个侦听器(PHP对象)讲述了一个中央调度程序对象,它想听kernel.response事件;
  • 在某些时候,Symfony内核告诉ob娱乐下载调度程序对象来调度kernel.response事件,通过一个事件访问的对象响应对象;
  • 调度员通知(即调用一个方法)的所有听众kernel.response事件,让他们每个人修改响应对象。

安装

您可以安装组件在两个不同的方面:

使用

事件

事件时,由一个唯一的名称(如标识。kernel.response),任意数量的听众可能听。一个事件实例也创造并传递给所有的听众。稍后您将看到,事件对象本身通常包含的数据事件被派出。

命名约定

独特的事件名称可以是任何字符串,但选择遵循几个简单的命名约定:

  • 只使用小写字母、数字、点()和下划线(_);
  • 前缀名称和名称空间的一个点(如紧随其后。内核。);
  • 终端名称动词,表示正在采取行动(如。请求)。

这里有一些例子好事件名称:

  • kernel.response
  • form.pre_set_data

事件名称和事件对象

当调度员通知监听器,它通过一个实际的事件侦听器对象。基地事件类非常简单:它包含一个方法阻止事件传播,但别的就没什么了。

通常,数据对一个特定的事件需要传递的事件对象,这样听众需要的信息。在的情况下kernel.response事件,事件对象的创建和传递给每个侦听器实际上是类型FilterResponseEvent,底部的一个子类事件对象。这个类包含的方法等getResponsesetResponse,让听众甚至取代响应对象。

这个故事的寓意是:在创建一个事件侦听器,事件对象传递给侦听器可能是一个特殊的子类,有额外的方法获取信息并响应事件。

分配器

分配器是中央对象事件的调度系统。一般来说,创建一个调度程序,维护一个侦听器注册。事件时派出通过分配器,它通知所有事件监听器注册:

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

连接监听器

利用现有的事件,你需要将一个侦听程序连接到调度程序,这样就可以将通知事件。调用调度程序addListener ()方法将任何有效的PHP调用一个事件:

1 2
美元侦听器=AcmeListener ();美元调度程序- >addListener (“foo.action”,数组(美元侦听器,“onFooAction”));

addListener ()方法有三个参数:

  • 事件名称(字符串),这个听者想听;
  • 一个PHP调用将通知事件时抛出它听;
  • 优先一个可选的整型(高=更重要,因此,侦听器将被触发)早些时候决定当一个倾听者和其他听众(默认为触发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实现决定的ob娱乐下载Symfony \ EventDispatcher \ \组件的事件实例被传递。例如,kernel.response事件经过的一个实例ob娱乐下载\组件\ HttpKernel\事件\ FilterResponseEvent:

1 2 3 4 5 6 7 8 9
使用ob娱乐下载\组件\HttpKernel\事件\FilterResponseEvent;公共函数onKernelResponse(FilterResponseEvent美元事件){美元响应=美元事件- >getResponse ();美元请求=美元事件- >getRequest ();/ /……}

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

1 2 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娱乐下载\组件\DependencyInjection\ContainerBuilder;使用ob娱乐下载\组件\DependencyInjection\定义;使用ob娱乐下载\组件\DependencyInjection\ParameterBag\ParameterBag;使用ob娱乐下载\组件\DependencyInjection\参考;使用ob娱乐下载\组件\HttpKernel\DependencyInjection\RegisterListenersPass;美元containerBuilder=ContainerBuilder (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”,美元订阅者);

默认情况下,听众通过假定事件调度器的服务idevent_dispatch的事件监听器kernel.event_listener标签和事件的用户kernel.event_subscriber标签。您可以更改这些默认值通过自定义值的构造函数RegisterListenersPass

创建和分派事件

除了与现有事件登记侦听器,您可以创建和分派自己的事件。这是非常有用的在创建第三方库,也当你想保持自己的系统的不同组件灵活和分离。

静态事件

假设您希望创建一个新的事件store.order——这是派遣每次在您的应用程序创建一个订单。为了保持组织,首先创建一个StoreEvents类在您的应用程序,用于定义和记录你的活动:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
名称空间Acme\StoreBundle;最后StoreEvents{/ * * *商店。订单事件is thrown each time an order is created * in the system. * * The event listener receives an * Acme\StoreBundle\Event\FilterOrderEvent instance. * *@var字符串* /常量STORE_ORDER =“store.order”;}

请注意,这个类并不实际任何东西。的目的StoreEvents类只是一个位置可以集中常见的事件信息。注意到一个特殊的FilterOrderEvent类将被传递给每个侦听器。

创建一个事件对象

之后,当你发送这个新的事件,您将创建一个事件实例并将其传递给调度程序。然后分派器将这个实例的每个事件的侦听器。如果你不需要传递任何信息到你的听众,你可以使用默认值ob娱乐下载Symfony \ EventDispatcher \ \组件的事件类。然而,大多数时候,你需要传递的信息事件侦听器。为此,您将创建一个新类,它扩展了ob娱乐下载Symfony \ EventDispatcher \ \组件的事件

在这个例子中,每个侦听器将需要访问一些假装订单对象。创建一个事件类,使这一切成为可能:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
名称空间Acme\StoreBundle\事件;使用ob娱乐下载\组件\EventDispatcher\事件;使用Acme\StoreBundle\订单;FilterOrderEvent扩展事件{受保护的美元订单;公共函数__construct(订单美元订单){美元- >订单=美元订单;}公共函数getOrder(){返回美元- >秩序;}}

现在可以访问每个侦听器订单对象通过getOrder方法。

调度事件

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

1 2 3 4 5 6 7 8 9 10 11
使用Acme\StoreBundle\StoreEvents;使用Acme\StoreBundle\订单;使用Acme\StoreBundle\事件\FilterOrderEvent;/ /订单创建或检索美元订单=订单();/ /……/ /创建FilterOrderEvent和调度美元事件=FilterOrderEvent (美元订单);美元调度程序- >调度(StoreEvents::STORE_ORDER,美元事件);

注意,特别FilterOrderEvent创建对象并传递到调度方法。现在,任何侦听器store.order事件将会收到FilterOrderEvent和访问订单对象通过getOrder方法:

1 2 3 4 5 6 7 8
/ /注册的侦听器类”商店。秩序”事件使用Acme\StoreBundle\事件\FilterOrderEvent;公共函数onStoreOrder(FilterOrderEvent美元事件){美元订单=美元事件- >getOrder ();/ /做某事或秩序}

使用事件订阅者

听一个事件最常见的方法是注册一个事件监听器调度程序。此侦听器可以听一个或多个事件和通知每次派遣这些事件。

听事件的另一种方法是通过一个事件订阅者。事件订阅是一个PHP类,能够确切地告诉调度员,事件应该订阅。它实现了EventSubscriberInterface接口,这就需要一个静态方法调用getSubscribedEvents。采取以下一个订户订阅的例子kernel.responsestore.order事件:

1 2 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 29 30 31 32 33 34 35 36 37 38 39
名称空间Acme\StoreBundle\事件;使用ob娱乐下载\组件\EventDispatcher\EventSubscriberInterface;使用ob娱乐下载\组件\HttpKernel\事件\FilterResponseEvent;StoreSubscriber实现了EventSubscriberInterface{公共静态函数getSubscribedEvents(){返回数组(“kernel.response”= >数组(数组(“onKernelResponsePre”,10),数组(“onKernelResponseMid”,5),数组(“onKernelResponsePost”,0),),“store.order”= >数组(“onStoreOrder”,0),);}公共函数onKernelResponsePre(FilterResponseEvent美元事件){/ /……}公共函数onKernelResponseMid(FilterResponseEvent美元事件){/ /……}公共函数onKernelResponsePost(FilterResponseEvent美元事件){/ /……}公共函数onStoreOrder(FilterOrderEvent美元事件){/ /……}}

这非常类似于一个侦听器类,除了类本身可以告诉调度员事件应该听。与调度程序,注册一个用户使用addSubscriber ()方法:

1 2 3 4
使用Acme\StoreBundle\事件\StoreSubscriber;美元订阅者=StoreSubscriber ();美元调度程序- >addSubscriber (美元订阅者);

调度程序将自动返回的每个事件的注册用户getSubscribedEvents方法。这个方法返回一个数组索引的事件名称和值是方法名称调用或一个数组组成的方法名和优先级。上面的例子展示了如何在用户注册多个侦听器方法相同的事件,也显示了如何通过每个侦听器方法的优先级。优先级越高,越早的方法。在上面的例子中,当kernel.response事件触发的方法onKernelResponsePre,onKernelResponseMid,onKernelResponsePost被称为这个顺序。

停止事件流/传播

在某些情况下,可能有一个侦听器,以防止其他听众被称为。换句话说,听者必须能够告诉调度员停止所有传播未来的事件侦听器(即不再通知侦听器)。这可以从一个侦听器通过内部完成stopPropagation ()方法:

1 2 3 4 5 6 7 8
使用Acme\StoreBundle\事件\FilterOrderEvent;公共函数onStoreOrder(FilterOrderEvent美元事件){/ /……美元事件- >stopPropagation ();}

现在,任何听众store.order尚未被称为被称为。

它可以检测事件是否停止使用isPropagationStopped ()方法返回一个布尔值:

1 2 3 4
美元调度程序- >调度(“foo.event”,美元事件);如果(美元事件- >isPropagationStopped ()) {/ /……}

EventDispatcher了解事件和监听器

2.4

因为Symfob娱乐下载ony 2.4,目前事件名称和EventDispatcher本身作为附加参数传递给听众。

EventDispatcher总是通过派遣事件,事件名称和引用自己的听众。这个可以用在一些高级的用法EventDispatcher像其他分派的事件监听器、事件链接甚至更多听众进入分配器对象的延迟加载,如以下示例所示。

延迟加载的听众:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21日22日23日
使用ob娱乐下载\组件\EventDispatcher\事件;使用ob娱乐下载\组件\EventDispatcher\EventDispatcherInterface;使用Acme\StoreBundle\事件\StoreSubscriber;喷火{私人美元开始=;公共函数myLazyListener(事件美元事件,美元eventName,EventDispatcherInterface美元调度程序){如果(= = =美元- >开始){美元订阅者=StoreSubscriber ();美元调度程序- >addSubscriber (美元订阅者);}美元- >开始=真正的;/ /……更多的代码}}

从内部调度另一个事件侦听器:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
使用ob娱乐下载\组件\EventDispatcher\事件;使用ob娱乐下载\组件\EventDispatcher\EventDispatcherInterface;喷火{公共函数myFooListener(事件美元事件,美元eventName,EventDispatcherInterface美元调度程序){美元调度程序- >调度(“日志”,美元事件);/ /……更多的代码}}

虽然这上面是足够的对于大多数使用,如果您的应用程序使用多个EventDispatcher情况下,您可能需要专门的注入一个已知实例EventDispatcher到你的听众。这可以通过使用构造函数或setter注入如下:

构造函数注入:

1 2 3 4 5 6 7 8 9 10 11
使用ob娱乐下载\组件\EventDispatcher\EventDispatcherInterface;喷火{受保护的美元调度程序=;公共函数__construct(EventDispatcherInterface美元调度程序){美元- >调度程序=美元调度程序;}}

或setter注入:

1 2 3 4 5 6 7 8 9 10 11
使用ob娱乐下载\组件\EventDispatcher\EventDispatcherInterface;喷火{受保护的美元调度程序=;公共函数setEventDispatcher(EventDispatcherInterface美元调度程序){美元- >调度程序=美元调度程序;}}

两者之间的选择是一个真正的味道。许多倾向于构造函数注入在施工时完全初始化的对象。但是当你有一长串的依赖关系,可以使用setter注入的路要走,尤其是对可选的依赖关系。

调度程序快捷方式

EventDispatcher:调度方法总是返回一个事件对象。这允许各种各样的捷径。例如如果一个人不需要一个定制的事件对象,一个可以依靠一个简单的事件对象。你甚至不需要通过这个调度程序,因为它将创建一个默认情况下,除非你特别通行证:

1
美元调度程序- >调度(“foo.event”);

此外,EventDispatcher总是返回派出任何一个事件对象,即通过事件或由调度员在内部创建的事件。这允许好快捷键:

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

或者:

1 2
美元barEvent=BarEvent ();美元酒吧=美元调度程序- >调度(“bar.event”,美元barEvent)- >getBar ();

或者:

1
美元酒吧=美元调度程序- >调度(“bar.event”,BarEvent ())- >getBar ();

等等……

事件名自省

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

事件名称,(与任何其他数据在一个定制的事件对象)可作为侦听器的处理逻辑:

1 2 3 4 5 6 7 8 9
使用ob娱乐下载\组件\EventDispatcher\事件;喷火{公共函数myEventListener(事件美元事件){回声美元事件- >getName ();}}

其他调度程序

除了常用的EventDispatcher2,组件附带其他调度程序:

这项工作,包括代码示例,许可下Creative Commons冲锋队3.0许可证。