YII源碼分析-處理請求過程

本文講解內容

從請求入口文件開始,到響應組件發送結果給用戶瀏覽器的過程簡要分析。php

流程分析

從入口文件進入,直接看關鍵點,分析application的啓動流程web

$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。(可後面再深刻)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);yii

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());ui

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\ModulerunActionthis

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給出的運行機制概述和示意圖來描述應用是如何處理一個請求的。

clipboard.png

clipboard.png

相關文章
相關標籤/搜索