本文讲解内容
从请求入口文件开始,到响应组件发送结果给用户浏览器的过程简要分析。
流程分析
从入口文件进入,直接看关键点,分析application
的启动流程
$config = require __DIR__.'/../config/web.php';
(new yii\web\Application($config))->run();
首先进入yii\web\Application
类
里面没有构造函数,即找到父级类\yii\base\Application
public function __construct($config = [])
{
// 保存当前启动的application实例,交给Yii::$app
Yii::$app = $this;
static::setInstance($this);
$this->state = self::STATE_BEGIN;
$this->preInit($config);
// 加载异常处理,这块比较深,后面再扩展。
$this->registerErrorHandler($config);
Component::__construct($config);
}
我们看一下static::setInstance($this);
指向到了yii\base\Module
public static function setInstance($instance)
{
// 将当前实例加入Yii::$app->loadedModules['yii\web\Application']数组中
if ($instance === null) {
unset(Yii::$app->loadedModules[get_called_class()]);
} else {
Yii::$app->loadedModules[get_class($instance)] = $instance;
}
}
这里主要是后面使用,比如module里,通过self::getInstance()获取App对象,类似于Yii::$app。(可后面再深入)
接下来看$this->preInit($config);
这里是加载配置文件的框架信息,如:设置别名,设置框架路径等等,最重要的是加载默认组件
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'];
}
}
再接着是Component::__construct($config);
public function __construct($config = [])
{
if (!empty($config)) {
Yii::configure($this, $config);//将配置文件里面的所有配置信息 本地变量化
}
$this->init(); // 取出控制器的命名空间(路径)
}
到这里为止,第一部分已经执行完了,开始执行$application->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;
}
}
我们来看下$response = $this->handleRequest($this->getRequest());
public function handleRequest($request)
{
if (empty($this->catchAll)) {
try {
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 {
$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);
}
}
我们再看看这句指向:\yii\base\Module
的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) . '".');
}
最后是$response->send();
public function send()
{
if ($this->isSent) {
return;
}
$this->trigger(self::EVENT_BEFORE_SEND);
//取得$response的format 再获得format对象的实例执行 format方法(header设置Content-Type)
$this->prepare();
$this->trigger(self::EVENT_AFTER_PREPARE);
$this->sendHeaders();
$this->sendContent();
$this->trigger(self::EVENT_AFTER_SEND);
$this->isSent = true;
}
总结
yii给出的运行机制概述和示意图来描述应用是如何处理一个请求的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。