Ein paar Fragen müssen zunächst geklärt werden:
F1. Was ist ein Ereignis?
A: Ein Ereignis ist ein benanntes Verhalten. Wenn dieses Verhalten auftritt, wird das Ereignis als ausgelöst bezeichnet.
F2. Was ist ein Monitor?
A: Der Zuhörer bestimmt den logischen Ausdruck des Ereignisses und wird durch das Ereignis ausgelöst. Zuhörer und Ereignisse werden oft gepaart. Natürlich kann ein Ereignis auch mehreren Zuhörern entsprechen. Zuhörer reagieren auf Ereignisse. Wenn ein Ereignis ausgelöst wird, ist es die Aufgabe des Zuhörers, darauf zu reagieren. Auf diese Weise kann das Auslösen mehrerer Ereignisse dazu führen, dass ein einzelner Zuhörer reagiert. Auf ein Ereignis können auch mehrere Zuhörer reagieren. (Mit einem Wort: Die Beziehung zwischen Zuhörern und Ereignissen kann entweder eins-zu-viele oder viele-zu-eins sein)
F3. Wofür wird der Eventmanager verwendet?
A: Der Event Manager (EventManager) wird, wie der Name schon sagt, zum Verwalten von Ereignissen verwendet. Doch wie schafft er es? Event-Manager aggregieren häufig mehrere Listener für mehrere Ereignisse (die Ereignisse und Listener sind hier beide unbestimmt [das heißt, es kann eines oder mehrere sein]). Der Eventmanager ist auch für das Auslösen von Events verantwortlich.
Im Allgemeinen verwenden wir Objekte, um Ereignisse darzustellen. Ein Ereignisobjekt beschreibt die Grundelemente eines Ereignisses, einschließlich wann und wie das Ereignis ausgelöst wird.
Über die Grundelemente des Ereignisses: Ereignisname, Ziel (das Objekt, das das Ereignis auslöst, normalerweise das Ereignisobjekt selbst) und Ereignisparameter. Wir haben bereits gesagt, dass ein Ereignis einem Verhalten entspricht. In Programmen verwenden wir häufig Methoden oder Funktionen, um Verhalten darzustellen. Daher sind Ereignisparameter häufig auch Funktionsparameter.
Auch zu Shared Managern: Wie bereits erwähnt, kann ein Ereignis mehrere Zuhörer ansprechen. Dies wird durch Shared Manager erreicht. Die Implementierung von EventManager umfasst (kombiniert) SharedEventManagerInterface [unter Verwendung von Code-Injection im Konstruktor oder setSharedManager, siehe Quellcode für Details]) und SharedEventManagerInterface beschreibt ein Objekt, das Listener nur mit Objekten mit angegebenen Identitätssymbolen verbindet Ereignis. SharedEventManager löst keine Ereignisse aus, sondern stellt lediglich Listener bereit und stellt eine Verbindung zu Ereignissen her. EventManger ruft Listener mit bestimmten Bezeichnern ab, indem es SharedEventMangaer abfragt.
Mehrere wichtige Verhaltensweisen in EventManager:
1. Ein Ereignis erstellen : Beim Erstellen eines Ereignisses wird eigentlich nur eine Instanz von EventManagerInterface erstellt
2. Triggerereignis : Im Allgemeinen wird Trigger im Ereignisverhalten verwendet, sodass das Ereignis direkt ausgelöst werden kann, wenn wir das Verhalten ausführen. Funktionsprototyp: trigger($eventName,$target=null,$argv=[]); $eventName ist im Allgemeinen der Name der Zeitaktion (häufig ersetzt durch __FUNCTION__), $target ist das Ereignisobjekt selbst und kann durch $this ersetzt werden, und $argv sind die Parameter des eingehenden Ereignisses (im Allgemeinen die Parameter des Ereignisverhaltens).
Natürlich gibt es nicht nur eine Möglichkeit, Ereignisse auszulösen: Trigger, sondern auch TriggerUntil, TriggerEvent und TriggerEventUntil. Aus dem Namen können wir ersehen, dass es in zwei Kategorien unterteilt ist: Trigger und TriggerEvent. Die Triggerklasse löst lediglich Ereignisse aus und muss nicht implementiert werden, um eine Ereignisinstanz zu erstellen. Sie benötigt nur einen Ereignisnamen und keinen Trigger Löst nur Ereignisse aus, löst aber auch Listener aus und erfordert eine Ereignisinstanz. Methoden mit dem Suffix „Until“ erfordern eine Callback-Funktion, und die Ergebnisse jedes Listeners werden an die Callback-Funktion übergeben. Wenn die Callback-Funktion einen echten Bool-Wert zurückgibt, muss der EventManager den Listener kurzschließen. (Für Kurzschluss siehe Kurzschluss unten)
Weitere Informationen finden Sie in der offiziellen API oder in den spezifischen Kommentaren von EventMangerInterface.
3. Erstellen Sie einen Zuhörer und verbinden Sie sich mit der Veranstaltung :
Listener können über EventManager oder SharedEventManager erstellt werden. Beide verwenden die Attach-Methode, die Parameter unterscheiden sich jedoch geringfügig.
Schauen wir uns zunächst die EventManager-Methode an:
Methodenprototyp: attachment($eventName, callable $listener, $priority = 1)
Es ist ganz einfach, wir brauchen nur den Ereignisnamen und eine aufrufbare Funktion, und schließlich ist die Standardpriorität 1 (die Priorität der integrierten Ereignisse in Zend ist meist negativ, wenn Sie also den Listener anpassen möchten). Die Priorität ist relativ hoch, weisen Sie einfach direkt eine positive Zahl zu)
.Die aufrufbare Funktion ist unser Listener. Für Ereignisnamen gibt es einen Sonderfall: „*“. Dies ähnelt dem regulären Matching und verbindet alle Ereignisse mit diesem Listener.
Schauen wir uns nun die SharedEventManager-Methode an:
Methodenprototyp: attachment($identifier, $eventName, callable $listener, $priority = 1);
Der einzige Unterschied zu vorher ist der Bezeichnerparameter. Die Quellcodekommentare zum Bezeichner lauten wie folgt:
wird verwendet, um gemeinsame Signale von der SharedEventManagerInterface-Instanz abzurufen;
Wird verwendet, um das Freigabesignal von der SharedEventmanager-Instanz abzurufen. Der Bezeichner ist nach meinem Verständnis ein Array: Wenn ein Ereignis (beachten Sie, dass SharedEventmanager keine Ereignisse erstellen kann) einen Bezeichner definiert, bedeutet dies, dass das Ereignis gemeinsam genutzt werden kann. Lassen Sie die SharedEventManger-Instanz den Bezeichnerparameter übergeben, wenn Sie den Listener mithilfe von „Attach“ erstellen. EventManager kann den Bezeichnerparameter verwenden, um alle Listener abzufragen.
Was verwirrend ist, ist, dass wir, da wir den Ereignisnamen haben, die relevanten Listener über den Ereignisnamen abfragen können. Warum sollten wir uns also die Mühe machen, das Attribut „Identifier“ hinzuzufügen? Was ich in Betracht ziehe, ist die Frage der Ereignisvererbung: Angenommen, es gibt eine Ereignisklasse Foo, die einen Ereignisverhaltensakt enthält. SubFoo erbt die Foo-Klasse und überschreibt den darin enthaltenen Ereignisverhaltensakt. Die Ereignisverhalten beider Klassen haben den gleichen Ereignisnamen act. Wenn Sie den Listener zu diesem Zeitpunkt anhand des Ereignisnamens abfragen, liegt offensichtlich ein Konflikt vor. Zu diesem Zeitpunkt definieren wir den Bezeichner [__CLASS__, get_class($this)] und geben den Bezeichner im Listener als SubFoo an. Offensichtlich stimmt er mit dem Ereignisverhalten in der SubFoo-Klasse überein.
Oben können wir mehrere Ereignisse über SharedEventManager abhören und es auch über Listener-Aggregate implementieren. Lassen Sie eine Klasse über ZendEventManagerListenerAggregateInterface mehrere Ereignisse abhören und verbinden Sie eine oder mehrere Instanzmethoden als Listener. Die gleiche Schnittstelle definiert auch attachment(EventManagerInterface $events) und detach(EventManagerInterface $events). In der spezifischen Implementierung von Attach verwenden wir die Attach-Methode der EventManager-Instanz, um mehrere Ereignisse abzuhören.
<span style="color: #0000ff;">use</span><span style="color: #000000;"> Zend\EventManager\EventInterface; </span><span style="color: #0000ff;">use</span><span style="color: #000000;"> Zend\EventManager\EventManagerInterface; </span><span style="color: #0000ff;">use</span><span style="color: #000000;"> Zend\EventManager\ListenerAggregateInterface; </span><span style="color: #0000ff;">use</span> Zend\<span style="color: #008080;">Log</span><span style="color: #000000;">\Logger; </span><span style="color: #0000ff;">class</span> LogEvents <span style="color: #0000ff;">implements</span><span style="color: #000000;"> ListenerAggregateInterface { </span><span style="color: #0000ff;">private</span> <span style="color: #800080;">$listeners</span> =<span style="color: #000000;"> []; </span><span style="color: #0000ff;">private</span> <span style="color: #800080;">$log</span><span style="color: #000000;">; </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __construct(Logger <span style="color: #800080;">$log</span><span style="color: #000000;">) { </span><span style="color: #800080;">$this</span>-><span style="color: #008080;">log</span> = <span style="color: #800080;">$log</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> attach(EventManagerInterface <span style="color: #800080;">$events</span><span style="color: #000000;">) { </span><span style="color: #800080;">$this</span>->listeners[] = <span style="color: #800080;">$events</span>->attach('do', [<span style="color: #800080;">$this</span>, 'log'<span style="color: #000000;">]); </span><span style="color: #800080;">$this</span>->listeners[] = <span style="color: #800080;">$events</span>->attach('doSomethingElse', [<span style="color: #800080;">$this</span>, 'log'<span style="color: #000000;">]); } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> detach(EventCollection <span style="color: #800080;">$events</span><span style="color: #000000;">) { </span><span style="color: #0000ff;">foreach</span> (<span style="color: #800080;">$this</span>->listeners <span style="color: #0000ff;">as</span> <span style="color: #800080;">$index</span> => <span style="color: #800080;">$listener</span><span style="color: #000000;">) { </span><span style="color: #800080;">$events</span>->detach(<span style="color: #800080;">$listener</span><span style="color: #000000;">); </span><span style="color: #0000ff;">unset</span>(<span style="color: #800080;">$this</span>->listeners[<span style="color: #800080;">$index</span><span style="color: #000000;">]); } } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> <span style="color: #008080;">log</span>(EventInterface <span style="color: #800080;">$e</span><span style="color: #000000;">) { </span><span style="color: #800080;">$event</span> = <span style="color: #800080;">$e</span>-><span style="color: #000000;">getName(); </span><span style="color: #800080;">$params</span> = <span style="color: #800080;">$e</span>-><span style="color: #000000;">getParams(); </span><span style="color: #800080;">$this</span>-><span style="color: #008080;">log</span>->info(<span style="color: #008080;">sprintf</span>('%s: %s', <span style="color: #800080;">$event</span>, json_encode(<span style="color: #800080;">$params</span><span style="color: #000000;">))); } }</span>
Vorteile der Verwendung von Aggregate:
1. Ermöglicht die Verwendung von Stateful-Listenern
2. Kombinieren Sie mehrere ähnliche Zuhörer in einer einzigen Klasse und verbinden Sie sie alle auf einmal
Vom Introspektion-Listener zurückgegebene Ergebnisse
Wir haben einen Zuhörer, aber wie erhalten wir die Ergebnisse, die er zurückgibt? Die Standardimplementierung von EventManager gibt eine Instanz von ResponseCollection zurück. Diese Klasse erbt von PHPs SplStack. Die Grundstruktur ist ein Stapel, sodass Sie Antworten in umgekehrter Reihenfolge durchlaufen können.
ResponseCollection bietet mehrere nützliche Methoden:
first(): Erstes Ergebnis abrufen
last(): Letztes Ergebnis abrufen
contains($value): Überprüfen Sie, ob der Stack einen bestimmten Wert enthält. Wenn ja, geben Sie true zurück, andernfalls false.
Short-Loop-Listener-Ausführung:
Was ist ein Kurzschluss? Angenommen, Sie möchten etwas tun, bis es ein Ergebnis hat. Dies ist eine Schleife. Wenn Sie das Ergebnis dieser Sache im Voraus kennen (z. B. wenn Sie diese Sache schon einmal gemacht haben), müssen Sie sie nicht vollständig abschließen. Zu diesem Zeitpunkt müssen Sie nur eine kurze Schleife ausführen.
Wir verfügen über einen Caching-Mechanismus beim Hinzufügen von EventManager. Lösen Sie ein Ereignis in einer Methode aus und kehren Sie direkt zurück, wenn wir ein zwischengespeichertes Ergebnis finden. Wenn das zwischengespeicherte Ergebnis nicht gefunden wird, speichern wir das ausgelöste Ereignis für die spätere Verwendung zwischen. Tatsächlich ist es dasselbe wie der Cache in der Computerhardware.
Die EventManager-Komponente bietet zwei Verarbeitungsmethoden: 1. triggerUntil(); 2. triggerEventUntil. Beide Methoden akzeptieren eine Callback-Funktion als ersten Parameter. Wenn die Rückruffunktion „true“ zurückgibt, wird die Ausführung gestoppt.
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> someExpensiveCall(<span style="color: #800080;">$criteria1</span>, <span style="color: #800080;">$criteria2</span><span style="color: #000000;">) { </span><span style="color: #800080;">$params</span> = <span style="color: #008080;">compact</span>('criteria1', 'criteria2'<span style="color: #000000;">); </span><span style="color: #800080;">$results</span> = <span style="color: #800080;">$this</span>->getEventManager()-><span style="color: #000000;">triggerUntil( </span><span style="color: #0000ff;">function</span>(<span style="color: #800080;">$r</span><span style="color: #000000;">){ </span><span style="color: #0000ff;">return</span> (<span style="color: #800080;">$r</span><span style="color: #000000;"> instanceof SomeResultClass); }</span>, <span style="color: #ff00ff;">__FUNCTION__</span>, <span style="color: #800080;">$this</span>, <span style="color: #800080;">$params</span><span style="color: #000000;"> ); </span><span style="color: #0000ff;">if</span>(<span style="color: #800080;">$results</span>-><span style="color: #000000;">stopped()) { </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$results</span>->last()'<span style="color: #000000;"> } }</span>
Aus dem obigen Beispiel wissen wir, dass ein Stopp der Ausführung höchstwahrscheinlich daran liegt, dass das letzte Ergebnis im Stapel unseren Anforderungen entspricht. Auf diese Weise müssen wir nur das Ergebnis zurückgeben. Warum müssen wir zusätzliche Berechnungen durchführen?
Der Prozess stoppt die Ausführung im Ereignis, und wir können die Ausführung auch im Listener stoppen. Der Grund dafür ist, dass wir bereits zuvor ein bestimmtes Ereignis erhalten haben. Nachdem wir nun dasselbe Ereignis erneut erhalten haben, können wir selbstverständlich die vorherigen Ergebnisse verwenden. In diesem Fall ruft der Listener stopPropagation(true) auf und der EventManager kehrt direkt zurück, ohne zusätzliche Listener zu benachrichtigen.
<span style="color: #800080;">$events</span>->attach('do', <span style="color: #0000ff;">function</span>(<span style="color: #800080;">$e</span><span style="color: #000000;">) { </span><span style="color: #800080;">$e</span>-><span style="color: #000000;">stopPropagation(); </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> SomeResultClass(); });</span>
当然,使用触发器范例可能会导致歧义,毕竟你并不知道最终的结果是否满足要求。
Keeping it in order.
偶尔你会关心监听器的执行顺序。我们通过监听器的优先级来控制执行顺序(上面说讲的短回路也会影响执行顺序)。每一个EventManager::attach()和SharedEventManager::attach()都会接受一个而外的参数:priority。默认情况下为1,我们可以省略该参数。如果你提供了该参数:高优先级执行的早,低优先级的可能会推迟执行。
自定义事件对象:
我们之前使用trigger()触发事件,在这同时我们也创建了事件。但trigger()的参数有限,我们只能指定事件的对象,参数,名称。实际上我们可以创建一个自定义事件,在Zendframework里面有个很重要的事件:MvcEvent。很显然MvcEvent便是一个自定义事件,该事件组合了application实例,路由器,路由匹配对象,请求和应答对象,视图模型还有结果。我们查看MvcEvent的源码会发现MvcEvent类实际上继承了Event类。同理我们的自定义事件对象也可以继承Event类或者继承MvcEvent。
<span style="color: #800080;">$event</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> CustomEvent(); </span><span style="color: #800080;">$event</span>->setName('foo'<span style="color: #000000;">); </span><span style="color: #800080;">$event</span>->setTarget(<span style="color: #800080;">$this</span><span style="color: #000000;">); </span><span style="color: #800080;">$event</span>->setSomeKey(<span style="color: #800080;">$value</span><span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">injected with event name and target:</span> <span style="color: #800080;">$events</span>->triggerEvent(<span style="color: #800080;">$event</span><span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">Use triggerEventUntil() for criteria-based short-circuiting:</span> <span style="color: #800080;">$results</span> = <span style="color: #800080;">$events</span>->triggerEventUntil(<span style="color: #800080;">$callback</span>, <span style="color: #800080;">$event</span>);
上面的代码可以看到我们使用自定义事件类创建了一个事件对象,调用相关拦截器为事件对象设置属性。我们有了事件对象还是用trigger()触发事件吗?显然不是,我们使用triggerEvent($event)方法,该方法接收一个事件对象。而triggerEventUntil有一个回调函数,该回调函数作为是否进行短回路的依据。