Yii2源碼分析(一):入口

寫在前面,寫這些隨筆是記錄下本身看Yii2源碼的過程,可能會有些流水帳,大部分解析放在註釋裏說明,因爲我的水平有限,有不正確的地方還望斧正。php

如下源碼版本基於Yii2的2.0.34版本,模板用的基礎版。web

web入口文件Index.php

// 定義全局的常量,YII_DEBUG標識是夠開啓debug模式,YII_ENV標識出當前運行環境,默認env(開發), 上線後改爲prod來表示正式環境。
defined('YII_DEBUG') or define('YII_DEBUG', true);

defined('YII_ENV') or define('YII_ENV', 'dev');

// 加載composer vendor的autoload文件
require(__DIR__ . '/../vendor/autoload.php');
// 加載Yii框架
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');

入口文件很簡單,作了一些初始化工做,具體看註釋。bootstrap

接下來看加載的Yii文件代碼:設計模式

<?php
/**
 * Yii bootstrap file.
 *
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

require(__DIR__ . '/BaseYii.php');

/**
 * Yii is a helper class serving common framework functionalities.
 *
 * It extends from [[\yii\BaseYii]] which provides the actual implementation.
 * By writing your own Yii class, you can customize some functionalities of [[\yii\BaseYii]].
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class Yii extends \yii\BaseYii
{
}
// 調用Yii::autoload來註冊autoload, 並且是放到autoload隊列之首。
spl_autoload_register(['Yii', 'autoload'], true, true);
// 包含class映射文件
Yii::$classMap = require(__DIR__ . '/classes.php');
// 初始化DI容器
Yii::$container = new yii\di\Container();

PS:這邊涉及到php的自動加載概念(https://www.php.net/manual/zh/function.spl-autoload-register)和設計模式-依賴注入(https://www.kancloud.cn/kancloud/yii-in-depth/50793)。數組

這個文件主要進行一些Yii源碼的初始化操做,這裏的class Yii只是繼承了BaseYii,沒有寫任何代碼,因此yii2的源碼都是在BaseYii裏面的,這裏留空是爲了給使用者自定義的。yii2

註冊自動加載

public static function autoload($className)
{
    // 從classMap裏尋找
    if (isset(static::$classMap[$className])) {
        $classFile = static::$classMap[$className];
        if ($classFile[0] === '@') {
            $classFile = static::getAlias($classFile);
        }
    } elseif (strpos($className, '\\') !== false) {
        // 命名空間訪問,先把命名空間的格式轉成路徑別名,例如: yii\base\Component 轉成 @yii/base/Component.php
        $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?");
    }
}

​ 簡單總結下,這個函數規定了一些規則讓php在尋找未知class時候能夠include對應的文件,規則以下:composer

​ (1) 在classMap裏面找框架

​ (2)若是class是使用命名空間訪問的(例如:yii\base\Component), 會按照@yii/base/Component.php這樣的路徑去加載。yii

規則1

規則1說從classMap裏面,那麼classMap是什麼呢,在入口文件裏能找到Yii::$classMap = require(__DIR__ . '/classes.php');,而後去看下classes.php是什麼樣子的:ide

return [
  'yii\base\Action' => YII2_PATH . '/base/Action.php',
  'yii\base\ActionEvent' => YII2_PATH . '/base/ActionEvent.php',
  .....
  ]

只是返回一個數組,key是class的名字,value是對應php文件的路徑(YII2_PATH是預約於的常量,表示當前目錄)。

if (isset(static::$classMap[$className])) {

因此Yii::autoload會先判斷是否在classMap裏面。

規則2(命名空間)

$classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false);

函數會先把命名空間形式的調用轉成對應的路徑別名,再用getAlias函數轉成對應的路徑。

路徑別名

再來看看static::getAlias()這個函數是怎麼把路徑別名轉成路徑的。

public static $aliases = ['@yii' => __DIR__]; // 預設的路徑別名映射數組
public static function getAlias($alias, $throwException = true)
{
    // 檢查是否有@前綴,沒有的話直接返回。
    if (strncmp($alias, '@', 1)) {
        // not an alias
        return $alias;
    }
    // 取出@alias部分賦值給root
    $pos = strpos($alias, '/');
    $root = $pos === false ? $alias : substr($alias, 0, $pos);
    // 檢查root是否在$aliases裏是否有對應的alias
    if (isset(static::$aliases[$root])) {
        // 在$aliases裏找到對應的@alias而後轉化成實際的路徑並返回
        if (is_string(static::$aliases[$root])) {
            return $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos);
        }
        // 若是找到的不是對應的路徑字符串,就變量這個數組,看看裏面有沒有對應的
        foreach (static::$aliases[$root] as $name => $path) {
            if (strpos($alias . '/', $name . '/') === 0) {
                return $path . substr($alias, strlen($name));
            }
        }
    }

    if ($throwException) {
        throw new InvalidParamException("Invalid path alias: $alias");
    }

    return false;
}

建立Web應用實例

$config = require(__DIR__ . '/../config/web.php'); // 加載配置文件
(new yii\web\Application($config))->run();

到這裏就開始涉及config配置的解析了,下一篇才慢慢分析。

相關文章
相關標籤/搜索