Yii2.0 框架源码阅读,从请求发起,到结束的运行步骤
其实最初阅读是从 yii\web\UrlManager 这个类开始看起,不断的寻找这个类中方法的调用者,最终回到了
yii\web\Application
,那就从头开始看.
1,Nginx
nginx 作为 web 服务器,时刻监听着 80 端口,等待接收用户请求,并转发给 php 进行处理,Yii2.0 框架使用了统一的入口脚本:index.php
所以 nginx 中有如下的配置:
location / {
try_files $uri $uri / /index.php?$args;
}/
首先 nginx 会检索 $uri 与 $uri / 这个路径下的文件,如果没有找到,那就将请求交给 index.php
2,创建 Yii Aapplication 实例
从 index.php 文件可以看出加载了各个文件夹下的配置项,然后 new 了一个 application,这个构造方法是在
yii\web\Application
的父类
yii\base\Applicaton
中,主要根据配置项初始化:
public
function __construct($config = []) {
Yii: :$app = $this;
static: :setInstance($this);
$this - >state = self: :STATE_BEGIN;
$this - >preInit($config);
$this - >registerErrorHandler($config);
Component: :__construct($config);
}
3,Application run
可以看到创建实例之后调用了 run 方法
$applicaton->run();
run 方法位于
yii\base\Application
中,总的来说就是执行:
before request 处理请求前的操作
handle request 真正的处理这次 HTTP 请求
after request 请求处理完成之后的操作
send response 将响应信息发送给客户端
public
function run() {
//代码简化 try catch去掉
$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;
}
4,Get Request Object
请求的处理从
$this - >handleRequest($this - >getRequest());
开始,首先看 handleRequest 的参数是 getRequest 的返回值,getRequest 方法位于
yii\web\Application
中:
public
function getRequest() {
return $this - >get('request');
}
get 方法,根据层层的继承关系找到其位于
yii\base\Application
的父类 yii\base\Module 的父类
yii\di\ServiceLocator
中:
public function get($id, $throwException = true)
{
if (isset($this->_components[$id])) {
return $this->_components[$id];
}
if (isset($this->_definitions[$id])) {
$definition = $this->_definitions[$id];
if (is_object($definition) && !$definition instanceof Closure) {
return $this->_components[$id] = $definition;
} else {
return $this->_components[$id] = Yii::createObject($definition);
}
} elseif ($throwException) {
throw new InvalidConfigException("Unknown component ID: $id");
} else {
return null;
}
}
这里我们先不考虑
$_definitions $_components
这些变量初始化的位置,打印 $id='request'是获取到的 $definition 为:
array(2) {
["cookieValidationKey"]=>
string(32) "0drrX5wQ2wZ8Hli2Ql48ss8efcE-W11m"
["class"]=>
string(15) "yii\web\Request"
}
说一下这个结果的由来,事实上这个就是我们在 config/main-local.php 中的配置项:
$config = [
'components' => [
'request' => [
'cookieValidationKey' => '0drrX5wQ2wZ8Hli2Ql48ss8efcE-W11m',
],
],
];
而我们获取到信息中有
class = >yii\web\Request
, 这个 class 的添加是在 new Application 的时候,也就在构造方法中执行了 preInit(),preInit 中对核心的组件进行了初始化,添加了 class 信息,然后更新到了 config 数组中.
//preInit
//coreComponents 在yii\web\Application 与 yii\base\Application中
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'];
}
}
回到上面 ServiceLocator 的 get 方法中,我们获取到了 $definition,现在是一个数组并非一个对象,所以执行的是:
return $this - >_components[$id] = Yii: :createObject($definition);
return 回来的就是 Request 对象:
object(yii\web\Request)
这个 createObject 方法,看注释中的描述就是可以理解为一个高级版本的 new,因为可以根据字符串(类名),数组(含 class 信息),匿名函数来创建一个对象,然后返回.
5,Handle Request
run 中调用的 handleRequest() 位于
yii\web\Application
中,这里主要的操作就是:
从 Request 中获取用户请求路由
调用这个路由对应的 action
// @param $request yii\web\Request
public
function handleRequest($request) {
if (empty($this - >catchAll)) {
list($route, $params) = $request - >resolve();
} else {
$route = $this - >catchAll[0];
$params = $this - >catchAll;
unset($params[0]);
}
//下面暂时省略
}
catchAll 用于系统维护的时候,将所有的请求转发到一处进行处理,默认值为空,所以这里的条件判断进入了
list($route, $params) = $request - >resolve();
resolve 肯定是在 yii\web\Request 中了:
public
function resolve() {
$result = Yii: :$app - >getUrlManager() - >parseRequest($this);
if ($result !== false) {
list($route, $params) = $result;
if ($this - >_queryParams === null) {
$_GET = $params + $_GET; // preserve numeric keys
} else {
$this - >_queryParams = $params + $this - >_queryParams;
}
return [$route, $this - >getQueryParams()];
} else {
throw new NotFoundHttpException(Yii: :t('yii', 'Page not found.'));
}
}
这里通过调用 yii\web\UrlManager 来解析请求路由,parseRequest 方法的参数为 Request 对象,它的主要任务是:
通过 Request 对象获取 path info
检查是否有跟我们配置的 url rule 匹配的,有则返回
没有则直接按照默认的方式进行解析
进入 UrlManager:
public
function parseRequest($request) {
//在enable pretty url的前提下
$pathInfo = $request - >getPathInfo();
//如果在rules中匹配到了 request 直接返回转换后的路由
foreach($this - >rules as $rule) {
if (($result = $rule - >parseRequest($this, $request)) !== false) {
return $result;
}
}
//没有则使用默认方式,同时对路由进行检查,判断是否有多与一个的斜线
//判断是否使用了.html后缀
//代码省略,这里做的就是比较和截取的操作
return [$pathInfo, []];
}
getPathInfo() 先不展开讨论,主要就是从当前请求的 http header 中获取 path info.
回到
yii\web\Application
的 handleRequest 中:
public
function handleRequest($request) {
//代码简化一下,完整版请查看yii\web\Application类
list($route, $params) = $request - >resolve();
$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;
}
}
在获取到属于 Yii 中的路由(route)信息之后,接下来的主要操作就是执行对应的 controller 中的 action,或者是对应模块(module)中的 controller,controller 中对应的 action.
runAction($route, $params)
先不展开讨论,主要知道执行完成之后我们获取的是一个 yii\web\Response 对象就行了.
6,回到 run 方法
将 response 信息发送给客户端
// yii\base\Application run()
$response = $this - >handleRequest($this - >getRequest());
$response - >send();
return $response - >exitStatus;
至此一次完整的请求完成.
来源: https://www.cnblogs.com/skyfynn/p/8308997.html