上一篇简单分析了一下yii的流程,从创建一个应用,到屏幕上输出结果。这一次我来一个稍复杂一点的,重点在输出上,不再是简单的一行"hello world",而是要经过view(视图)层的处理。

依然是demos目录,这次我们选择hangman,一个简单的猜字游戏。

和helloworld应用相比,这次多了main.php,打开main看下源码:

  1. <?php
  2.  
  3. return array(
  4. 'name'=>'Hangman Game',
  5. 'defaultController'=>'game',
  6. 'components'=>array(
  7. 'urlManager'=>array(
  8. 'urlFormat'=>'path',
  9. 'rules'=>array(
  10. 'game/guess/<g:\w>'=>'game/guess',
  11. ),
  12. ),
  13. ),
  14. );

在我们以后的实际项目中,也是经常要用到配置文件的,所以我觉得有必要了解一下yii的配置文件--main.php

'name'=>'这里通常是定义网站的标题',也就是我们打开index.php时,在网页上显示的标题。

'defaultController'=>'这里是默认的控制器',也就是我们的index.php后面没有指定控制器时系统采用的控制器,如果我们这里没有指出来,默认就是site

'components'=>'这里是组件的参数,用多维数组进行配置。' 具体的参数可以查看yii手册。

Yii::createWebApplication($config)->run(); 上一次我们已经详细分析过它了,这里就不再走一遍了。

上次我们没有配置过程,所以$this->configure($config)什么也没有做,但是这次有配置参数,所以我们进去看看yii做了哪些操作:

CApplication自己没有实现configure方法,是继承于CModule.php的:

  1. public function configure($config)
  2. {
  3. if(is_array($config))
  4. {
  5. foreach($config as $key=>$value)
  6. $this->$key=$value;
  7. }
  8. }

代码非常简单,就是把配置参数的键做为类的属性名,value做为类的属性值进行了扩展。

由于url是index.php,后面没有任何参数,所以都是走的默认控制器,也就是我们在main.php中设定的game. 所以$controller 就等于 controllers/gameController.php, 通过上次的源码分析我们可以知道,在gameController.php中没有init方法时,都是走的父类中定义的默认方法(实际上是一个空方法),

  1. $controller->run($actionID); == gameController->run('');

前面已经分析过了,没有指定时,都是默认参数。那么此时的$actionID为空,actionID就是gameController中定义的默认动作:public $defaultAction='play';

  1. runActionWithFilters ---> runAction --> $action->runWithParams

走了这么多过程,和hello world的流程是差不多的。据上次的分析可以知道,这里执行了

  1. $controller->$methodName(); 也就是GameController->actionPlay()
  2.  
  3. 到此,我们本节的重点才真正开始:
  1. public function actionPlay()
  2. {
  3. static $levels=array(
  4. '10'=>'Easy game; you are allowed 10 misses.',
  5. '5'=>'Medium game; you are allowed 5 misses.',
  6. '3'=>'Hard game; you are allowed 3 misses.',
  7. );
  8.  
  9. // if a difficulty level is correctly chosen
  10. if(isset($_POST['level']) && isset($levels[$_POST['level']]))
  11. {
  12. $this->word=$this->generateWord();
  13. $this->guessWord=str_repeat('_',strlen($this->word));
  14. $this->level=$_POST['level'];
  15. $this->misses=0;
  16. $this->setPageState('guessed',null);
  17. // show the guess page
  18. $this->render('guess');
  19. }
  20. else
  21. {
  22. $params=array(
  23. 'levels'=>$levels,
  24. // if this is a POST request, it means the level is not chosen
  25. 'error'=>Yii::app()->request->isPostRequest,
  26. );
  27. // show the difficulty level page
  28. $this->render('play',$params);
  29. }
  30. }
  1. 重点请看 $this->render('play',$params); 这个render方法这么面熟,很多框架中都有类似的方法,比如discuz,smarty,CI 等等. 纵观yii框架,render 在它整个MVC模式中,是V得以实现的重要骨干。所以有必要把它翻个底朝天。
  1. public function render($view,$data=null,$return=false)
  2. {
  3. if($this->beforeRender($view))
  4. {
  5. $output=$this->renderPartial($view,$data,true);
  6. if(($layoutFile=$this->getLayoutFile($this->layout))!==false)
  7. $output=$this->renderFile($layoutFile,array('content'=>$output),true);
  8.  
  9. $this->afterRender($view,$output);
  10.  
  11. $output=$this->processOutput($output);
  12.  
  13. if($return)
  14. return $output;
  15. else
  16. echo $output;
  17. }
  18. }

