cview类 public_Yii分析9:CController控制视图(Cview)的展现

《The Definitive Guide to Yii》描述了视图的概念:“视图是一个包含了主要的用户交互元素的PHP脚本.他可以包含PHP语句”,不同于很多类似的框架,Yii并没有使用smarty作为模板引擎,而是直接使用原生的PHP脚本作为模板,笔者认为至少有以下两点好处:

(1)门槛低,易于使用,不需要理解和记忆smarty的模板语言语法

(2)减少了模板引擎解析的过程,直接include,结合opcode缓存,效率可能会更高

当然也有缺点:

(1)没有smarty的前端过滤功能,前后端分离开发也有一定的门槛(前端工程师需要学习PHP语法,尤其是字符串处理相关函数)

(2)直接使用PHP,对于一些约束很难控制,比如全局的错误级别,如何控制逻辑处理与数据处理的界限?

因此,对于是使用PHP原生脚本还是使用smarty作为模板各有利弊,不论程序开发人员使用哪一种,都要了解这种方式的利弊才能更加安全有效地进行开发。

在Yii中,显示一个视图非常简单,即调用Ccontroller的方法render:

$this->render(‘viewName’);

而这个过程是如何实现的呢?我们看看render这个方法的源码:

/**

* 使用框架来渲染一个视图

*

* 这个方法首先调用{@link renderPartial}来渲染视图 (内容视图)

* 然后渲染框架视图(在合适的位置嵌入内容视图)

* 在框架视图中,内容视图的渲染结果以变量的方式来访问

* 最后,调用{@link processOutput}在可用时来插入脚本

* 和动态内容

*

* 默认的框架视图位置为:"protected/views/layouts/main.php".

* 可以通过设置{@link layout}.来自定义

*

* @param string $view 要被渲染的视图名称See {@link getViewFile} for details

* about how the view script is resolved.

* @param array $data 在视图中被解析为PHP变量的数据

* @param boolean $return 返回渲染的结果或者直接显示给最终用户

* @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事件,beforeRender直接返回true(子类覆盖自定义)

if($this->beforeRender($view))

{

//将$view的内容渲染后返回给变量$output

$output=$this->renderPartial($view,$data,true);

if(($layoutFile=$this->getLayoutFile($this->layout))!==false)

//将$output作为数据渲染整个框架

$output=$this->renderFile($layoutFile,array('content'=>$output),true);

//afterRender是一个空函数,子类覆盖自定义

$this->afterRender($view,$output);

//处理输出

$output=$this->processOutput($output);

//返回or直接输出

if($return)

return $output;

else

echo $output;

}

}

在render的过程中,用到了另外render函数:renderPartial和renderFile,renderPartial是CController的一个成员函数:

/**

* 渲染一个视图

*

* 视图名,指向一个PHP脚本通过{@link getViewFile}解析

* 如果$data是一个数组,会被解析为PHP的变量

* 可以在视图脚本中使用

*

* 这个方法不同于{@link render(),他不会使用框架(layout)来渲染

* 常用来渲染一个视图的一部分,或者AJAX响应

*

* @param string $view 被渲染的视图名称{@link getViewFile}

* @param array $data 被解析为PHP变量的数据,在视图脚本中可用

* @param boolean $return 渲染的结果是否被返回,否则直接展示给最终用户

* @param boolean $processOutput 是否使用{@link processOutput}来处理渲染结果

* @return string 渲染结果

* @throws CException 如果视图不存在会抛出异常

* @see getViewFile

* @see processOutput

* @see render

*/

public function renderPartial($view,$data=null,$return=false,$processOutput=false)

{

//获取视图路径

if(($viewFile=$this->getViewFile($view))!==false)

{

//调用renderFile来返回渲染结果

$output=$this->renderFile($viewFile,$data,true);

if($processOutput)

$output=$this->processOutput($output);

//返回or直接显示

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)));

}

