YII 的源码分析(二)

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

依然是demos目录,这次我们选择hangman,一个简单的猜字游戏。老规则,还是从入口处开始看。

index.php:

<?php

// change the following paths if necessary
$yii=dirname(__FILE__).‘/../../framework/yii.php‘;
$config=dirname(__FILE__).‘/protected/config/main.php‘;

// remove the following line when in production mode
// defined(‘YII_DEBUG‘) or define(‘YII_DEBUG‘,true);

require_once($yii);
Yii::createWebApplication($config)->run();

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

<?php

return array(
    ‘name‘=>‘Hangman Game‘,
    ‘defaultController‘=>‘game‘,
    ‘components‘=>array(
        ‘urlManager‘=>array(
            ‘urlFormat‘=>‘path‘,
            ‘rules‘=>array(
                ‘game/guess/<g:\w>‘=>‘game/guess‘,
            ),
        ),
    ),
);

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

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

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

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

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

CWebApplication.php -> CApplication.php -> __construct($config) :

$this->preinit();

        $this->initSystemHandlers();
        $this->registerCoreComponents();

        $this->configure($config);
        $this->attachBehaviors($this->behaviors);
        $this->preloadComponents();

        $this->init();

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

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

    public function configure($config)
    {
        if(is_array($config))
        {
            foreach($config as $key=>$value)
                $this->$key=$value;
        }
    }

代码非常简单,就是把配置参数的键做为类的属性名,value做为类的属性值进行了扩展。完成这一过程就运行CApplication 上的run方法了。

    public function run()
    {
        if($this->hasEventHandler(‘onBeginRequest‘))
            $this->onBeginRequest(new CEvent($this));
        register_shutdown_function(array($this,‘end‘),0,false);
        $this->processRequest();
        if($this->hasEventHandler(‘onEndRequest‘))
            $this->onEndRequest(new CEvent($this));
    }

我们前面说过,这里只要关注 $this->processRequest(); 就可以了。运行的结果就是执行$this->runController(‘‘);

    public function runController($route)
    {
        if(($ca=$this->createController($route))!==null)
        {
            list($controller,$actionID)=$ca;
            $oldController=$this->_controller;
            $this->_controller=$controller;
            $controller->init();
            $controller->run($actionID);
            $this->_controller=$oldController;
        }
        else
            throw new CHttpException(404,Yii::t(‘yii‘,‘Unable to resolve the request "{route}".‘,
                array(‘{route}‘=>$route===‘‘?$this->defaultController:$route)));
    }

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

$controller->run($actionID); == gameController->run(‘‘); gameController上没有实现run方法,于是又是去父类中找run

从class GameController extends CController 可以看出,父类是CController , 找到相应的run方法:

public function run($actionID)
    {
        if(($action=$this->createAction($actionID))!==null)
        {
            if(($parent=$this->getModule())===null)
                $parent=Yii::app();
            if($parent->beforeControllerAction($this,$action))
            {
                $this->runActionWithFilters($action,$this->filters());
                $parent->afterControllerAction($this,$action);
            }
        }
        else
            $this->missingAction($actionID);
    }

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

runActionWithFilters --->  runAction --> $action->runWithParams

这里的$action 需要从CAction -> CInlineAction中去找
public function runWithParams($params)
    {
        $methodName=‘action‘.$this->getId();
        $controller=$this->getController();
        $method=new ReflectionMethod($controller, $methodName);
        if($method->getNumberOfParameters()>0)
            return $this->runWithParamsInternal($controller, $method, $params);
        else
            return $controller->$methodName();
    }

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

$controller->$methodName(); 也就是GameController->actionPlay()

到此,我们本节的重点才真正开始:
    public function actionPlay()
    {
        static $levels=array(
            ‘10‘=>‘Easy game; you are allowed 10 misses.‘,
            ‘5‘=>‘Medium game; you are allowed 5 misses.‘,
            ‘3‘=>‘Hard game; you are allowed 3 misses.‘,
        );

        // if a difficulty level is correctly chosen
        if(isset($_POST[‘level‘]) && isset($levels[$_POST[‘level‘]]))
        {
            $this->word=$this->generateWord();
            $this->guessWord=str_repeat(‘_‘,strlen($this->word));
            $this->level=$_POST[‘level‘];
            $this->misses=0;
            $this->setPageState(‘guessed‘,null);
            // show the guess page
            $this->render(‘guess‘);
        }
        else
        {
            $params=array(
                ‘levels‘=>$levels,
                // if this is a POST request, it means the level is not chosen
                ‘error‘=>Yii::app()->request->isPostRequest,
            );
            // show the difficulty level page
            $this->render(‘play‘,$params);
        }
    }
