Table of Contents
I researched executed process in CakePHP 3, by controller execution. It was really complicated than I expected, so I omitted some explanation, and I guess it is somehow difficult to read this article, sorry.
Environment
- PHP 5.5.9
- cakephp/cakephp 3.0.9
- Ubuntu 14.04.2 LTS
I founded server as “php bin/cake.php server
“. When the server receive a request and CakePHP 3 create html output, webroot/index.php
is executed first. Let’s look into index.php
.
index.php
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 |
<?php /** * The Front Controller for handling every request * * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * * Licensed under The MIT License * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @since 0.2.9 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ // for built-in server if (php_sapi_name() === 'cli-server') { $_SERVER['PHP_SELF'] = '/' . basename(__FILE__); $url = parse_url(urldecode($_SERVER['REQUEST_URI'])); $file = __DIR__ . $url['path']; if (strpos($url['path'], '..') === false && strpos($url['path'], '.') !== false && is_file($file)) { return false; } } require dirname(__DIR__) . '/config/bootstrap.php'; use Cake\Network\Request; use Cake\Network\Response; use Cake\Routing\DispatcherFactory; $dispatcher = DispatcherFactory::create(); $dispatcher->dispatch( Request::createFromGlobals(), new Response() ); |
bootstrap.php
setup basic environment and dispatcher created by DispatcherFactory
handles request.
I picked up the important part of bootstrap.php
.
bootstrap.php
1 2 3 4 5 6 7 8 9 10 11 12 |
// Only try to load DebugKit in development mode // Debug Kit should not be installed on a production system if (Configure::read('debug')) { Plugin::load('DebugKit', ['bootstrap' => true]); } /** * Connect middleware/dispatcher filters. */ DispatcherFactory::add('Asset'); DispatcherFactory::add('Routing'); DispatcherFactory::add('ControllerFactory'); |
Plugin::load
and DispatcherFactory::add
registers distcher filters. Plugin::load('DebugKit', ['bootstrap' => true]);
works according to settings in app.php
. app.php
is a sort of configuration file. (related article: CakePHP3 Config File for Each Environment)。
Plugin::load
Let’s read comment.
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 61 62 63 64 65 66 67 68 |
/** * Loads a plugin and optionally loads bootstrapping, * routing files or runs an initialization function. * * Plugins only need to be loaded if you want bootstrapping/routes/cli commands to * be exposed. If your plugin does not expose any of these features you do not need * to load them. * * This method does not configure any autoloaders. That must be done separately either * through composer, or your own code during config/bootstrap.php. * * ### Examples: * * `Plugin::load('DebugKit')` * * Will load the DebugKit plugin and will not load any bootstrap nor route files. * However, the plugin will be part of the framework default routes, and have its * CLI tools (if any) available for use. * * `Plugin::load('DebugKit', ['bootstrap' => true, 'routes' => true])` * * Will load the bootstrap.php and routes.php files. * * `Plugin::load('DebugKit', ['bootstrap' => false, 'routes' => true])` * * Will load routes.php file but not bootstrap.php * * `Plugin::load('FOC/Authenticate')` * * Will load plugin from `plugins/FOC/Authenticate`. * * It is also possible to load multiple plugins at once. Examples: * * `Plugin::load(['DebugKit', 'ApiGenerator'])` * * Will load the DebugKit and ApiGenerator plugins. * * `Plugin::load(['DebugKit', 'ApiGenerator'], ['bootstrap' => true])` * * Will load bootstrap file for both plugins * * ``` * Plugin::load([ * 'DebugKit' => ['routes' => true], * 'ApiGenerator' * ], * ['bootstrap' => true]) * ``` * * Will only load the bootstrap for ApiGenerator and only the routes for DebugKit * * ### Configuration options * * - `bootstrap` - array - Whether or not you want the $plugin/config/bootstrap.php file loaded. * - `routes` - boolean - Whether or not you want to load the $plugin/config/routes.php file. * - `ignoreMissing` - boolean - Set to true to ignore missing bootstrap/routes files. * - `path` - string - The path the plugin can be found on. If empty the default plugin path (App.pluginPaths) will be used. * - `classBase` - The path relative to `path` which contains the folders with class files. * Defaults to "src". * - `autoload` - boolean - Whether or not you want an autoloader registered. This defaults to false. The framework * assumes you have configured autoloaders using composer. However, if your application source tree is made up of * plugins, this can be a useful option. * * @param string|array $plugin name of the plugin to be loaded in CamelCase format or array or plugins to load * @param array $config configuration options for the plugin * @throws \Cake\Core\Exception\MissingPluginException if the folder for the plugin to be loaded is not found * @return void */ |
From the comment, we can know Plugin::load('DebugKit', ['bootstrap' => true])
execute bootstrap.php
in every plugins.
DispatcherFactory::add($debugBar)
registers DebugBarFilter
as a filter of DispatcherFactory
. Until to reach DispatcherFactory::add($debugBar)
, load several configuration values, which change according to app.php
.
After loading DebugKit
, it adds 3 kinds of class with DispatcherFactory::add
.
DispatcherFactory::add
This method add middle-ware object to be executed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/** * Add a new middleware object to the stack of middleware * that will be executed. * * Instances of filters will be re-used across all sub-requests * in a request. * * @param string|\Cake\Routing\DispatcherFilter $filter Either the classname of the filter * or an instance to use. * @param array $options Constructor arguments/options for the filter if you are using a string name. * If you are passing an instance, this argument will be ignored. * @return \Cake\Routing\DispatcherFilter */ public static function add($filter, array $options = []) { if (is_string($filter)) { $filter = static::_createFilter($filter, $options); } static::$_stack[] = $filter; return $filter; } |
All of arguments for add
method are string, 'Asset'
, 'Routing'
, 'ControllerFactory'
. So the return value of _createFilter
method will be added into static::$_stack
.
Let’s see the return value of _createFilter
.
_createFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * Create an instance of a filter. * * @param string $name The name of the filter to build. * @param array $options Constructor arguments/options for the filter. * @return \Cake\Routing\DispatcherFilter * @throws \Cake\Routing\Exception\MissingDispatcherFilterException When filters cannot be found. */ protected static function _createFilter($name, $options) { $className = App::className($name, 'Routing/Filter', 'Filter'); if (!$className) { $msg = sprintf('Cannot locate dispatcher filter named "%s".', $name); throw new MissingDispatcherFilterException($msg); } return new $className($options); } |
This method creates an instance of filter class, it is obvious from the comment.
App::className
generates class name with namespace from argument. I wrote more detail in the article CakePHP 3 read. In this case, add
method is called with argument 'Asset'
, 'Routing'
, 'ControllerFactory'
, and added Routing\Filter\AssetFilter
, Routing\Filter\RoutingFilter
, Routing\Filter\ControllerFactoryFilter
were returned as return value of _createFilter
method.
As for now, 4 instance were inserted into static::$_stack
.
- DebugKit\Routing\Filter\DebugBarFilter
- Cake\Routing\Filter\AssetFilter
- Cake\Routing\Filter\RoutingFilter
- Cake\Routing\Filter\ControllerFactoryFilter
The remain code of index.php
is the following 5 lines.
1 2 3 4 5 |
$dispatcher = DispatcherFactory::create(); $dispatcher->dispatch( Request::createFromGlobals(), new Response() ); |
Continued to CakePHP 3 the Way to Reach Controller Invoking Part 2 of 3.