1 单一入口 index.php
文件名:basic/web/index.php
<?php
// 测试环境使用,部署到生产环境时注释掉以下两行
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
// 引用自动加载
require __DIR__ . '/../vendor/autoload.php';
// 引用yii.php
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';
// 获取web配置文件
$config = require __DIR__ . '/../config/web.php';
// 启动
(new yii\web\Application($config))->run();
2 Yii.php
文件路径:basic/vendor/yiisoft/yii2/Yii.php
<?php
require __DIR__ . '/BaseYii.php';
// yii是一个辅助类,提供常见的框架功能
// 通过补充yii内容,可以定制自己的框架功能
class Yii extends \yii\BaseYii
{
}
/*
* Yii_autoload执行过程
* 1、先查看类是否在 Yii::$classMap 中存在
* 2、存在直接调用getAlias生成类文件物理地址
* 3、不存在将命名空间转换为实际路径调用
*/
spl_autoload_register(['Yii', 'autoload'], true, true);
// 核心类的类名和物理文件地址映射的hash数组
Yii::$classMap = require __DIR__ . '/classes.php';
/**
* 依赖注入(Dependency Injection,DI)容器
* 依赖注入容器知道怎样初始化并配置对象及其依赖的所有对象
* 在Yii中使用DI解耦,常见注入方式:构造函数注入、属性注入
* yii\di\Container继承了
* yii\base\Component继承了
* yii\base\BaseObject
* BaseObject实现了Configurable
* Configurable是支持由可配置的类实现的接口
* DI容器只支持 yii\base\Object 类
* 如果你的类想放在DI容器里,那么必须继承自 yii\base\Object类
*/
Yii::$container = new yii\di\Container();
3 web/Application.php继承base/Application.php 执行构造方法
public function __construct($config = [])
{
Yii::$app = $this;
// application对象放到注册树中
static::setInstance($this);
// 初始化执行状态
$this->state = self::STATE_BEGIN;
// 验证并初始化config配置
$this->preInit($config);
// 将errorHandler组件注册为PHP错误处理程序
$this->registerErrorHandler($config);
// 调用Component
// 调用BaseObject
// BaseObject下的__construct方法执行init方法
// 该方法被下层类重写过,最终执行base\Application的init
Component::__construct($config);
}
4 base/Application.php中的init()方法
public function init()
{
$this->state = self::STATE_INIT;
$this->bootstrap();
}
5 web/Application.php中的bootstrap()方法
protected function bootstrap()
{
$request = $this->getRequest();
// 定义别名
Yii::setAlias('@webroot', dirname($request->getScriptFile()));
Yii::setAlias('@web', $request->getBaseUrl());
// 调用base/Application.php中的bootstrap()
parent::bootstrap();
}
6 base/Application.php中的bootstrap()方法
protected function bootstrap() {
// 如果扩展文件为空直接加载扩展清单文件
if ($this->extensions === null) {
$file = Yii::getAlias('@vendor/yiisoft/extensions.php');
$this->extensions = is_file($file) ? include $file : [];
}
// 在扩展返回的数组中,使用createObject实例化对象
foreach ($this->extensions as $extension) {
if (!empty($extension['alias'])) {
foreach ($extension['alias'] as $name => $path) {
Yii::setAlias($name, $path);
}
}
if (isset($extension['bootstrap'])) {
$component = Yii::createObject($extension['bootstrap']);
if ($component instanceof BootstrapInterface) {
Yii::debug('Bootstrap with ' . get_class($component) . '::bootstrap()', __METHOD__);
$component->bootstrap($this);
} else {
Yii::debug('Bootstrap with ' . get_class($component), __METHOD__);
}
}
}
// 创建并运行各个组件
foreach ($this->bootstrap as $mixed) {
$component = null;
if ($mixed instanceof \Closure) {
Yii::debug('Bootstrap with Closure', __METHOD__);
if (!$component = call_user_func($mixed, $this)) {
continue;
}
} elseif (is_string($mixed)) {
if ($this->has($mixed)) {
$component = $this->get($mixed);
} elseif ($this->hasModule($mixed)) {
$component = $this->getModule($mixed);
} elseif (strpos($mixed, '\\') === false) {
throw new InvalidConfigException("Unknown bootstrapping component ID: $mixed");
}
}
if (!isset($component)) {
$component = Yii::createObject($mixed);
}
if ($component instanceof BootstrapInterface) {
Yii::debug('Bootstrap with ' . get_class($component) . '::bootstrap()', __METHOD__);
$component->bootstrap($this);
} else {
Yii::debug('Bootstrap with ' . get_class($component), __METHOD__);
}
}
}
7 base/Application.php中的run()方法
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 yii\web\Application::handleRequest()方法
public function handleRequest($request)
{
if (empty($this->catchAll)) {
try {
// resolve 方法调用 urlManager 对 url 进行解析
list($route, $params) = $request->resolve();
} catch (UrlNormalizerRedirectException $e) {
$url = $e->url;
if (is_array($url)) {
if (isset($url[0])) {
// ensure the route is absolute
$url[0] = '/' . ltrim($url[0], '/');
}
$url += $request->getQueryParams();
}
return $this->getResponse()->redirect(Url::to($url, $e->scheme), $e->statusCode);
}
} else {
// 如果设置catchall,所有请求都会跳转到配置的路由
$route = $this->catchAll[0];
$params = $this->catchAll;
unset($params[0]);
}
try {
Yii::debug("Route requested: '$route'", __METHOD__);
$this->requestedRoute = $route;
// 根据route访问对应的 module/controller/action
// 入参类似 : yii\base\Module::runAction('country/index', ['r' => 'country/index'])
$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);
}
}
9 yii\base\Module::runAction()方法
public function runAction($route, $params = []){
// 重点关注创建controller方法
// 如果route为空,则使用defaultRouter属性
// router不为空,查看controllerMap是否命中
// 未命中则调用yii/base/Module::getModule查看
// 查看route中是否有module存在
// 存在则直接调用yii/base/Module::createController
// 否则yii/base/Module::createControllerByID
$parts = $this->createController($route);
if (is_array($parts)) {
/* @var $controller Controller */
list($controller, $actionID) = $parts;
$oldController = Yii::$app->controller;
Yii::$app->controller = $controller;
// 调用形式为
// yii\base\Controller::runAction('index', ['r' => 'country/index'])
$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) . '".');
}
10 yii\base\Controller::runAction() 方法
public function runAction($id, $params = [])
{
// 重点看createAction
// 如果id为空,则访问默认action
// id不为空,看Controller::actions 方法中是否有配置
// 如果有,直接使用配置中的class
// 利用反射(ReflectionMethod)查看调用方法是否存在
// 如果是,返回 yii\base\InlineAction 的实例
$action = $this->createAction($id);
if ($action === null) {
throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);
}
Yii::debug('Route to run: ' . $action->getUniqueId(), __METHOD__);
if (Yii::$app->requestedAction === null) {
Yii::$app->requestedAction = $action;
}
$oldAction = $this->action;
$this->action = $action;
$modules = [];
$runAction = true;
// 调用所有加载module中的beforeAction
foreach ($this->getModules() as $module) {
if ($module->beforeAction($action)) {
array_unshift($modules, $module);
} else {
$runAction = false;
break;
}
}
$result = null;
if ($runAction && $this->beforeAction($action)) {
// 运行action
$result = $action->runWithParams($params);
$result = $this->afterAction($action, $result);
// 调用所有module的afterAction方法
foreach ($modules as $module) {
/* @var $module Module */
$result = $module->afterAction($action, $result);
}
}
if ($oldAction !== null) {
$this->action = $oldAction;
}
return $result;
}
11 yii2/base/InlineAction下runWithParams()
public function runWithParams($params) {
$args = $this->controller->bindActionParams($this, $params);
Yii::debug('Running action: ' . get_class($this->controller) . '::' . $this->actionMethod . '()', __METHOD__);
if (Yii::$app->requestedParams === null) {
Yii::$app->requestedParams = $args;
}
// 回调形式类似如下call_user_func_array([app\controllers\CountryController, 'actionIndex'], [])
return call_user_func_array([$this->controller, $this->actionMethod], $args);
}