目次
CakePHP 3 コントローラ実行までのプロセスを追う 前編の続きです。
index.php
に残されたのは次の5行です。
1 2 3 4 5 |
$dispatcher = DispatcherFactory::create(); $dispatcher->dispatch( Request::createFromGlobals(), new Response() ); |
DispatcherFactory::create()
を見てみます。
DispatcherFactory::create()
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * Create a dispatcher that has all the configured middleware applied. * * @return CakeRoutingDispatcher */ public static function create() { $dispatcher = new Dispatcher(); foreach (static::$_stack as $middleware) { $dispatcher->addFilter($middleware); } return $dispatcher; } |
create
メソッド では Dispatcher
のインスタンスを作っています。 Factory のお決まりの使い方です。 作成されるのは CoreRoutingDispatcher
です。 Dispathcer
クラス では特にコンストラクタも記述されていませんので、 シンプルにインスタンスを作ることになります。
そして foreach
を使って static::$_stack
の中に格納されたフィルタを addFilter
で追加していきます。 ここで前編で見ていた4つのフィルタが追加されます。
addFilter
の中身を見てみます。
addFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/** * Connected filter objects * * @var array */ protected $_filters = []; /** * Add a filter to this dispatcher. * * The added filter will be attached to the event manager used * by this dispatcher. * * @param CakeEventEventListenerInterface $filter The filter to connect. Can be * any EventListenerInterface. Typically an instance of CakeRoutingDispatcherFilter. * @return void */ public function addFilter(EventListenerInterface $filter) { $this->_filters[] = $filter; $this->eventManager()->on($filter); } |
addFilter
の中で実行されている $this->eventManager()
は Dispatcher.php
では定義されていませんが、 Dispatcher
が使用する EventManagerTrait
で定義されています。
eventManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * Returns the CakeEventEventManager manager instance for this object. * * You can use this instance to register any new listeners or callbacks to the * object events, or create your own events and trigger them at will. * * @param CakeEventEventManager|null $eventManager the eventManager to set * @return CakeEventEventManager */ public function eventManager(EventManager $eventManager = null) { if ($eventManager !== null) { $this->_eventManager = $eventManager; } elseif (empty($this->_eventManager)) { $this->_eventManager = new EventManager(); } return $this->_eventManager; } |
eventManager()
が実行されると $this->>_eventManager
の値、 EventManager
のインスタンスが返されます。
そして addFilter
に戻って、 on
のところで イベントリスナ としてフィルタが追加されていきます。
on
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
/** * Adds a new listener to an event. * * A variadic interface to add listeners that emulates jQuery.on(). * * Binding an EventListenerInterface: * * ``` * $eventManager->on($listener); * ``` * * Binding with no options: * * ``` * $eventManager->on('Model.beforeSave', $callable); * ``` * * Binding with options: * * ``` * $eventManager->on('Model.beforeSave', ['priority' => 90], $callable); * ``` * * @param string|CakeEventEventListenerInterface $eventKey The event unique identifier name * with which the callback will be associated. If $eventKey is an instance of * CakeEventEventListenerInterface its events will be bound using the `implementedEvents` methods. * * @param array|callable $options Either an array of options or the callable you wish to * bind to $eventKey. If an array of options, the `priority` key can be used to define the order. * Priorities are treated as queues. Lower values are called before higher ones, and multiple attachments * added to the same priority queue will be treated in the order of insertion. * * @param callable $callable The callable function you want invoked. * * @return void * @throws InvalidArgumentException When event key is missing or callable is not an * instance of CakeEventEventListenerInterface. */ public function on($eventKey = null, $options = [], $callable = null) { if ($eventKey instanceof EventListenerInterface) { $this->_attachSubscriber($eventKey); return; } $argCount = func_num_args(); if ($argCount === 2) { $this->_listeners[$eventKey][static::$defaultPriority][] = [ 'callable' => $options ]; return; } if ($argCount === 3) { $priority = isset($options['priority']) ? $options['priority'] : static::$defaultPriority; $this->_listeners[$eventKey][$priority][] = [ 'callable' => $callable ]; return; } throw new InvalidArgumentException('Invalid arguments for EventManager::on().'); } |
引数には フィルタのインスタンスが渡されます。 今回出てくるフィルタはすべて DispatcherFilter
を継承しており、 DispatcherFilter
は EventListenerInterface
を実装していますから、 最初の if
節 が実行され、 _attachSubscriber
が実行されて終了します。
_attachSubscriber
を見てみます。
_attachSubscriber
コールバックを設定しています。
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 |
/** * Auxiliary function to attach all implemented callbacks of a CakeEventEventListenerInterface class instance * as individual methods on this manager * * @param CakeEventEventListenerInterface $subscriber Event listener. * @return void */ protected function _attachSubscriber(EventListenerInterface $subscriber) { foreach ((array)$subscriber->implementedEvents() as $eventKey => $function) { $options = []; $method = $function; if (is_array($function) && isset($function['callable'])) { list($method, $options) = $this->_extractCallable($function, $subscriber); } elseif (is_array($function) && is_numeric(key($function))) { foreach ($function as $f) { list($method, $options) = $this->_extractCallable($f, $subscriber); $this->on($eventKey, $options, $method); } continue; } if (is_string($method)) { $method = [$subscriber, $function]; } $this->on($eventKey, $options, $method); } } |
このメソッドでは、implementedMethod
で設定するべきコールバックを導き出して $this->on($eventKey, $options, $method)
を実行しています。
implementedMethod
を使用していますが、 それぞれの フィルタ では実装されておらず、 フィルタの親クラスとなっている DispatcherFilter
で実装されています。
implementedEvents
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/** * Returns the list of events this filter listens to. * Dispatcher notifies 2 different events `Dispatcher.before` and `Dispatcher.after`. * By default this class will attach `preDispatch` and `postDispatch` method respectively. * * Override this method at will to only listen to the events you are interested in. * * @return array */ public function implementedEvents() { return [ 'Dispatcher.beforeDispatch' => [ 'callable' => 'handle', 'priority' => $this->_config['priority'] ], 'Dispatcher.afterDispatch' => [ 'callable' => 'handle', 'priority' => $this->_config['priority'] ], ]; } |
_attachSubscriber
に戻って見てみると、 最初の if
節 が実行されて _extractCallable
が呼び出されることがわかります。
_extractCallable
の中身を見てみます。
_extractCallable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * Auxiliary function to extract and return a PHP callback type out of the callable definition * from the return value of the `implementedEvents` method on a CakeEventEventListenerInterface * * @param array $function the array taken from a handler definition for an event * @param CakeEventEventListenerInterface $object The handler object * @return callback */ protected function _extractCallable($function, $object) { $method = $function['callable']; $options = $function; unset($options['callable']); if (is_string($method)) { $method = [$object, $method]; } return [$method, $options]; } |
引数 $function
は配列が、 $object
には $subscriber
すなわちフィルタのインスタンスが割り当てられます。 そして $method
には 'handle'
が、 $options
には連想配列が代入されます。 handle
がどんなメソッドかは必要になったときにまた調べるとして、処理を読み進めます。
処理を追っていくと on('Dispatcher.beforeDispatch', ['priority' => 10], [FilterObject, 'handle']);
のようなコードが実行されることがわかります。 _listener
の配列に イベントと priority をキーにして オブジェクトとそのメソッドの配列が追加されていることがわかります。
このイベントリスナの登録が終わると、リクエストの処理が始まります。
後編に続きます。