先看$output=$this->renderPartial($view,$data,true); 从字面上来看,就是渲染局部视图。

  1. public function renderPartial($view,$data=null,$return=false,$processOutput=false)
  2. {
  3.  
  4. if(($viewFile=$this->getViewFile($view))!==false)
  5. {
          //$viewFile=yii\demos\hangman\protected\views\game\play.php
  6. $output=$this->renderFile($viewFile,$data,true);
  7. if($processOutput)
  8. $output=$this->processOutput($output);
  9. if($return)
  10. return $output;
  11. else
  12. echo $output;
  13. }
  14. else
  15. throw new CException(Yii::t('yii','{controller} cannot find the requested view "{view}".',
  16. array('{controller}'=>get_class($this), '{view}'=>$view)));
  17. }

这里调用了renderFile来处理,这个renderfile哪里来的,要自己学会分析继承关系,不明白的看第一篇,我这里直接上源码了:

  1. public function renderFile($viewFile,$data=null,$return=false)
  2. {
  3. $widgetCount=count($this->_widgetStack);
  4. if(($renderer=Yii::app()->getViewRenderer())!==null && $renderer->fileExtension==='.'.CFileHelper::getExtension($viewFile)){
  5. $content=$renderer->renderFile($this,$viewFile,$data,$return);
  6. }
  7. else{
  8. $content=$this->renderInternal($viewFile,$data,$return);
  9. }
  10.  
  11. if(count($this->_widgetStack)===$widgetCount){
  12. return $content;
  13. }
  14. else
  15. {
  16. $widget=end($this->_widgetStack);
  17. 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.',
  18. array('{controller}'=>get_class($this), '{view}'=>$viewFile, '{widget}'=>get_class($widget))));
  19. }
  20. }

逻辑上走的是renderInternal方法:

  1. public function renderInternal($_viewFile_,$_data_=null,$_return_=false)
  2. {
  3. // we use special variable names here to avoid conflict when extracting data
  4. if(is_array($_data_)){
  5. extract($_data_,EXTR_PREFIX_SAME,'data');
  6. }
  7. else{
  8. $data=$_data_;
  9. }
  10. if($_return_)
  11. {
  12. ob_start();
  13. ob_implicit_flush(false);
  14. require($_viewFile_);
  15. return ob_get_clean();
  16. }
  17. else
  18. require($_viewFile_);
  19. }

为什么要用ob_start() ? 这个方法比较有意思。它非常巧妙的把play.php视图中的html的内容和php输出的内容组装在了一起。然后整个return 出去,非常值得借鉴的思想。

接着,我们再回到render。走余下的过程。

  1. $output=$this->renderFile($layoutFile,array('content'=>$output),true);
  1. 这里直接又调用了一次renderFile方法,这个$layoutFile是什么呢?就是yii\demos\hangman\protected\views\layouts\main.php
    通过上次的分析,$output就是main.php的内容了,不过呢,是结合了play.php的内容。这又是怎么做到的呢?
    这就是extract($_data_,EXTR_PREFIX_SAME,'data');所发挥的功能了。
    还有一个processOutput方法,输出前进行一些缓存什么的,这里先不深究它。

最后就是echo $output;因此屏幕上就有内容显示出来了。

也就是我们在index.php上最终看到的内容了。本次渲染比较简单,但是该走的逻辑都差不多用到了。

总结一下:视图层,是通过render方法进行渲染输出的。而render实际上是分成两部分来做的,一部分是局部视图,另一部分是通用视图。

类似于如下结构:

  1. <html><!--这里是通用视图-->
  2. <head>
  3. <title>xxx</title>
  4. </head>
  5. <body>
  6. {{这里是局部视图内容}}
  7. </body>
  8. </html>