显然走的是else的逻辑,重点请看 $this->render(‘play‘,$params); 这个render方法这么面熟,很多框架中都有类似的方法,比如discuz,smarty,CI 等等. 纵观yii框架,rnder 在它整个MVC模式中,是V得以实现的重要骨干。所以有必要把它翻个底朝天。在CController.php中有这个方法:
    public function render($view,$data=null,$return=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);

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

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

            if($return)
                return $output;
            else
                echo $output;
        }
    }

当我们echo $output=$this->renderPartial($view,$data,true);的时候,就发现,此时的$output已经就拿到我们最终的结果了。它对应的文件是views/game/play.php

也就是我们在index.php上最终看到的内容了。由于本次渲染比较简单,所以程序经过的流程也较少,但是从源码中可以看到,里边进行了许多的处理,比如主题什么的。本次就先分析到这。晚安!

				
时间: 2024-11-05 18:33:54

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

netty 源码分析二

以服务端启动,接收客户端连接整个过程为例分析, 简略分为 五个过程: 1.NioServerSocketChannel 管道生成, 2.NioServerSocketChannel 管道完成初始化, 3.NioServerSocketChannel注册至Selector选择器, 4.NioServerSocketChannel管道绑定到指定端口,启动服务 5.NioServerSocketChannel接受客户端的连接,进行相应IO操作 Ps:netty内部过程远比这复杂,简略记录下方便以后回忆

[Android]Volley源码分析(二)Cache

Cache作为Volley最为核心的一部分,Volley花了重彩来实现它.本章我们顺着Volley的源码思路往下,来看下Volley对Cache的处理逻辑. 我们回想一下昨天的简单代码,我们的入口是从构造一个Request队列开始的,而我们并不直接调用new来构造,而是将控制权反转给Volley这个静态工厂来构造. com.android.volley.toolbox.Volley: public static RequestQueue newRequestQueue(Context conte

哇!板球 源码分析二

游戏主页面布局 创建屏下Score标签 pLabel = CCLabelTTF::create("Score", "Arial", TITLE_FONT_SIZE); //分数标签 //设置标签字体的颜色 pLabel->setColor (ccc3(0, 0, 0)); //设置文本标签的位置 pLabel->setPosition ( ccp ( SCORE_X, //X坐标 SCORE_Y //Y坐标 ) ); //将文本标签添加到布景中 this

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

                        YII 框架源码分析             百度联盟事业部--黄银锋   目 录 1. 引言 3 1.1.Yii 简介 3 1.2.本文内容与结构 3 2.组件化与模块化 4 2.1.框架加载和运行流程 4 2.2.YiiBase 静态类 5 2.3.组件 6 2.4.模块 9 2.5 .App 应用   10 2.6 .WebApp 应用   11 3.系统组件 13 3.1.日志路由组件  13 3.2.Url 管理组件  15 3.3.异常

baksmali和smali源码分析(二)

这一节,主要介绍一下 baksmali代码的框架. 我们经常在反编译android apk包的时候使用apktool这个工具,其实本身这个工具里面对于dex文件解析和重新生成就是使用的baksmali 和smali这两个jar包其中 baksmali是将 dex文件转换成便于阅读的smali文件的,具体使用命令如下:java -jar baksmali.jar classes.dex -o myout其中myout是输出的文件夹 而smali是将smali文件重新生成回 dex文件的具体使用的命

【梦幻连连连】源码分析(二)

转载请注明出处:http://blog.csdn.net/oyangyufu/article/details/24736711 GameLayer场景界面效果: 源码分析: GameLayer场景初始化,主要是初始化加载界面及背景音乐 bool GameLayer::init() { float dt=0.0f; if ( !CCLayerColor::initWithColor(ccc4(255, 255, 255, 255))) { return false; } this->initLoa

[Android]Fragment源码分析(二) 状态

我们上一讲,抛出来一个问题,就是当Activity的onCreateView的时候,是如何构造Fragment中的View参数.要回答这个问题我们先要了解Fragment的状态,这是Fragment管理中非常重要的一环.我们先来看一下FragmentActivity提供的一些核心回调: @Override protected void onCreate(Bundle savedInstanceState) { mFragments.attachActivity(this, mContainer,

JAVA Collection 源码分析(二)之SubList

昨天我们分析了ArrayList的源码,我们可以看到,在其中还有一个类,名为SubList,其继承了AbstractList. // AbstractList类型的引用,所有继承了AbstractList都可以传进来 private final AbstractList<E> parent; // 这个是其实就是parent的偏移量,从parent中的第几个元素开始的 private final int parentOffset; private final int offset; int s

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

Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Connector.Container等组件,接下来我们一起去大致的看看这些组件的作用和他们之间的相互联系.在这之前,我们先补充一个知识点,也就是Tomcat它实现的功能点是什么呢?通过查找一些资料,这里参考下极客时间<深入拆解Tomcat_Jetty>中的总结,即Tomcat 要实现 2 个核心功能: