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
文件在你的代码,使作曲家提供的类加载机制。读这篇文章为更多的细节。
使用
另请参阅
这篇文章解释了如何使用EventDispatcher功能作为一个独立的组件在任何PHP应用程序。读了事件和事件监听器文章在Symfony应用程序了解如何使用它。ob娱乐下载
事件
事件时,由一个唯一的名称(如标识。kernel.response
),任意数量的听众可能听。一个事件实例也创造并传递给所有的听众。稍后您将看到,事件
对象本身通常包含的数据事件被派出。
命名约定
独特的事件名称可以是任何字符串,但是选择遵循一些命名约定:
- 只使用小写字母、数字、点(
。
)和下划线(_
); - 前缀名称和名称空间的一个点(如紧随其后。
订单。*
,用户。*
); - 终端名称动词,表示已经采取行动(如。
order.placed
)。
事件名称和事件对象
当调度员通知监听器,它通过一个实际的事件
侦听器对象。基地事件
类包含一个方法阻止事件传播,但别的就没什么了。
另请参阅
读作“通用事件对象“关于这个基础事件对象的更多信息。
通常,数据对一个特定的事件需要传递的事件
对象,这样听众所需的信息。在这种情况下,一个特殊的子类,额外的方法来检索和调度时首要的信息可以通过一个事件。例如,kernel.response
使用一个事件ResponseEvent,其中包含方法获得,甚至取代响应
对象。
分配器
分配器是中央对象事件的调度系统。一般来说,创建一个调度程序,维护一个侦听器注册。事件时派出通过分配器,它通知所有事件监听器注册:
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时执行。动作事件派遣});
一旦一个侦听器注册的调度程序,它将等待事件通知。在上面的例子中,当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 ()
在容器建造者:
1 2 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 (新ParameterBag ());/ /注册编译器通过处理“kernel.event_listener”/ /和内核。事件_订阅者' service tags美元容器- >addCompilerPass (新RegisterListenersPass ());美元容器- >注册(“event_dispatch”,EventDispatcher::类);/ /注册一个事件侦听器美元容器- >注册(“listener_service_id”,\ AcmeListener::类)- >addTag (“kernel.event_listener”,(“事件”= >“acme.foo.action”,“方法”= >“onFooAction”]);/ /注册事件订阅者美元容器- >注册(“subscriber_service_id”,\ AcmeSubscriber::类)- >addTag (“kernel.event_subscriber”);
RegisterListenersPass
解决别名类名称,例如可以通过完全限定类名是指一个事件(FQCN)事件的类。通过将读取别名专用容器参数的映射。这个参数可以延长注册另一个编译器,AddEventAliasesPass
:
1 2 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 (新ParameterBag ());美元容器- >addCompilerPass (新AddEventAliasesPass ([\ AcmeFooActionEvent::类= >“acme.foo.action”)));美元容器- >addCompilerPass (新PassConfig RegisterListenersPass ()::TYPE_BEFORE_REMOVING);美元容器- >注册(“event_dispatch”,EventDispatcher::类);/ /注册一个事件侦听器美元容器- >注册(“listener_service_id”,\ AcmeListener::类)- >addTag (“kernel.event_listener”,(/ /将翻译acme.foo。RegisterListenersPass行动”。“事件”= > \ AcmeFooActionEvent::类,“方法”= >“onFooAction”]);
请注意
请注意,AddEventAliasesPass
已经处理过吗RegisterListenersPass
。
听众通过假定事件调度器的服务idevent_dispatch
的事件监听器kernel.event_listener
标签,事件的用户kernel.event_subscriber
标签和别名映射存储参数event_dispatcher.event_aliases
。
创建和分派事件
除了与现有事件登记侦听器,您可以创建和分派自己的事件。这是非常有用的在创建第三方库,也当你想保持自己的系统的不同组件灵活和分离。
创建一个事件类
假设您希望创建一个新的事件order.placed
——这是派遣每次客户订单产品与您的应用程序。当调度这个事件时,你会通过一个定制的事件实例访问放置订单。首先创建这个定制事件类和记录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21日22日23日
名称空间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
事件:
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
名称空间Acme\商店\事件;使用Acme\商店\事件\OrderPlacedEvent;使用ob娱乐下载\组件\EventDispatcher\EventSubscriberInterface;使用ob娱乐下载\组件\HttpKernel\事件\ResponseEvent;使用ob娱乐下载\组件\HttpKernel\KernelEvents;类StoreSubscriber实现了EventSubscriberInterface{公共静态函数getSubscribedEvents(){返回[KernelEvents::响应= > [[“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美元调度程序){/ /……做一些与事件的名字吗}}