Table of Contents
This is a continued article from CakePHP 3 the Way to Reach Controller Invoking part 1 of 3.
index.php remains the following 5 lines.
|
1 2 3 4 5 |
$dispatcher = DispatcherFactory::create(); $dispatcher->dispatch( Request::createFromGlobals(), new Response() ); |
Let’s see 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 method creates an instance of Dispatcher class, CoreRoutingDispatcher. This is ordinal use of factory pattern. Dispathcer class doesn’t have any special constructing process, constructor isn’t written, so create method simply create an instance.
And with foreach, addFilter method adds each filter pushed in static::$_stack. Here, the filters we saw in CakePHP 3 the Way to Reach Controller Invoking part 2 of 3 will be added.
Let’s look into 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); } |
$this->eventManager() executed in addFilter doesn’t defined in Dispatcher.php, it’s defined in EventManagerTrait that Dispatcher uses.
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() method returns $this->>_eventManager, the instance of EventManager.
Now back to addFilter, at on method filters are added as event listeners.
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().'); } |
Instance of filter is passed as argument to on method. All filters we discussing about inherit DispatcherFilter. And DispatcherFilter implements EventListenerInterface, so the first if clause is processed and _attachSubscriber is executed and the process ends.
Now, look into _attachSubscriber.
_attachSubscriber
It configures callbacks.
|
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); } } |
In this method, gather callbacks callbacks, that should be configured, with implementedMethod and call $this->on($eventKey, $options, $method) with them.
implementedMethod doesn’t defined in each filter, it’s defined in the parent class of each filter, 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'] ], ]; } |
Back to _attachSubscriber, the first if clause will be executed and it calls _extractCallable.
Then, look into _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]; } |
Argument $function will be assigned an array, $object will be assigned $subscriber which is instance of filter, $method will be assigned 'handle', $options will be assigned an associative array. Let’s look at the next process. I will research handle method when it’s required.
The code like on('Dispatcher.beforeDispatch', ['priority' => 10], [FilterObject, 'handle']); will be executed. Array of object and its method is added to _listener associative array with key of event and priority.
After this event listener registration, it starts processing request.
Continued to CakePHP 3 the Way to Reach Controller Invoking part 3 of 3.
