Annovent – Event Dispatching
Vor kurzem „musste“ ich für ein Projekt einen Event Dispatcher bauen. Ja ich weiß, Symfony hat da einen, der eigentlich schon alles kann. Aber irgendwie hatte ich da keine Lust drauf, denn ich wollte ein paar Special-Features reinklöppeln. Damit ich aber nicht komplett das Rad neu erfinde, habe ich mich einfach an der Interface von Symfony gehalten und da etwas kompatibles gebaut.
Die Grundlagen des Event Dispatchers könnt ihr einfach auf der Symfony Components Webseite nachlesen, denn bis dorthin sind wir fast 100% kompatibel. Aber wie gesagt ich wollte Special-Features und die sehen wie folgt aus:
Namesspaces
In dem Standard-Dispatcher kann ich meine Listeners nur auf einen ganz speziellen Event hören lassen. Das war mir nicht genug, denn ich wollte auf eine Gruppe von Ereignissen reagieren. Deswegen habe ich Namespaces eingeführt und kann somit mit Platzhaltern arbeiten. In der Verwendung sieht dies dann wie folgt aus:
$dispatcher->connect('SomeComponent.MyEvent', array($listener, 'Method1'));
$dispatcher->connect('SomeComponent.*', array($listener, 'Method1'));
$dispatcher->connect('*', array($listener, 'Method2'));
Der erste Listener hört nur genau auf SomeComponent.MyEvent, der zweite auf alle Ereignisse, die von SomeComponent erzeugt werden und der dritte hört einfach auf jedes Event, das irgendwo verwendet wird.
Annotations
Auch wenn wir jetzt schon besser sind als Symfony, waren die Namespaces nicht genug. Dank der Doctrine-Implementierung zum Annotation-Handling war es kein Problem auch noch Annotations draufzusetzen. Ich habe ja vor ein paar Tagen darüber geredet und hier ist endlich der Anwendungsfall dazu:
class Listener
{
/**
* @Event("SomeComponent.Bar")
* @Event("SomeComponent.Foo")
*/
public function method1(Event $event)
{
}
}
$dispatcher->connectListener( new Listener );
Dieser registriere Listener würde nun auf zwei Events hören: SomeComponent.Bar und SomeComponent.Foo. Ich kann aber natürlich auch die method1 über connect wie gewohnt registrieren, man muss dieses Feature also nicht nutzen, wenn man nicht will.
Named Parameters
Irgendwie fand ich es auch noch ganz lustig mit Named Parameters zu arbeiten. Das fand ich ganz spannend, denn so muss ich nicht wissen, wie die notifacation des Events ihre Parameter mitbringt. Deswegen hab ich den Code so gebaut, das folgendes Beispiel funktioniert.
class Listener2
{
/**
* @Event("SomeComponent.Render")
*/
public function method1($argument1, $foo)
{
}
}
$dispatcher->notify(new Event('SomeComponent.Render', array('foo' => 'bar', 'argument1' => 'arg1' ));
Zusätzlich wird immer das Event selbst als Parameter angehängt. Das System ist so gebaut, dass Parameter, die die Methode nicht kennt einfach unterschlagen werden. Fand ich kompatibler zu Erweiterungen und wenn man nun der method1 noch ein Argument $event hinzufügt, so bekommt man dort das Event und kann damit die üblichen Dinge mit machen.
So das waren meine drei Special-Features. Meiner Meinung nach fühlt sich der Dispatcher in der Handhabung gut an und ich hoffe, dass der ein oder andere ihn nutzen oder forken will.
Ich finde die Idee der Namespaced Events sehr gut. Auch die named Patameter sind sehr nützlich. Könntest Du die Erweiterungen nicht einfach zu Symfony bringen? Die würden profitieren.