CakePHP 3 コントローラ実行までのプロセスを追う 後編


中編に続いて処理内容を見ていきます。 webroot/index.php に残されたのは次の4行でした。

Request::createFromGlobals() はメソッド名から明らかですが、 グローバル変数 $_GET 等 からリクエストオブジェクを作成します。

dispatch

dispatchEvent でイベントを発生させています。 そしてイベントが発生した後の処理結果がレスポンスオブジェクトになっていたら終了します。

dispatchEvent

dispatchEvent は、 Dispatcher では定義されておらず、 Dispathcer で使用されている EventManagerTrait で定義されています。

このメソッドが更に EventManagerdispatch を呼んでいます。 引数として渡される $event\Cake\Event\Event のインスタンスです。

\Cake\Event\Event のコンストラクタを確認します。

\Cake\Event\Event __construct

コンストラクタの引数は、 'Dispatcher.beforeDispatch' になります。

インスタンス変数に引数の値を格納しているだけでした。 今回 $data'request''response' をキーにもつ連想配列になります。

作成された $event を引数にして EventManagerdispatch メソッド が実行されます。

EventManager dispatch

dispatch の中で listeners メソッドが実行されます。 引数は $event->name() ですので 'Dispatcher.beforeDispatch' です。
listeners は 呼び出されるべきリスナを、呼び出される順序で配列にして返します。

listenres

呼び出されているメソッド prioritisedListeners の中身を見てみます。

prioritisedListeners

とくに難しいことは行っていません。 イベントキー すなわち 'Dispatcher.beforeDispatch''Dispatcher.afterDispatch' に関係するリスナを返すメソッドです。

そして listeners では $globaListeners$localListeners のリスナーをマージしています。 Eventmanager はシングルトンっぽいつくりになっていて、 グローバルに扱えるインスタンスと、 個別の用途に使うインスタンスを分けることができるようになっています。 実際のところ今回の処理では $globalListeners が空なので、 $localListeners のキーがそのまま $priorities に入ります。 リスナのキーは優先度(priority) ですので、 $priorities には int 型 の 優先度の配列になります。
優先度の数値が低いものから順に、そしてグローバルリスナを優先して、リスナの配列を作成しています。

EventManagerdispatch に戻り、 isStoppedfalse の場合、すなわちイベントが終了していない場合に _callListener でリスナを呼び出します。

_callListener

$listener に渡された値がメソッドの名前になり、実行されます。 実際に渡されるメソッド名は 'handle' です。 $eventnew Event('Dispatcher.beforeDispatch') で作成されたイベントです。 $data[0]request$data[1]response です。

すると _callListener の 返り値 は フィルタオブジェクト の handle($event, request, response) になります。 実際のところ、 登録された 4つのフィルタについて $listener が実行されます。

handle メソッドは それぞれのフィルタではなく、 その親クラスの DispathcerFilter で定義されています。 今まで出てきたすべてのフィルタは DispatcherFilter を継承していました。

handle

今、 $event->name()'Dispatcher.beforeDispatch' になりますから、 $name には 'beforeDispatch' が入ります。 つまり、 handle を実行するそれぞれのクラスで定義された beforeDispatch メソッド が実行されることになります。

それぞれのクラスの beforeDispatch を見てみましょう。
実行される順で見ると、 AssetFilter, RoutingFilter, ControllerFactoryFilter です。 (DebugBarFilter は省略します。)

AssetFilter beforeDispatch

Asset ファイル のリクエストだった場合にレスポンスを返します。 Asset ファイル だった場合、 header メソッド を呼び出したりして適宜処理をします。

RoutingFilter beforeDispatch

どのコントローラを使うべきなのかを指定しています。

ControllerFactoryFilter beforeDispatch

コントローラをインスタンス化して $event->data['controller'] に代入しています。

このようにして設定されたコントローラが、 Dispatcherinvoke メソッド で実行されます。 invoke が実行されると、 Controllerrender まで実行します。 そして html の生成まで行われます。

あとは、 dispatch メソッド のところで afterDispatch を実行して終わります。