Yii 1.1 雖已停止維護,但其清晰的架構設計、組件化思想、惰性載入機制,至今仍是學習 PHP 框架內核的優秀教材。本文以 demos/helloworld 為切入點,層層剝開從 index.php 到 SiteController::actionIndex() 的完整調用鏈,揭示框架如何將一個 HTTP 請求轉化為控制器動作執行。
require_once(dirname(__FILE__).'/../../framework/yii.php');
Yii::createWebApplication($config)->run();
spl_autoload_register(['YiiBase','autoload']):註冊 PSR-0 風格自動載入YII_PATH、YII_DEBUG 等)Yii::createWebApplication() → createApplication('CWebApplication') → new CWebApplication($config)public function __construct($config=null) {
Yii::setApplication($this); // 1. 設定全域應用實例
$this->setBasePath($config['basePath'] ?? 'protected'); // 2. 基礎路徑
Yii::setPathOfAlias('application', $this->getBasePath()); // 3. 路徑別名
Yii::setPathOfAlias('webroot', dirname($_SERVER['SCRIPT_FILENAME']));
$this->preinit(); // 4. 預初始化(留給擴充)
$this->initSystemHandlers(); // 5. 註冊錯誤/異常處理器
$this->registerCoreComponents(); // 6. 註冊核心組件(request, session, db...)
$this->configure($config); // 7. 套用設定檔屬性
$this->attachBehaviors($this->behaviors); // 8. 綁定行為
$this->preloadComponents(); // 9. 預載入組件
$this->init(); // 10. 子類初始化
}
public function run() {
$this->onBeginRequest(new CEvent($this)); // 觸發 onBeginRequest 事件
register_shutdown_function([$this, 'end']); // 註冊關閉函數
$this->processRequest(); // 核心:處理請求
$this->onEndRequest(new CEvent($this)); // 觸發 onEndRequest 事件
}
public function processRequest() {
if ($this->catchAllRequest) { // 全域捕獲(維護模式)
$route = $this->catchAllRequest[0];
foreach (array_splice($this->catchAllRequest,1) as $k=>$v) $_GET[$k]=$v;
} else {
$route = $this->getUrlManager()->parseUrl($this->getRequest()); // 解析路由
}
$this->runController($route); // 執行控制器
}
PATH_INFO 或 GET 參數 rrules 陣列進行雙向匹配(路由→URL、URL→路由)controllerID/actionID(如 site/index)public function runController($route) {
if (($ca = $this->createController($route)) !== null) {
list($controller, $actionID) = $ca;
$oldController = $this->_controller;
$this->_controller = $controller;
$controller->init(); // 控制器初始化
$controller->run($actionID); // 執行動作
$this->_controller = $oldController;
} else {
throw new CHttpException(404, ...);
}
}
createController():根據 controllerMap、模組、預設命名空間解析控制器類,實例化並回傳 [$controller, $actionID]run($actionID):建立 CInlineAction → runActionWithFilters() → runAction()public function runActionWithFilters($action, $filters) {
if (empty($filters)) {
$this->runAction($action); // 無過濾器直接執行
} else {
$this->_action = $action;
CFilterChain::create($this, $action, $filters)->run(); // 串聯過濾器
$this->_action = $priorAction;
}
}
過濾器支援前置/後置邏輯(存取控制、效能分析、快取等),CFilterChain 透過遞迴實現洋蔥模型。
public function runWithParams($params) {
$methodName = 'action' . $this->getId(); // actionIndex
$controller = $this->getController();
$method = new ReflectionMethod($controller, $methodName);
if ($method->getNumberOfParameters() > 0) {
return $this->runWithParamsInternal($controller, $method, $params); // 參數綁定
} else {
return $controller->$methodName(); // 無參數直接呼叫
}
}
亮點:使用 ReflectionMethod 自動綁定 $_GET/$_POST 參數到動作方法簽名,實現參數自動注入。
$this->render('view', $data)CController::render() → renderPartial() → CBaseController::renderFile()extract($data) 變數作用域隔離ob_start()) 捕獲內容,最終佈局檔案 main.php 包裝輸出| 設計模式 | Yii 實現位置 | 核心價值 |
|---|---|---|
| 單一入口 | index.php | 集中式請求分發、安全控制 |
| 應用單例 | Yii::app() / CApplication | 全域資源存取、組件管理 |
| 惰性載入 | CModule::getComponent() | 按需實例化、效能優化 |
| 事件驅動 | CComponent::raiseEvent() | 解耦業務邏輯、擴充點 |
| 行為混入 | CModel::attachBehaviors() | 水平復用、動態擴充 |
| 過濾器鏈 | CFilterChain | AOP 切面、請求預處理 |
| 反射參數綁定 | CInlineAction | 慣例優於配置、開發體驗 |
Yii 1.1 的核心思想——組件化、惰性載入、事件驅動、慣例優於配置——在 Laravel、Symfony、Yii2 中均有傳承與發展。閱讀經典源碼,不僅能理解框架運作,更能養成「框架思維」:如何設計可擴充的核心、如何平衡靈活與效能、如何讓開發者用得爽。