Symfony2のドキュメントを見ながら比較しつつ共通項を見る

Symfony2はすばらしいフレームワークで、ドキュメントも充実していていいですね。ここでは、Symfony2のドキュメントを参照しながら共通項を探ってみたいと思います。基本的にはこちら。文中、このドキュメントからの引用とzf2を比較していきます。
http://docs.symfony.gr.jp/symfony2/book/internals.html#event-dispatcher

イベントオブジェクト

通常は、特定のイベントに対するリスナーで必要とされるデータは、``Event`` オブジェクトとともにリスナーへ渡します。 kernel.response イベントの場合、作成され各リスナーへ渡される Event オブジェクトは、基底の Event オブジェクトのサブクラスである Symfony\Component\HttpKernel\Event\FilterResponseEvent のインスタンスです。 このクラスには getResponse や setResponse といったメソッドがあり、Response オブジェクトを取得したり置き換えたりできます。

通常のストーリーは次のようになります。 イベントに対するリスナーを作成する時、リスナーへ渡される Event オブジェクトは専用のサブクラスで、イベントから情報を取得したり、イベントに情報を返すための追加のメソッドを持っています。

http://docs.symfony.gr.jp/symfony2/book/internals.html#index-14

ZF2でも、基底のEventはシンプルで、Applicationで利用するイベントオブジェクトはMvcEventになります。MvcEventオブジェクトにはMvcフローで使われるリクエストやレスポンス、ビューモデルなどさまざまなオブジェクトのコンテナになります。

イベントのユニーク性(命名規則・インターフェース)

ユニークなイベント名には任意の文字列を使えますが、次のような簡単な命名規約に従うことが推奨されています:

  • 小文字、数字、ドット (.) およびアンダースコア (_) のみを使うようにしてください。
  • プレフィックス名前空間とドットを使います (例 kernel.)。
  • 実行されるアクションの内容を表す動詞で終わるようにします (例 request)。

次に、良いイベント名の例を示します:

  • kernel.response
  • form.pre_set_data
http://docs.symfony.gr.jp/symfony2/book/internals.html#index-13

イベント名を識別するのに名前空間とアクションの組み合わせになるパターンはよく見られます。
zf2では文字列としては結合せず、独立したパラメータとして名前空間の部分はID、アクションに相当するのがイベント名になります。zf2でのIDには少し特徴があり、拡張性の肝になっているので、SharedEventManagerのところで後述します。

イベントディスパッチャのスコープと抽入

EventDispatcher クラスのソースコードを読むと、シングルトンではないことに気づくでしょう (getInstance() スタティックメソッドは定義されていません)。 これは設計者の意図で、単一の PHP リクエストの処理中に複数のイベントディスパッチャーを同時に実行したい場合に役立ちます。 その代わりに、イベントへの接続や通知を行う必要があるオブジェクトへ、ディスパッチャーオブジェクトを渡す必要があります。

この問題に対するベストプラクティスは、対象となるオブジェクトへディスパッチャーオブジェクトを注入することで、これを依存性の注入 (dependency injection) と呼びます。

http://docs.symfony.gr.jp/symfony2/book/internals.html#id29

zf2で、Symfony2のEventDispatcherに相当するのはEventManagerです。Symfony2でEventDispatcherがアプリケーション全体をラップしていると思いますが、zf2の場合、個々のオブジェクトに対してEventManagerインスタンスを割り当て、EventManagerオブジェクトのスコープはイベントを発生させるオブジェクト(=target)に対応することになります。通常、イベントオブジェクトにsetTargetで登録します。また、EventManagerのIdentifierとしてTargetのクラス名やインターフェース名などを登録し、これがSymfony2での名前空間と同じ役割を果たします。ここがかなり重要になりますので冗長ですが後述します。
EventManagerの抽入については、EventManagerAwareInterfaceを実装してサービスマネージャーで抽入してもらうこともできますし、もともとイベントのスコープはそのオブジェクト内でのものなので、innerクラス的に内部でnewしたとしても原則に反しないと思います。PHP5.4で、trait ProvidesEventsを使うか、5.4以前ならProvidesEvents相当のメソッドを実装することでイベントフルなオブジェクトになります。

リスナーとサブスクライバ

イベントを監視する最も一般的な方法は、イベントリスナーをディスパッチャーに登録することです。 リスナーは 1 つ以上のイベントを監視でき、イベントがディスパッチされるたびに通知されます。

http://docs.symfony.gr.jp/symfony2/book/internals.html#index-17

Symfony2と同様、zf2でもリスナーを同時に複数のイベントをリッスンするように登録できます。
また、

イベントを監視するもう 1 つの方法が、イベントサブスクライバ です。 イベントサブスクライバは PHP のクラスで、ディスパッチャーに対してどのイベントが監視されるのかを、サブスクライバクラス自身が通知します。
これはリスナークラスとよく似ていますが、監視するイベントの種類をクラス自身がディスパッチャーに対して通知できる点が異なります。

http://docs.symfony.gr.jp/symfony2/book/internals.html#index-17

これは、リスナーを登録する詳細をどのオブジェクトの責務にするかという問題になります。
たとえばbootstrapで、あるリスナーを特定のイベントと結びつけて登録する処理を書いたとします。この関連付けを決定するのはbootstrapクラスとなり、bootsrapにイベントの関連付けの知識が配置されます。これは目先、問題がないように見えますが、リッスン対象を変えるときに、bootstrapにも変更が必要になります。これは、リスナーとリスナーを登録するオブジェクトの間の結合を意味します。Diによって関連付けは分離されますが、もし直接書いたとしたらという仮定です。設定ファイルに分離すれば疎結合かというとそうでもないのです。
サブスクライバはこの、"どのイベントをリッスンするかという知識"についてサブスクライバオブジェクトが責任を持つことを意味します。bootstrapは、そのサブスクライバを使用する使用しないだけを決定すればよく、どのイベントをリッスンしているのかということについての知識が不要になります。イベント変更時もサブスクライバ内部のみになります。疎結合、いいですね。
zf2での、これと同じ仕組みはListnerAggregateです。詳細はドキュメントで!

イベントの流れを止める

イベントループ中、そのフローを中断したいときがあります。

リスナーの処理後に、他のリスナーが呼び出されないようにしたい場合もあります。 つまり、リスナーからディスパッチャーに対して、以降のリスナーへのイベントの伝播を停止する (これ以上リスナーへ通知を行わない) よう要求できるようにしたいのです。 このような場合は、リスナーから :method:`Symfony\\Component\\EventDispatcher\\Event::stopPropagation` method: メソッドを呼び出します。

http://docs.symfony.gr.jp/symfony2/book/internals.html#event-dispatcher-event-propagation

zf2でも同様で、$event->stopPropagation();でそのイベントにループの停止を指定できます。
zf2での終了条件はもう一つありますが、それについては後述します。