目次
CakePHP 3 で コントローラ が実行されるまでのプロセス、 前処理 がどうなっているのか気になって調べました。 予想以上に追いかけるのが大変だったので簡単に済ませたところがいくつもあります。 きっとこの記事を読むのも大変だと思います。
環境
- PHP 5.5.9
- cakephp/cakephp 3.0.9
- Ubuntu 14.04.2 LTS
まずサーバを起動します。 “php bin/cake.php server
“。 サーバがリクエストを受けて CakePHP 3 が HTML を生成するとき、 まず最初に webroot/index.php
が実行されます。 まず 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
で初期設定をして、 DispatcherFactory
で作成した dispatcher
が リクエストを処理しています。
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
、DispatcherFactory::add
で dispatcher filter (ディスパッチャフィルタ) を登録しています。 Plugin::load('DebugKit', ['bootstrap' => true]);
のところは app.php
で設定した値に応じて読み込まれます。 app.php
は一種の設定ファイルです。 (関連記事: CakePHP3 環境ごとに設定ファイルを分ける方法)。
Plugin::load
コメントのみ読んでみます。
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 */ |
ここから、Plugin::load('DebugKit', ['bootstrap' => true])
で プラグインの中の bootstrap.php
を読み込むことがわかります。
DispatcherFactory::add($debugBar)
で DebugBarFilter
を DispatcherFactory
のフィルタとして追加しています。 DispatcherFactory::add($debugBar)
に辿り着くまで、結構な数の設定値を読み込んでいます。 設定値は app.php
の内容次第で変わってきます。
DebugKit
を load
した後に 、 3つのクラスについて DispatcherFactory::add
が実行されています。
DispatcherFactory::add
実行されるべきミドルウェアオブジェクトを追加するものです。
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; } |
add
メソッド の引数は、 全て文字列 'Asset'
、'Routing'
、'ControllerFactory'
でしたから、 _createFilter
の返り値が static::$_stack
に追加されます。
_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); } |
コメントから明らかですが、 フィルタのインスタンスを作ります。
App::className
は 以前の記事で詳しく見ましたが、引数を元にネームスペース付きのクラス名を作って返します。
ここでは 'Asset'
, 'Routing'
, 'ControllerFactory'
について add
が呼び出されていて、 Routing\Filter\AssetFilter
、 Routing\Filter\RoutingFilter
、 Routing\Filter\ControllerFactoryFilter
が _createFilter
の返り値として返されます。
ここまでをまとめると、 static::$_stack
に次の4つのクラスのインスタンスが追加されることになります。
- DebugKit\Routing\Filter\DebugBarFilter
- Cake\Routing\Filter\AssetFilter
- Cake\Routing\Filter\RoutingFilter
- Cake\Routing\Filter\ControllerFactoryFilter
index.php
に残されたのは次の5行です。
1 2 3 4 5 |
$dispatcher = DispatcherFactory::create(); $dispatcher->dispatch( Request::createFromGlobals(), new Response() ); |
中編に続きます。