render输出的是两者融合的内容。这样做的好处也是显而易见的,避免创建重复内容。

YII 的源码分析(二)的更多相关文章

  1. Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题

    4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...

  2. YII框架源码分析(百度PHP大牛创作-原版-无广告无水印)

           YII 框架源码分析    百度联盟事业部——黄银锋 目 录 1. 引言 3 1.1.Yii 简介 3 1.2.本文内容与结构 3 2.组件化与模块化 4 2.1.框架加载和运行流程 4 ...

  3. 框架-springmvc源码分析(二)

    框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...

  4. Tomcat源码分析二:先看看Tomcat的整体架构

    Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Conn ...

  5. 十、Spring之BeanFactory源码分析(二)

    Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...

  6. Vue源码分析(二) : Vue实例挂载

    Vue源码分析(二) : Vue实例挂载 author: @TiffanysBear 实例挂载主要是 $mount 方法的实现,在 src/platforms/web/entry-runtime-wi ...

  7. 多线程之美8一 AbstractQueuedSynchronizer源码分析<二>

    目录 AQS的源码分析 该篇主要分析AQS的ConditionObject,是AQS的内部类,实现等待通知机制. 1.条件队列 条件队列与AQS中的同步队列有所不同,结构图如下: 两者区别: 1.链表 ...

  8. YII 的源码分析(-)

    做为源码分析的首秀,我就挑了yii(读作歪依依而不是歪爱爱):它的赞美之词我就不多说了,直接入正题.先准备材料,建议直从官网下载yii的源码包(1.1.15). 在demos里边有一个最简单的应用—h ...

  9. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

随机推荐

  1. sublime 安装 插件

    从菜单 View - Show Console 或者 ctrl + ~ 快捷键,调出 console.将以下 Python 代码粘贴进去并 enter 执行,不出意外即完成安装.以下提供 ST3 和 ...

  2. Files 的值“<<<<<<< .mine”无效。路径中具有非法字符

    解决冲突,告诉SVN这个问题已解决(Resolved). 一般更简单些:在你的工程OBJ/DEBUG目录下,找到 工程名.csproj.FileListAbsolute.txt的文件打开并删除含有'& ...

  3. Torch7 Tensor切片总结

    1.narrow(k,m,n) 这个函数是选中第k维的从m行开始,供选中n行 2.sub(dim1s,dim1e[,dim2s,dim2e,..,dim4s,dim4e]) 功能最强大,可以切任意的一 ...

  4. iOS开发资源(持续更新)

    vm10虚拟机安装Mac OS X10.10教程 马上着手开发 iOS 应用程序 (Start Developing iOS Apps Today) Xcode使用教程详细讲解 (上) Xcode使用 ...

  5. HTML5 使用application cache 接口实现离线数据缓存

    1.配置缓存文件 cache manifest MIME TYPE:text/cache-manifest文件名称:name.appcache作用:用于配置需要缓存的文件 2.使用方法 在服务器上添加 ...

  6. JavaWeb中读取文件资源的路径问题

    在做javaweb开发的时候,我们可能会需要从本地硬盘上读取某一个文件资源,或者修改某一个文件,这个时候就需要先找到这个文件,然后用FileInputStrem等文件字节.字符流来将这个文件读取到内存 ...

  7. java并发编程(十七)内存操作总结

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17377197 主内存与工作内存 Java内存模型的主要目标是定义程序中各个变量的访问规则, ...

  8. Comparing the MSTest and Nunit Frameworks

    I haven't seen much information online comparing the similarities and differences between the Nunit ...

  9. 华为oj 刷题记录之合唱团

    华为OJ-合唱队 描述 计算最少出列多少位同学,使得剩下的同学排成合唱队形 说明: N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形. 合唱队形是指这样的一种队 ...

  10. ubuntu1404安装搜狗输入法后出现黑框的问题

    1.安装xcompmgr   sudo apt-get install xcompmgr 2.设置xcompmgr自动启动 mkdir-/.config/autostart vim xcompmgr. ...