目錄
概述
分析
首頁 php教程 php手册 Yii源码阅读笔记

Yii源码阅读笔记

Jun 06, 2016 pm 08:14 PM
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为例):

  1. 将Smarty封装成一个Yii的普通组件,然后配置加载到Yii::app()。假设组件ID为smarty,那么就可以通过Yii::app()->smarty来调用组件。
  2. 参考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);
}
登入後複製

CControllerrender方法在调用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等,模板文件名的扩展类型应与模板引擎组件配置的一样。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

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

熱門文章

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

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

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1318
25
PHP教程
1268
29
C# 教程
1248
24
小紅書筆記怎麼刪除 小紅書筆記怎麼刪除 Mar 21, 2024 pm 08:12 PM

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

小紅書發布過的筆記不見了怎麼辦?它剛發的筆記搜不到的原因是什麼? 小紅書發布過的筆記不見了怎麼辦?它剛發的筆記搜不到的原因是什麼? Mar 21, 2024 pm 09:30 PM

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

小紅書怎麼在筆記中加入商品連結 小紅書在筆記中加入商品連結教學 小紅書怎麼在筆記中加入商品連結 小紅書在筆記中加入商品連結教學 Mar 12, 2024 am 10:40 AM

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

Oracle API整合策略解析:實現系統間無縫通信 Oracle API整合策略解析:實現系統間無縫通信 Mar 07, 2024 pm 10:09 PM

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

PHP程式碼在瀏覽器中如何顯示原始碼而不被解釋執行? PHP程式碼在瀏覽器中如何顯示原始碼而不被解釋執行? Mar 11, 2024 am 10:54 AM

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

如何在沉浸式閱讀器中使用Microsoft Reader Coach 如何在沉浸式閱讀器中使用Microsoft Reader Coach Mar 09, 2024 am 09:34 AM

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

PPT蒙版該怎麼添加 PPT蒙版該怎麼添加 Mar 20, 2024 pm 12:28 PM

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

網站線上看原始碼 網站線上看原始碼 Jan 10, 2024 pm 03:31 PM

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

See all articles