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.