寫在前面,寫這些隨筆是記錄下自己看Yii2原始碼的程序,可能會有些流水賬,大部分決議放在注釋里說明,由于個人水平有限,有不正確的地方還望斧正,
以下原始碼版本基于Yii2的2.0.34版本,模板用的基礎版,
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');
入口檔案很簡單,做了一些初始化作業,具體看注釋,
接下來看加載的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 <[email protected]> * @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里面的,這里留空是為了給使用者自定義的,
注冊自動加載
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對應的檔案,規則如下:
? (1) 在classMap里面找
? (2)如果class是使用命名空間訪問的(例如:yii\base\Component), 會按照@yii/base/Component.php這樣的路徑去加載,
規則1
規則1說從classMap里面,那么classMap是什么呢,在入口檔案里能找到Yii::$classMap = require(__DIR__ . '/classes.php');,然后去看下classes.php是什么樣子的:
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配置的決議了,下一篇才慢慢分析,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/570.html
標籤:其他
