CakePHP 2.7.5 のリクエストの受け付けからビューのレンダリングまでのおおまかな流れ

## .htaccess

.htaccess によりリクエストは `app/webroot/index.php` に集約されます。

.htaccess

```
<IfModule mod_rewrite.c>
	RewriteEngine on
	RewriteRule ^$ app/webroot/ [L]
	RewriteRule (.*) app/webroot/$1 [L]
</IfModule>
```

## app/webroot/index.php

いくつかの定数の定義が行われたのちに `lib/Cake/bootstrap.php` が include されます。処理の最後に Dispatcher::dispatch メソッドに CakeRequest クラスと CakeResponse クラスのインスタンスを引数として渡し実行します。

app/webroot/index.php

```php
<?php

// Some processes

if (!defined('CAKE_CORE_INCLUDE_PATH')) {
	if (function_exists('ini_set')) {
		ini_set('include_path', ROOT . DS . 'lib' . PATH_SEPARATOR . ini_get('include_path'));
	}
	if (!include 'Cake' . DS . 'bootstrap.php') {
		$failed = true;
	}
} else {
	if (!include CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'bootstrap.php') {
		$failed = true;
	}
}
if (!empty($failed)) {
	trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
}

App::uses('Dispatcher', 'Routing');

$Dispatcher = new Dispatcher();
$Dispatcher->dispatch(
	new CakeRequest(),
	new CakeResponse()
);
```

## bootstrap.php

いくつかの定数の定義が行われたのちに `lib/Cake/basics.php` 、`lib/Cake/Core/App.php` 、`lib/Cake/Error/exceptions.php` が require されます。また spl_autoload_register 関数で、まだ読み込みされていないクラスが呼ばれた場合に自動的に App::load メソッドが実行され、事前に App::uses メソッドで登録されたクラスが読み込まれるようにしています。

lib/Cake/basics.php

```php
<?php

// Some processes

require CAKE . 'basics.php';
require CAKE . 'Core' . DS . 'App.php';
require CAKE . 'Error' . DS . 'exceptions.php';

spl_autoload_register(array('App', 'load'));

// Some processes
```

lib/Cake/Core/App.php

```php
	public static function uses($className, $location) {
		static::$_classMap[$className] = $location;
	}
```

```php
	public static function load($className) {
		if (!isset(static::$_classMap[$className])) {
			return false;
		}
		if (strpos($className, '..') !== false) {
			return false;
		}

		$parts = explode('.', static::$_classMap[$className], 2);
		list($plugin, $package) = count($parts) > 1 ? $parts : array(null, current($parts));

		$file = static::_mapped($className, $plugin);
		if ($file) {
			return include $file;
		}
		$paths = static::path($package, $plugin);

		if (empty($plugin)) {
			$appLibs = empty(static::$_packages['Lib']) ? APPLIBS : current(static::$_packages['Lib']);
			$paths[] = $appLibs . $package . DS;
			$paths[] = APP . $package . DS;
			$paths[] = CAKE . $package . DS;
		} else {
			$pluginPath = CakePlugin::path($plugin);
			$paths[] = $pluginPath . 'Lib' . DS . $package . DS;
			$paths[] = $pluginPath . $package . DS;
		}

		$normalizedClassName = str_replace('\\', DS, $className);
		foreach ($paths as $path) {
			$file = $path . $normalizedClassName . '.php';
			if (file_exists($file)) {
				static::_map($file, $className, $plugin);
				return include $file;
			}
		}

		return false;
	}
```

## Dispatcher::dispatch メソッド
まず `Dispatcher.beforeDispatch` という名前のイベントを実行します。`Dispatcher.beforeDispatch` イベントには `Dispatcher::implementedEvents` メソッドで返される配列にて `Dispatcher::parseParams` メソッドが実行されるように関連付けされています。この段階で `Dispatcher::parseParams` メソッドが実行されます。`Dispatcher::parseParams` はルーティングを反映して実行するコントローラーとアクションを決定します。

実行するべきコントローラーとアクションを決定したら `Dispatcher::_getController` メソッドと `Dispatcher::_invoke` メソッドを実行してコントローラーにアクションの実行を依頼します。


lib/Cake/Routing/Dispatcher.php

