Yii源码阅读笔记
概述 通常我们会使用模板引擎来渲染HTML页面,而不是使用HTML代码中插入PHP代码的方式来编写动态页面。Yii框架中模板引擎也是作为组件引入的,默认ID为viewRenderer,但从Yii源码阅读笔记 - 组件集成可以看到Yii Web应用加载的核心组件中并没有viewRenderer
概述
通常我们会使用模板引擎来渲染HTML页面,而不是使用HTML代码中插入PHP代码的方式来编写动态页面。Yii框架中模板引擎也是作为组件引入的,默认ID为viewRenderer,
但从Yii源码阅读笔记 - 组件集成可以看到Yii Web应用加载的核心组件中并没有viewRenderer,所以需要自己配置。
Yii提供了一个直接可用的模板引擎组件类CPradoViewRenderer(见文件yii/framework/web/renderers/CPradoViewRenderer.php
),该模板引擎类让开发者可以使用类Prado框架的模板语法。
如果你想使用Smarty这种第三方模板引擎,有两种方式将模板引擎引入Yii中使用(以Smarty为例):
- 将Smarty封装成一个Yii的普通组件,然后配置加载到Yii::app()。假设组件ID为smarty,那么就可以通过
Yii::app()->smarty
来调用组件。 - 参考CPradoViewRenderer类的实现,将Smarty封装成一个模板引擎组件,并以ID为viewRenderer进行配置加载。
相比而言,第二种方式更好。原因是:第一种方式由于每种第三方模板引擎的接口不一样,如果应用要替换模板引擎,就需要修改控制器类中的代码。而第二种方式由于第三方组件统一封装成Yii框架定义的模板引擎接口形式, 所以如果要替换模板引擎,只需修改自定义模板引擎组件类的接口实现就可以了。这样调用模板引擎的代码逻辑就只依赖接口形式,而不是依赖于接口实现,从而实现解耦。
本文主要分析第二种方式的实现。
分析
Yii中对页面模板进行渲染可以调用CController
类(见文件yii/framework/web/CController.php
)的方法render
,实现如下:
/** * Renders a view with a layout. * * This method first calls {@link renderPartial} to render the view (called content view). * It then renders the layout view which may embed the content view at appropriate place. * In the layout view, the content view rendering result can be accessed via variable * <code>$content</code>. At the end, it calls {@link processOutput} to insert scripts * and dynamic contents if they are available. * * By default, the layout view script is "protected/views/layouts/main.php". * This may be customized by changing {@link layout}. * * @param string $view name of the view to be rendered. See {@link getViewFile} for details * about how the view script is resolved. * @param array $data data to be extracted into PHP variables and made available to the view script * @param boolean $return whether the rendering result should be returned instead of being displayed to end users. * @return string the rendering result. Null if the rendering result is not required. * @see renderPartial * @see getLayoutFile */ public function render($view,$data=null,$return=false) { // beforeRender默认返回true, // 可以在自定义controller类中重写该方法,实现渲染之前的预处理 // 但和beforeAction一样,应该要返回true或false if($this->beforeRender($view)) { // 渲染真正的内容部分 $output=$this->renderPartial($view,$data,true); // 获取布局文件 if(($layoutFile=$this->getLayoutFile($this->layout))!==false) // 渲染整个页面 $output=$this->renderFile($layoutFile,array('content'=>$output),true); // 渲染的后处理,默认为空,在processOutput之前调用 $this->afterRender($view,$output); // 对渲染结果进行处理 $output=$this->processOutput($output); // 可以将渲染结果作为方法的返回值返回,或者直接输出到用户浏览器 if($return) return $output; else echo $output; } }
其中方法renderPartial的实现如下所示:
/** * Renders a view. * * The named view refers to a PHP script (resolved via {@link getViewFile}) * that is included by this method. If $data is an associative array, * it will be extracted as PHP variables and made available to the script. * * This method differs from {@link render()} in that it does not * apply a layout to the rendered result. It is thus mostly used * in rendering a partial view, or an AJAX response. * * @param string $view name of the view to be rendered. See {@link getViewFile} for details * about how the view script is resolved. * @param array $data data to be extracted into PHP variables and made available to the view script * @param boolean $return whether the rendering result should be returned instead of being displayed to end users * @param boolean $processOutput whether the rendering result should be postprocessed using {@link processOutput}. * @return string the rendering result. Null if the rendering result is not required. * @throws CException if the view does not exist * @see getViewFile * @see processOutput * @see render */ public function renderPartial($view,$data=null,$return=false,$processOutput=false) { // 获取目标模板文件 $viewFile=$this->getViewFile($view); echo (basename(__FILE__).':'.__LINE__.':'.__FUNCTION__.'() $viewFile '. var_export($viewFile, true)); if(($viewFile)!==false) { // 渲染 $output=$this->renderFile($viewFile,$data,true); // 如果$processOutput为真,则也会对结果进行后处理 if($processOutput) $output=$this->processOutput($output); if($return) return $output; else echo $output; } else throw new CException(Yii::t('yii','{controller} cannot find the requested view "{view}".', array('{controller}'=>get_class($this), '{view}'=>$view))); }
renderPartial
方法并不会渲染出一个完整的页面,只是渲染页面的一部分,通常是主体部分,或者为AJAX请求渲染出响应结果。
其中调用的getViewFile方法实现如下:
public function getViewFile($viewName) { // 如果未配置theme项,即表示不使用theme,那么getTheme方法返回null if(($theme=Yii::app()->getTheme())!==null && ($viewFile=$theme->getViewFile($this,$viewName))!==false) return $viewFile; // viewPath默认为views,可配置 $moduleViewPath=$basePath=Yii::app()->getViewPath(); echo (basename(__FILE__).':'.__LINE__.':'.__FUNCTION__.'() $moduleViewPath '. var_export($moduleViewPath, true)),"\n"; // 模块化,如果没有,则getModule返回null if(($module=$this->getModule())!==null) $moduleViewPath=$module->getViewPath(); // $this->getViewPath()得到的路径相比$moduleViewPath就是多了controller的ID一级 return $this->resolveViewFile($viewName,$this->getViewPath(),$basePath,$moduleViewPath); }
代码中$this->getViewPath()
方法的实现如下:
public function getViewPath() { if(($module=$this->getModule())===null) $module=Yii::app(); // $this->getId()是得到当前controller的ID,这个ID是在controller实例化时构造方法中赋值给属性_id的。 // 这也就意味着页面模板文件需要按照controller的ID分目录存放 return $module->getViewPath().DIRECTORY_SEPARATOR.$this->getId(); }
getViewFile
中最后调用的方法resolveViewFile
实现如下所示:
public function resolveViewFile($viewName,$viewPath,$basePath,$moduleViewPath=null) { // 连模板文件名都不给,还玩个屁啊 if(empty($viewName)) return false; // 若$moduleViewPath未设置,则在应用的页面模板的根目录下找 if($moduleViewPath===null) $moduleViewPath=$basePath; // 获取设置的模板渲染引擎,其实就是加载ID为viewRenderer的组件 if(($renderer=Yii::app()->getViewRenderer())!==null) // 模板文件的扩展类型默认为'.php',可配置 $extension=$renderer->fileExtension; else $extension='.php'; echo (basename(__FILE__).':'.__LINE__.':'.__FUNCTION__.'() $extension '. var_export($extension, true)),"\n"; // 如果指定的模板文件名以/开始 if($viewName[0]==='/') { // 如果指定的模板文件名以//开始,则表示在模板的根目录下查找 if(strncmp($viewName,'//',2)===0) $viewFile=$basePath.$viewName; // 否则(以单个/开始)在模块的模板目录下查找 else $viewFile=$moduleViewPath.$viewName; } // 如果模板文件名中存在.且.不出现在第一个位置,则认为这是一个路径别名,需要转换真正的路径 elseif(strpos($viewName,'.')) $viewFile=Yii::getPathOfAlias($viewName); else // 否则在当前controller的模板目录下找 $viewFile=$viewPath.DIRECTORY_SEPARATOR.$viewName; // 可能站点是需要国际化的 // 所以在找到默认的模板文件后,尝试找一下对应用户目标语言的模板文件 if(is_file($viewFile.$extension)) return Yii::app()->findLocalizedFile($viewFile.$extension); // 如果不存在指定扩展类型的模板文件,且扩展类型不为'.php',则看一下'.php'类型的模板文件是否存在 elseif($extension!=='.php' && is_file($viewFile.'.php')) return Yii::app()->findLocalizedFile($viewFile.'.php'); else return false; }
方法resolveViewFile
中最后调用的方法findLocalizedFile
,定义于抽象类CApplication中,实现如下:
public function findLocalizedFile($srcFile,$srcLanguage=null,$language=null) { if($srcLanguage===null) // sourceLanguage为public的属性,可配置,默认为en_us $srcLanguage=$this->sourceLanguage; if($language===null) // getLanguage的实现:$this->_language===null ? $this->sourceLanguage : $this->_language // 默认_language为未赋值,即null,所以取到的还是sourceLanguage属性值。 // 但因为__set,所以也是可赋值的,这个赋值不应该是配置造成的,应该是根据用户的cookie中指定的语言选项,在请求处理时设置的,表示用户的目标语言 $language=$this->getLanguage(); // 如果用户的目标语言(或者用户选择的是默认语言),则直接返回默认模板文件的路径 if($language===$srcLanguage) return $srcFile; // 否则取到对应目标语言的模板文件 $desiredFile=dirname($srcFile).DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.basename($srcFile); // 如果对应目标语言的模板文件不存在,则还是返回默认的模板文件 return is_file($desiredFile) ? $desiredFile : $srcFile; }
从上述模板文件的寻找过程可以看到,最后返回的目标模板文件的路径是一个相对路径,以动态脚本根目录(默认为protected)开始。
方法renderPartial
中在得到目标模板文件相对路径后,即调用renderFile方法(定义于CBaseController类中)来渲染模板,该方法的实现如下:
public function renderFile($viewFile,$data=null,$return=false) { $widgetCount=count($this->_widgetStack); // Yii::app()->getViewRenderer() 获取模板引擎组件 if(($renderer=Yii::app()->getViewRenderer())!==null && $renderer->fileExtension==='.'.CFileHelper::getExtension($viewFile)) $content=$renderer->renderFile($this,$viewFile,$data,$return); else // 如果没法用模板引擎来渲染(可能不是模板引擎的目标模板,也可能是没设置模板引擎组件),则当前普通的PHP文件(HTML代码中夹杂着PHP代码)来渲染 $content=$this->renderInternal($viewFile,$data,$return); if(count($this->_widgetStack)===$widgetCount) return $content; else { $widget=end($this->_widgetStack); throw new CException(Yii::t('yii','{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.', array('{controller}'=>get_class($this), '{view}'=>$viewFile, '{widget}'=>get_class($widget)))); } }
renderFile
中$content=$renderer->renderFile($this,$viewFile,$data,$return);
一行调用的renderFile方法,在抽象类CViewRenderer中定义如下:
public function renderFile($context,$sourceFile,$data,$return) { if(!is_file($sourceFile) || ($file=realpath($sourceFile))===false) throw new CException(Yii::t('yii','View file "{file}" does not exist.',array('{file}'=>$sourceFile))); // 尝试从runtime目录中获取已编译好的模板,如果编译好的模板不是同一存放在runtime目录下,则默认和未编译的模板文件在同一个目录下,并且文件名多一个"c"后缀 // 得到$viewFile可能并不存在,第一次请求该模板 $viewFile=$this->getViewFile($sourceFile); // 如果相比已编译好的模板文件,未编译的模板已发生变更,则需要重新编译 // 如果已编译好的模板文件不存在,则@filemtime($viewFile)返回的是false,这个条件也是返回true if(@filemtime($sourceFile)>@filemtime($viewFile)) { // 抽象类CViewRenderer中generateViewFile方法并未实现,所以自己封装模板引擎组件时需要实现该方法 $this->generateViewFile($sourceFile,$viewFile); // 设置编译好的模板文件的访问权限,默认是0755 (owner rwx, group rx and others rx) @chmod($viewFile,$this->filePermission); } // 编译好的模板文件其实就是一个PHP脚本(HTML代码中夹杂PHP代码),所以还需要渲染一下 return $context->renderInternal($viewFile,$data,$return); }
类CController
的render
方法在调用renderPartial
得到渲染结果后,取得页面布局模板文件,然后将renderPartial
的渲染结果作为数据渲染布局模板,从而得到一个完整HTML页面。
获取布局模板文件路径的方法getLayoutFile
实现如下所示(定义于类CController中):
public function getLayoutFile($layoutName) { if($layoutName===false) return false; if(($theme=Yii::app()->getTheme())!==null && ($layoutFile=$theme->getLayoutFile($this,$layoutName))!==false) return $layoutFile; if(empty($layoutName)) { $module=$this->getModule(); // 递归向父级模板查找布局文件 while($module!==null) { if($module->layout===false) return false; if(!empty($module->layout)) break; $module=$module->getParentModule(); } // 如果当前controller不属于某个module if($module===null) $module=Yii::app(); // 默认为main,可配置 $layoutName=$module->layout; } elseif(($module=$this->getModule())===null) $module=Yii::app(); return $this->resolveViewFile($layoutName,$module->getLayoutPath(),Yii::app()->getViewPath(),$module->getViewPath()); }
其逻辑与方法getViewFile
类似。
由上述分析可知,将第三方模板引擎封装成Yii框架的模板引擎组件,可以继承自抽象类CViewRenderer
,并实现其方法generateViewFile
,然后配置该组件的ID为viewRenderer
。
对于模板文件的存放,需要考虑Web应用是否分模块、应用是否国际化、模板文件相关controller的ID等,模板文件名的扩展类型应与模板引擎组件配置的一样。
原文地址:Yii源码阅读笔记 - 模板引擎集成, 感谢原作者分享。

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

小紅書筆記怎麼刪除?在小紅書APP中是可以編輯筆記的,多數的用戶不知道小紅書筆記如何的刪除,接下來就是小編為用戶帶來的小紅書筆記刪除方法圖文教程,有興趣的用戶快來一起看看吧!小紅書使用教學小紅書筆記怎麼刪除1、先打開小紅書APP進入到主頁面,選擇右下角【我】進入到專區;2、之後在我的專區,點擊下圖所示的筆記頁面,選擇要刪除的筆記;3、進入到筆記頁面,右上角【三個點】;4、最後下方會展開功能欄,點選【刪除】即可完成。

作為一名小紅書的用戶,我們都曾經遇到過發布過的筆記突然不見了的情況,這無疑讓人感到困惑和擔憂。在這種情況下,我們該怎麼辦呢?本文將圍繞著「小紅書發布過的筆記不見了怎麼辦」這個主題,為你詳細解答。一、小紅書發布過的筆記不見了怎麼辦?首先,不要驚慌。如果你發現筆記不見了,保持冷靜是關鍵,不要慌張。這可能是由於平台系統故障或操作失誤引起的。檢查發布記錄很簡單。只要打開小紅書App,點擊“我”→“發布”→“所有發布”,就可以查看自己的發布記錄。在這裡,你可以輕鬆找到之前發布的筆記。 3.重新發布。如果找到了之

小紅書怎麼在筆記中添加商品連結?在小紅書這款app中用戶不僅可以瀏覽各種內容還可以進行購物,所以這款app中關於購物推薦、好物分享的內容是非常多的,如果小夥伴在這款app也是一個達人的話,也可以分享一些購物經驗,找到商家進行合作,在筆記中添加連結之類的,很多人都願意使用這款app購物,因為不僅方便,而且有很多達人會進行一些推薦,可以一邊瀏覽有趣內容,一邊看看有沒有適合自己的衣服商品。一起看看如何在筆記中添加商品連結吧!小紅書筆記添加商品連結方法 在手機桌面上開啟app。 在app首頁點擊

OracleAPI整合策略解析:實現系統間無縫通信,需要具體程式碼範例在當今數位化時代,企業內部系統之間需要相互通信和資料共享,而OracleAPI就是幫助實現系統間無縫通信的重要工具之一。本文將從OracleAPI的基本概念和原則入手,探討API整合的策略,最終給出具體的程式碼範例幫助讀者更好地理解和應用OracleAPI。一、OracleAPI基本

PHP程式碼在瀏覽器中如何顯示原始碼而不被解釋執行? PHP是一種伺服器端腳本語言,通常用於開發動態網頁。當PHP檔案在伺服器上被要求時,伺服器會解釋執行其中的PHP程式碼,並將最終的HTML內容傳送到瀏覽器以供顯示。然而,有時我們希望在瀏覽器中直接展示PHP檔案的原始碼,而不是被執行。本文將介紹如何在瀏覽器中顯示PHP程式碼的源碼,而不被解釋執行。在PHP中,可以使

在這篇文章中,我們將向你展示如何在WindowsPC上的沉浸式閱讀器中使用Microsoft閱讀教練。閱讀指導功能幫助學生或個人練習閱讀並培養他們的識字技能。你從閱讀支援的應用程式中的一段或一份文件開始,基於此,你的閱讀報告由閱讀教練工具產生。閱讀報告顯示了閱讀的準確性、閱讀所花費的時間、每分鐘的正確單字數,以及你在閱讀時發現最具挑戰性的單字。你還將能夠練習這些單詞,這總體上有助於培養你的閱讀技能。目前,僅有Office或Microsoft365(包括OneNoteforWeb和WordforWe

關於PPT蒙版,很多人肯定對它很陌生,一般人做PPT不會將牠吃透,而是湊活著可以做出來自己喜歡的就行,所以很多人都不知道PPT蒙版到底是什麼意思,也不知道這個蒙版有什麼作用,甚至更不知道它可以讓圖片變得不再那麼單調,想要學習的小伙伴們快來了學習學習,為你的PPT圖片上添上點吧PPT蒙版吧,讓它不再單調了。那麼,PPT蒙版該怎麼添呢?請往下看。 1.首先我們開啟PPT,選擇一張空白的圖片,之後右鍵點選【設定背景格式】,純色選擇顏色就行。 2.點選【插入】,藝術字,輸入字3.點選【插入】,點選【形狀】

可以使用瀏覽器的開發者工具來查看網站的源代碼,在Google Chrome瀏覽器中:1、開啟Chrome 瀏覽器,造訪要查看原始碼的網站;2、右鍵點選網頁上的任何位置,然後選擇「檢查」或按下快速鍵Ctrl + Shift + I開啟開發者工具;3、在開發者工具的頂部功能表列中,選擇「Elements」標籤;4、看到網站的HTML 和CSS 程式碼即可。
