接續上篇,本文分析 yii\web\View 剩餘關鍵方法:視圖檔案追蹤、前後置渲染事件、PHP 原生模板渲染、動態內容快取佔位符、區塊/內容裝飾/片段快取/頁面生命週期等輔助方法。
public function getViewFile() {
return end($this->_viewFiles); // 回傳當前正在渲染的視圖檔路徑
}
_viewFiles 陣列在 renderFile() 進入時 array_push,結束時 array_pop,支援巢狀渲染追蹤。
public function beforeRender($viewFile, $params) {
$event = new ViewEvent(['viewFile' => $viewFile, 'params' => $params]);
$this->trigger(self::EVENT_BEFORE_RENDER, $event);
return $event->isValid; // false 可中止渲染
}
public function afterRender($viewFile, $params, &$output) {
if ($this->hasEventHandlers(self::EVENT_AFTER_RENDER)) {
$event = new ViewEvent(['viewFile' => $viewFile, 'params' => $params, 'output' => $output]);
$this->trigger(self::EVENT_AFTER_RENDER, $event);
$output = $event->output; // 允許事件修改輸出內容
}
}
應用場景:EVENT_BEFORE_RENDER 可注入通用變數、權限檢查;EVENT_AFTER_RENDER 可進行 HTML 壓縮、關鍵字替換、CSP nonce 注入。
public function renderPhpFile($_file_, $_params_ = []) {
ob_start();
ob_implicit_flush(false);
extract($_params_, EXTR_OVERWRITE); // 參數轉為區域變數
require($_file_); // 直接執行 PHP 檔案
return ob_get_clean(); // 取得輸出並關閉緩衝
}
這是 View 渲染的最底層實作,所有 render()/renderFile() 最終皆呼叫此方法。extract(EXTR_OVERWRITE) 確保同名變數以新值覆蓋。
片段快取/頁面快取時,部分需即時更新的內容(如 CSRF token、用戶名、購物車數量)不應被快取。Yii2 採用佔位符替換策略:
public function renderDynamic($statements) {
if (!empty($this->cacheStack)) { // 正在快取中
$n = count($this->dynamicPlaceholders);
$placeholder = "<![CDATA[YII-DYNAMIC-$n]]>";
$this->addDynamicPlaceholder($placeholder, $statements);
return $placeholder; // 回傳佔位符寫入快取
}
return $this->evaluateDynamicContent($statements); // 非快取模式直接執行
}
public function addDynamicPlaceholder($placeholder, $statements) {
foreach ($this->cacheStack as $cache) {
$cache->dynamicPlaceholders[$placeholder] = $statements; // 向外層所有快取登記
}
$this->dynamicPlaceholders[$placeholder] = $statements;
}
public function evaluateDynamicContent($statements) {
return eval($statements); // 直接執行 PHP 代碼字串
}
流程:
= $this->renderDynamic('return Yii::$app->request->csrfToken;') ?>。<![CDATA[YII-DYNAMIC-0]]> 佔位符,並將 PHP 代碼登記到所有層級快取的 dynamicPlaceholders。FragmentCache::getCachedContent()),將快取內容中的佔位符替換為 eval() 執行結果。| 方法 | 用途 | 對應 Widget |
|---|---|---|
beginBlock($id, $renderInPlace) | 開始記錄具名區塊,稍後在佈局中輸出 | Block |
endBlock() | 結束區塊記錄 | Block |
beginContent($viewFile, $params) | 開始內容裝飾(巢狀佈局),$viewFile 為外層佈局 | ContentDecorator |
endContent() | 結束內容裝飾 | ContentDecorator |
beginCache($id, $properties) | 開始片段快取,回傳 false 表示命中快取無需重新生成 | FragmentCache |
endCache() | 結束片段快取並儲存 | FragmentCache |
beginPage() | 頁面開始,啟動輸出緩衝,觸發 EVENT_BEGIN_PAGE | - |
endPage() | 頁面結束,觸發 EVENT_END_PAGE,刷新輸出緩衝 | - |
// 區塊
<div class="sidebar">...</div>
// 佈局中:= $this->blocks['sidebar'] ?? '' ?>
// 內容裝飾(巢狀佈局)
// 內容區內容
// 片段快取
// 昂貴的選單生成邏輯
// 頁面生命週期(main.php 佈局)
<!DOCTYPE html>
<html>
<head>...</head>
<body>
= $content ?>
</body>
</html>
beginPage() ob_start(),endPage() ob_end_flush(),確保 EVENT_END_PAGE 事件可修改最終輸出(如注入腳本、壓縮 HTML)。cacheStack 陣列維護巢狀快取層級,動態內容佔位符需向所有外層快取登記,確保多層快取正確替換。beforeRender/afterRender、BEGIN_PAGE/END_PAGE 讓行為擴充與核心邏輯解耦。eval() 僅用於開發者可控的動態內容代碼,不接受用戶輸入。View 類別是 Yii2 視圖層的核心協調者,除模板渲染外,還統一管理快取、動態內容、區塊、佈局裝飾、頁面生命週期。理解其內部機制,能更靈活地使用進階功能、排查渲染異常、編寫高效能視圖元件。