1、入口文件index.php
2、composer的自动加载
index.php
require __DIR__ . '/../vendor/autoload.php';
3、yii框架文件的自动加载
yii.php
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';
4、yii核心类映射,该映射关系是由build命令自动生成的,不需要个开发者管
yii.php
Yii::$classMap = require __DIR__ . '/classes.php';
5、加载配置文件 config/web.php
index.php
$config = require __DIR__ . '/../config/web.php';
6、传递配置文件获取应用实例,并运行应用实例
index.php
(new yii\web\Application($config))->run();
7、可以看到,实例化应用时,将自定义配置文件传了过去,这一步进行了一些校验和设置,以及将核心组件和自定义组件合并
比如关于配置ID、bashpath的校验
比如对timezone、errohandle的设置
vendor/yiisoft/yii2/base/Application.php(Application的父类)
public function __construct($config = [])
{
// 将应用实例自身赋值给Yii::$app
Yii::$app = $this;
// 设置此模块类的当前请求实例
static::setInstance($this);
// 标识请求处理生命周期中的当前应用程序状态,由程序管理
$this->state = self::STATE_BEGIN;
// 预初始化应用程序
$this->preInit($config);
// 注册错误处理器
$this->registerErrorHandler($config);
Component::__construct($config);
}
/**
* 预初始化应用程序(由构造函数调用该方法)
* @param array $config the application configuration
* @throws InvalidConfigException if either [[id]] or [[basePath]] configuration is missing.
*/
public function preInit(&$config)
{
// 校验配置文件中是否设置配置ID,如果没有设置,则抛出异常
if (!isset($config['id'])) {
throw new InvalidConfigException('The "id" configuration for the Application is required.');
}
// 校验配置文件中是否设置程序的基本路径。如果有设置,则将基本路径设置到Module类(Application类的父类)属性中,否则抛出异常
if (isset($config['basePath'])) {
$this->setBasePath($config['basePath']);
unset($config['basePath']);
} else {
throw new InvalidConfigException('The "basePath" configuration for the Application is required.');
}
// 校验是否在配置文件中设置,并进行设置
if (isset($config['vendorPath'])) {
$this->setVendorPath($config['vendorPath']);
unset($config['vendorPath']);
} else {
// set "@vendor"
$this->getVendorPath();
}
if (isset($config['runtimePath'])) {
$this->setRuntimePath($config['runtimePath']);
unset($config['runtimePath']);
} else {
// set "@runtime"
$this->getRuntimePath();
}
if (isset($config['timeZone'])) {
$this->setTimeZone($config['timeZone']);
unset($config['timeZone']);
} elseif (!ini_get('date.timezone')) {
$this->setTimeZone('UTC');
}
if (isset($config['container'])) {
$this->setContainer($config['container']);
unset($config['container']);
}
// 将核心组件与自定义组件合并
foreach ($this->coreComponents() as $id => $component) {
if (!isset($config['components'][$id])) {
$config['components'][$id] = $component;
} elseif (is_array($config['components'][$id]) && !isset($config['components'][$id]['class'])) {
$config['components'][$id]['class'] = $component['class'];
}
}
}
8、运行实例
vendor/yiisoft/yii2/base/Application.php
/**
* 运行应用程序
* This is the main entrance of an application.
* @return int the exit status (0 means normal, non-zero values mean abnormal)
*/
public function run()
{
try {
$this->state = self::STATE_BEFORE_REQUEST;
$this->trigger(self::EVENT_BEFORE_REQUEST);
$this->state = self::STATE_HANDLING_REQUEST;
$response = $this->handleRequest($this->getRequest());
$this->state = self::STATE_AFTER_REQUEST;
$this->trigger(self::EVENT_AFTER_REQUEST);
$this->state = self::STATE_SENDING_RESPONSE;
$response->send();
$this->state = self::STATE_END;
return $response->exitStatus;
} catch (ExitException $e) {
$this->end($e->statusCode, isset($response) ? $response : null);
return $e->statusCode;
}
}
8.1 可以看到,首先执行了请求前的事件触发
$this->state = self::STATE_BEFORE_REQUEST;
$this->trigger(self::EVENT_BEFORE_REQUEST);
8.2 然后,处理请求
$this->state = self::STATE_HANDLING_REQUEST;
$response = $this->handleRequest($this->getRequest());
8.3 之后,再执行请求后的时间触发
$this->state = self::STATE_AFTER_REQUEST;
$this->trigger(self::EVENT_AFTER_REQUEST);
8.4 发送响应给客户端
$this->state = self::STATE_SENDING_RESPONSE;
$response->send();
8.5 将应用状态标识为应用程序已结束
$this->state = self::STATE_END;
9、分析处理请求的过程
/**
* 处理请求
* @param Request $request
* @return Response the resulting response
* @throws NotFoundHttpException if the requested route is invalid
*/
public function handleRequest($request)
{
// 解析请求为路由和参数(如果设置了catchAll,则将$route和$params重置)
if (empty($this->catchAll)) {
try {
// resolve方法的作用是将当前请求解析为路由和关联的参数
list($route, $params) = $request->resolve();
} catch (UrlNormalizerRedirectException $e) {
$url = $e->url;
if (is_array($url)) {
if (isset($url[0])) {
$url[0] = '/' . ltrim($url[0], '/');
}
$url += $request->getQueryParams();
}
return $this->getResponse()->redirect(Url::to($url, $e->scheme), $e->statusCode);
}
} else {
// 如果设置了维护(catchAll),则重置$route和$params为维护的路由和参数
$route = $this->catchAll[0];
$params = $this->catchAll;
unset($params[0]);
}
try {
// 记录调试消息
Yii::debug("Route requested: '$route'", __METHOD__);
// 设置路由
$this->requestedRoute = $route;
// 运行由路由指定的控制器操作
$result = $this->runAction($route, $params);
if ($result instanceof Response) {
return $result;
}
// 获取响应
$response = $this->getResponse();
if ($result !== null) {
$response->data = $result;
}
return $response;
} catch (InvalidRouteException $e) {
throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'), $e->getCode(), $e);
}
}
10、可以看到,实际处理请求的是runAction方法
public function runAction($route, $params = [])
{
$parts = $this->createController($route);
if (is_array($parts)) {
/* @var $controller Controller */
list($controller, $actionID) = $parts;
$oldController = Yii::$app->controller;
Yii::$app->controller = $controller;
$result = $controller->runAction($actionID, $params);
if ($oldController !== null) {
Yii::$app->controller = $oldController;
}
return $result;
}
$id = $this->getUniqueId();
throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".');
}
11、而其中最重要的就是createController方法
public function createController($route)
{
if ($route === '') {
$route = $this->defaultRoute;
}
// double slashes or leading/ending slashes may cause substr problem
$route = trim($route, '/');
if (strpos($route, '//') !== false) {
return false;
}
if (strpos($route, '/') !== false) {
list($id, $route) = explode('/', $route, 2);
} else {
$id = $route;
$route = '';
}
// 模块和控制器映射优先
if (isset($this->controllerMap[$id])) {
$controller = Yii::createObject($this->controllerMap[$id], [$id, $this]);
return [$controller, $route];
}
$module = $this->getModule($id);
if ($module !== null) {
return $module->createController($route);
}
if (($pos = strrpos($route, '/')) !== false) {
$id .= '/' . substr($route, 0, $pos);
$route = substr($route, $pos + 1);
}
$controller = $this->createControllerByID($id);
if ($controller === null && $route !== '') {
$controller = $this->createControllerByID($id . '/' . $route);
$route = '';
}
return $controller === null ? false : [$controller, $route];
}
可以很容易的看到,这个方法表达的大致意思就是
如果路由是空,比如访问 www.haveyb.com/ ,则跳转到默认路由
否则继续判断是否有设置模块和控制器映射,如果有,则直接返回
否则就正常处理请求
12、返回响应