```php
	public function dispatch(CakeRequest $request, CakeResponse $response, $additionalParams = array()) {
		$beforeEvent = new CakeEvent('Dispatcher.beforeDispatch', $this, compact('request', 'response', 'additionalParams'));
		$this->getEventManager()->dispatch($beforeEvent);

		$request = $beforeEvent->data['request'];
		if ($beforeEvent->result instanceof CakeResponse) {
			if (isset($request->params['return'])) {
				return $beforeEvent->result->body();
			}
			$beforeEvent->result->send();
			return null;
		}

		$controller = $this->_getController($request, $response);

		if (!($controller instanceof Controller)) {
			throw new MissingControllerException(array(
				'class' => Inflector::camelize($request->params['controller']) . 'Controller',
				'plugin' => empty($request->params['plugin']) ? null : Inflector::camelize($request->params['plugin'])
			));
		}

		$response = $this->_invoke($controller, $request);
		if (isset($request->params['return'])) {
			return $response->body();
		}

		$afterEvent = new CakeEvent('Dispatcher.afterDispatch', $this, compact('request', 'response'));
		$this->getEventManager()->dispatch($afterEvent);
		$afterEvent->data['response']->send();
	}
```

lib/Cake/Routing/Dispatcher.php

```php
	public function parseParams($event) {
		$request = $event->data['request'];
		Router::setRequestInfo($request);
		$params = Router::parse($request->url);
		$request->addParams($params);
		if (!empty($event->data['additionalParams'])) {
			$request->addParams($event->data['additionalParams']);
		}
	}

```


### Dispatcher::_invoke メソッド
`Controller::invokeAction` メソッドでアクションの実行、 `Controller::render` メソッドでレンダリング内容の組み立てを行います。コントローラーのアクションに記述されているモデルはマジックメソッドである `Controller::__isset` メソッドにて `Controller::loadModel` メソッドを使い、呼びだされた時点で読み込まれます。

lib/Cake/Routing/Dispatcher.php

```php
	protected function _invoke(Controller $controller, CakeRequest $request) {
		$controller->constructClasses();
		$controller->startupProcess();
		$response = $controller->response;
		$render = true;
		$result = $controller->invokeAction($request);
		if ($result instanceof CakeResponse) {
			$render = false;
			$response = $result;
		}
		if ($render && $controller->autoRender) {
			$response = $controller->render();
		} elseif (!($result instanceof CakeResponse) && $response->body() === null) {
			$response->body($result);
		}
		$controller->shutdownProcess();
		return $response;
	}
```

### Controller::render メソッド
`Controller::_getViewObject` メソッドで View のオブジェクトを取得し、`View::render` メソッドでビューのレンダリング内容の組み立てを行います。

lib/Cake/Controller/Controller.php

```php
	public function render($view = null, $layout = null) {
		$event = new CakeEvent('Controller.beforeRender', $this);
		$this->getEventManager()->dispatch($event);
		if ($event->isStopped()) {
			$this->autoRender = false;
			return $this->response;
		}
		if (!empty($this->uses) && is_array($this->uses)) {
			foreach ($this->uses as $model) {
				list($plugin, $className) = pluginSplit($model);
				$this->request->params['models'][$className] = compact('plugin', 'className');
			}
		}
		$this->View = $this->_getViewObject();
		$models = ClassRegistry::keys();
		foreach ($models as $currentModel) {
			$currentObject = ClassRegistry::getObject($currentModel);
			if ($currentObject instanceof Model) {
				$className = get_class($currentObject);
				list($plugin) = pluginSplit(App::location($className));
				$this->request->params['models'][$currentObject->alias] = compact('plugin', 'className');
				$this->View->validationErrors[$currentObject->alias] =& $currentObject->validationErrors;
			}
		}
		$this->autoRender = false;
		$this->response->body($this->View->render($view, $layout));
		return $this->response;
	}
```

## CakeResponse::send メソッド

アクションを実行して、ビューのレンダリング内容の組み立てを行ったのち、`Dispatcher::dispatch` メソッドの最後に記述されている `CakeResponse::send` メソッドでレンダリング内容を出力します。

lib/Cake/Network/CakeResponse.php

```php
	public function send() {
		if (isset($this->_headers['Location']) && $this->_status === 200) {
			$this->statusCode(302);
		}

		$codeMessage = $this->_statusCodes[$this->_status];
		$this->_setCookies();
		$this->_sendHeader("{$this->_protocol} {$this->_status} {$codeMessage}");
		$this->_setContent();
		$this->_setContentLength();
		$this->_setContentType();
		foreach ($this->_headers as $header => $values) {
			foreach ((array)$values as $value) {
				$this->_sendHeader($header, $value);
			}
		}
		if ($this->_file) {
			$this->_sendFile($this->_file, $this->_fileRange);
			$this->_file = $this->_fileRange = null;
		} else {
			$this->_sendContent($this->_body);
		}
	}
```

```php
	protected function _sendContent($content) {
		echo $content;
	}
```