renderFile并不是在CController中定义的,而是在CController的父类CBaseController中定义:

/**

* 渲染一个视图文件

*

* @param string $viewFile 视图文件路径view file path

* @param array $data 解析为变量的数据

* @param boolean $return 是否返回渲染结果

* @return string 渲染结果

* @throws CException if the view file does not exist

*/

public function renderFile($viewFile,$data=null,$return=false)

{

$widgetCount=count($this->_widgetStack);

//获取renderer类,同时检查视图文件扩展名,默认情况下,是没有自定义的viewRenderer对象的,Yii内置了抽象类CViewRenderer,如果自定义可以继承这个类

if(($renderer=Yii::app()->getViewRenderer())!==null && $renderer->fileExtension==='.'.CFileHelper::getExtension($viewFile))

//如果renderer的扩展名与视图文件扩展名相同

$content=$renderer->renderFile($this,$viewFile,$data,$return);

else

//调用renderInternal

$content=$this->renderInternal($viewFile,$data,$return);

//使用widget的堆栈计数,如果与widgetCount不一致,说明没有调用endWidget()来释放

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))));

}

}

renderInternal代码如下:

public function renderInternal($_viewFile_,$_data_=null,$_return_=false)

{

// we use special variable names here to avoid conflict when extracting data

if(is_array($_data_))

//如果data是数组,使用extract

extract($_data_,EXTR_PREFIX_SAME,'data');

else

//如果不是数组,直接赋给$data变量

$data=$_data_;

//如果需要返回,使用ob进行缓冲输出,之后再返回,否则直接调用require(如果文件不存在,会导致一个fatal error)

if($_return_)

{

ob_start();

ob_implicit_flush(false);

require($_viewFile_);

return ob_get_clean();

}

else

require($_viewFile_);

}

接着,我们来看一下在渲染玩view文件之后,处理输出的过程:

/**

*由{@link render()}调用处理最后的输出

* 这个方法在{@link render()}和{@link renderText()}的结尾调用

* 如果有注册的客户端脚本,这个方法会把他们在合适的位置

* 插入到输出中。如果有动态内容,也会被插入

* 这个方法可能会在页面的隐藏状态窗口中保存页面的持久状态

* @param string $output 由当前action产生的输出

* @return string 处理过的输出

*/

public function processOutput($output)

{

//插入客户端脚本

Yii::app()->getClientScript()->render($output);

// 如果使用了页面缓存,我们可以延迟动态内容的替换

if($this->_dynamicOutput!==null && $this->isCachingStackEmpty())

$output=$this->processDynamicOutput($output);

//页面状态的处理

if($this->_pageStates===null)

$this->_pageStates=$this->loadPageStates();

if(!empty($this->_pageStates))

$this->savePageStates($this->_pageStates,$output);

return $output;

}

public function processDynamicOutput($output)

{

if($this->_dynamicOutput)

{

//使用正则替换特殊字符

$output=preg_replace_callback('//',array($this,'replaceDynamicOutput'),$output);

}

return $output;

}

至此,整个render过程已经完成,总结下来,主要经历了数据解析和require,加载客户端脚本和动态数据两部分。

3

1

分享到:

18e900b8666ce6f233d25ec02f95ee59.png

72dd548719f0ace4d5f9bca64e1d7715.png

2011-11-30 00:01

浏览 4532

评论

2 楼

silentime

2011-12-13

z.y.f 写道

先赞一个~感觉smarty本身在前后端开发分离上的作用不大,倒是可以防止出现view层出现过多的逻辑代码,可以规范一下view代码,使之只负责数据展示。。。

966903dea4bcb507358d5dcce8b912e5.gif

1 楼

z.y.f

2011-12-10

先赞一个~感觉smarty本身在前后端开发分离上的作用不大,倒是可以防止出现view层出现过多的逻辑代码,可以规范一下view代码,使之只负责数据展示。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值