yii2 beta版 执行流程
yii2 beta版 执行流程
自动加载
1.composer的自动加载
//composer的加载实现了四种方式,可以看看 require(__DIR__ . '/../../vendor/autoload.php');
composer的自动加载只加载其下载的组件....和yii框架的加载没有任何关系 2.yii的加载
spl_autoload_register(['Yii', 'autoload'], true, true); Yii::$classMap = include(__DIR__ . '/classes.php'); Yii::$container = new yii\di\Container;
这里使用spl_autoload_register把Yii::autoload注册成自动加载器,
- 判断在不在$classMap中,如果有别名获取别名的文件路径
- 如果不在$classMap中,则去除下划线,使用别名获取文件路径
- 引入该文件,并判断是不是类或者接口或者trait
public static function autoload($className) { if (isset(static::$classMap[$className])) { $classFile = static::$classMap[$className]; if ($classFile[0] === '@') { $classFile = static::getAlias($classFile); } } elseif (strpos($className, '\\') !== false) { $classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false); if ($classFile === false || !is_file($classFile)) { return; } } else { return; } include($classFile); if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) { throw new UnknownClassException("Unable to find '$className' in file: $classFile. Namespace missing?"); } }
Yii::$classMap中保存的是命名空间和文件实际路径的对应表,默认加载了所有yii提供的类 Yii::$container 实现了依赖注入, Yii::$app 继承serviceLocal..只实现了单例组件,其中属性_components保存的是对象,_definitions保存的是类,匿名函数或者对象
引导前初始化配置
1.检查配置中模块ID是不是存在
2.检查配置中basePath,vendorPath,runtimePath,timeZone是否存在,并设置
3.向配置中添加核心组件配置:
base:log,view,formatter,i18n,mailer,urlManager,assetManager,security
web:request,response,session,user,errorHandler
4.如果启用了YII_ENABLE_ERROR_HANDLER错误处理,则会注册错误处理组件.然后从配置中卸载errorHandler,避免之后的引导再次添加组件.
ini_set('display_errors', false); //注意这里... set_exception_handler([$this, 'handleException']); set_error_handler([$this, 'handleError']); if ($this->memoryReserveSize > 0) { $this->_memoryReserve = str_repeat('x', $this->memoryReserveSize); } register_shutdown_function([$this, 'handleFatalError']);
5.然后将配置中的值赋给applicaition
属性 | 说明 | 所在文件 |
$_events | 使用on xxx 开通注册事件 | yii\base\Component |
$_behaviors | 使用 as xxx开头注册行为 | yii\base\Component |
$_components | 保存组件单例对象 | yii\di\ServiceLocator |
$_definitions | 保存组件的原始数据 | yii\di\ServiceLocator |
$params | 参数,是个数组 | yii\base\Module |
$id | 模块ID | yii\base\Module |
$module | 该模块的父模块 | yii\base\Module |
$layout | 布局 | yii\base\Module |
$controllerMap | 控制器地图 | yii\base\Module |
$controllerNamespace | 控制器命名空间 | yii\base\Module |
$defaultRoute | 默认路由 | yii\base\Module |
$_basePath | 模块的根目录 | yii\base\Module |
$_viewPath | 模块的视图目录 | yii\base\Module |
$_layoutPath | 模块的布局目录 | yii\base\Module |
$_modules | 子模块 | yii\base\Module |
$_instances | 这个不常用,获取当前请求模块的实例 | yii\base\Module |
$controllerNamespace | 'app\\controllers' | yii\base\Application |
$name | 应用名称 | yii\base\Application |
$version | 版本 | yii\base\Application |
$charset | 字符编码.默认utf8 | yii\base\Application |
$language | 语言,默认en-US | yii\base\Application |
$sourceLanguage | 源语言 | yii\base\Application |
$controller | 当前的控制器实例 | yii\base\Application |
$layout | 布局 | yii\base\Application |
$requestedRoute | 请求的路由 | yii\base\Application |
$requestedAction | 请求的动作 | yii\base\Application |
$requestedParams | 请求的参数 | yii\base\Application |
$extensions | 扩展 | yii\base\Application |
$bootstrap | 引导 | yii\base\Application |
$state | 状态 | yii\base\Application |
$defaultRoute | 默认路由 | yii\web\Application |
$catchAll | 捕捉所有 | yii\web\Application |
$controller | 当前控制器实例 | yii\web\Application |
引导阶段
- 1.初始化request组件
- 2.设置@webroot和@web别名
- 3.加载扩展extensions,如果没有设置extensions会读取@vendor/yiisoft/extensions.php中的扩展并设置别名,如果扩展可引导,则执行其bootstrap方法-----本身来讲扩展概念没什么用处,因为还是依靠modules来加载这些扩展
- 4.加载bootstrap中设置的组件和模块或者类,如果引导类实现了BootstrapInterface接口则执行bootstrap的方法
运行
- 触发请求前事件EVENT_BEFORE_REQUEST,yii-debug中注册了该事件
- 处理请求,如果catchAll不为空,则使用里面的路由.否则的话会解析获得路由和参数,
然后执行动作action,返回响应Response,或者响应的data数据.public function handleRequest($request) { if (empty($this->catchAll)) { list ($route, $params) = $request->resolve(); } else { $route = $this->catchAll[0]; $params = array_splice($this->catchAll, 1); } try { Yii::trace("Route requested: '$route'", __METHOD__); $this->requestedRoute = $route; $result = $this->runAction($route, $params); if ($result instanceof Response) { return $result; } else { $response = $this->getResponse(); if ($result !== null) { $response->data = $result; } return $response; } } catch (InvalidRouteException $e) { throw new NotFoundHttpException($e->getMessage(), $e->getCode(), $e); } }
- 动作的执行:首先会根据路由创建控制器.然后执行动作.
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); Yii::$app->controller = $oldController; return $result; } else { $id = $this->getUniqueId(); throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".'); } }
创建控制器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 = ''; } // module and controller map take precedence $module = $this->getModule($id); if ($module !== null) { //如果有模块,则继续往下面找.这里是递归...能一直查询子模块 return $module->createController($route); } //查看控制器地图...一般控制器地图里的控制器..作为产品临时使用的页面 if (isset($this->controllerMap[$id])) { $controller = Yii::createObject($this->controllerMap[$id], [$id, $this]); return [$controller, $route]; } //执行到最后,没有子模块的时候...则将id加上剩余的路径(可能有多个/文件夹) 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]; }
获取模块public function getModule($id, $load = true) { //这一段是获取子模块的..在运行中是没有作用的..这一小部分可以当方法使用.. if (($pos = strpos($id, '/')) !== false) { // sub-module $module = $this->getModule(substr($id, 0, $pos)); return $module === null ? null : $module->getModule(substr($id, $pos + 1), $load); } //这是返回模块 if (isset($this->_modules[$id])) { if ($this->_modules[$id] instanceof Module) { return $this->_modules[$id]; } elseif ($load) { Yii::trace("Loading module: $id", __METHOD__); if (is_array($this->_modules[$id]) && !isset($this->_modules[$id]['class'])) { $this->_modules[$id]['class'] = 'yii\base\Module'; } /* @var $module Module */ $module = Yii::createObject($this->_modules[$id], [$id, $this]); $module->setInstance($module); return $this->_modules[$id] = $module; } } return null; }
- 触发请求后事件EVENT_AFTER_REQUEST,yii未注册事件
- 响应发送