Fork me on GitHub
随笔 - 87  文章 - 0  评论 - 308  阅读 - 42万

YII 的源码分析(-)

做为源码分析的首秀,我就挑了yii(读作歪依依而不是歪爱爱);它的赞美之词我就不多说了,直接入正题。先准备材料,建议直从官网下载yii的源码包(1.1.15)。

在demos里边有一个最简单的应用—helloworld.就是用yii框架输出一句话:”hello world”;

我就从它下手,分析框架执行一个最小流程要经过哪些组件,浅析它的运行过程。

首先从单一入口文件开始阅读。(源码一般都是从调用处开始分析)

 index.php 只有两行代码,非常的简单,就是导入yiibase类 ,启动应用。

// include Yii bootstrap file
require_once(dirname(__FILE__).'/../../framework/yii.php');

Yii::createWebApplication()->run();

//YiiBase is a helper class serving common framework functionalities.

//YiiBase是一个助手类,它服务于整个框架。YiiBase代码有点长. 主要完成一些常量的定义和初始化。

代码到这里似乎就终结了,页面的内容也程现出来,可是框架到底做了些什么,我们却一无所知。所以我们需要把这一步进行分解,把里边的细节暴露出来。

Yii::createWebApplication()->run() 一共可以分成三部分

  1. Yii
  2. createWebApplication
  3. run

Yii 这个东西是什么? 这个很容易,从yii.php 可以找到这样一行代码class Yii extends YiiBase,说明它就是YiiBase的继承类,而且作者的扩展是留空的,所以Yii就是YiiBase的一个引用而已。

眼下还有两件事要搞清楚,一是这个new 做了什么操作,二是run做了什么操作?

先看第一个问题,new操作干了些什么事。

    public static function createWebApplication($config=null)
    {
        return self::createApplication('CWebApplication',$config);
    }

它又把任务传递给了createApplication:

         public static function createApplication($class,$config=null)
         {
                   return new $class($config);
         }

 

结合起来看,createWebApplication () 就是return new CWebApplication($config); 所以new 就是返回了CWebApplication的实例.然而这个CWebApplication类又在哪呢?它又是怎么引入的呢?它执行了哪些操作?带着这些问题,我们再次回到YiiBase.php.

在YiiBase.php里边定义了一个很长的数组,你可以找到:

'CWebApplication' => '/web/CWebApplication.php',

说胆CWebApplication这个类是属于自动加载的。关于它是如何实现自动加载的,可以查看spl_autoload_register的相关文档,在这里,我们先只要知道它是Yii框架给我们自动加载进来的就可以了。

我们继续往CWebApplication这个里边深挖。打开/web/CWebApplication.php这个文件,居然没有构造函数,根据我的经验,用了new的类,一般有一个构造函数进行一些初始化工作的,于是我试着往父类找找看。从 class CWebApplication extends CApplication 可以看出,父类是CApplication. 它确实有一个构造函数,代码如下:

复制代码
public function __construct($config=null)
    {
        Yii::setApplication($this);

        // set basePath at early as possible to avoid trouble
        if(is_string($config))
            $config=require($config);

        if(isset($config['basePath']))
        {
            $this->setBasePath($config['basePath']);
            unset($config['basePath']);
        }
        else
            $this->setBasePath('protected');
        Yii::setPathOfAlias('application',$this->getBasePath());
        Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));
        if(isset($config['extensionPath']))
        {
            $this->setExtensionPath($config['extensionPath']);
            unset($config['extensionPath']);
        }
        else
            Yii::setPathOfAlias('ext',$this->getBasePath().DIRECTORY_SEPARATOR.'extensions');
        if(isset($config['aliases']))
        {
            $this->setAliases($config['aliases']);
            unset($config['aliases']);
        }

        $this->preinit();//预留

        $this->initSystemHandlers();//设置错误处理
        $this->registerCoreComponents(); //注册核心组件

        $this->configure($config); //加载配置文件

        $this->attachBehaviors($this->behaviors);//行为相关
        $this->preloadComponents(); //加载预加载组件

        $this->init(); 
    }
复制代码

这样,new的过程就完结了,没有什么特别的,下面我们重点来看看run做了哪些工作。在CWebApplication 找不到run方法,它来自父类CApplication:

复制代码
  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(); 因为前面和后面部分都是注册事件相关的,当前条件下执行不到。而CApplication中的processRequest方法是抽象的,所以猜测是在子类中进行实现的。又回到CWebApplication中,查找processRequest:

复制代码
    public function processRequest()
    {
        if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0]))
        {
            $route=$this->catchAllRequest[0];
            foreach(array_splice($this->catchAllRequest,1) as $name=>$value)
                $_GET[$name]=$value;
        }
        else
            $route=$this->getUrlManager()->parseUrl($this->getRequest());
        $this->runController($route);
    }
复制代码

注意重点在$this->runController($route);

复制代码
    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)));
    }
复制代码

我们要注意的代码只有两行:

$controller->init();

$controller->run($actionID);

这里的$controller可以能过查看createController得知,就是默认的控制器Sitecontroller.php

而Action则是index,你问我是怎么看出来的?哈哈,我在猜不出来的地方echo或var_dump一下不就可以了吗?这么简单的逻辑,还轮不到xdebug 这样的神器出场。后面我们分析路由的时候,会知道,没有指明的时候,系统会有一个默认的index作为Action.

显然,init什么也没有做,看看run做了什么

Sitecontroller中没有run方法,又要去它的父类中查找。从定义:class SiteController extends CController得出父类。

在CController中有这个方法:

复制代码
    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);
    }
复制代码

查看$this->createAction($actionID),得到return new CInlineAction($this,$actionID);

我们呆会再看这个CInlineAction,先看$this->runActionWithFilters($action,$this->filters());

 

复制代码
public function runActionWithFilters($action,$filters)
         {
                   if(empty($filters)){
                            $this->runAction($action);
                   }
                   else
                   {
                            $priorAction=$this->_action;

                            $this->_action=$action;

                            CFilterChain::create($this,$action,$filters)->run();

                            $this->_action=$priorAction;
                   }
         }
复制代码

显然$filters是空的,所以执行第一个表达式$this->runAction($action);

复制代码
public function runAction($action)
         {
                   $priorAction=$this->_action;
                   $this->_action=$action;
                   if($this->beforeAction($action))
                   {
                            if($action->runWithParams($this->getActionParams())===false){
                                     $this->invalidActionParams($action);
                            }
                            else{
                                     $this->afterAction($action);
                            }
                   }
                   $this->_action=$priorAction;
         }
复制代码

这段代码的重点是 $action->runWithParams($this->getActionParams())这一句;

这里的$action就是$this->createAction($actionID)返回的结果,而它的结果就是

return new CInlineAction($this,$actionID);

CInlineAction.php

是时候查看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();
         }
复制代码

 

哇哦,好高级,居然还用了反射,不过我喜欢!

不过呢,打印$method发现:

object(ReflectionMethod)#6 (2) {

 

["name"]=>

 

string(11) "actionIndex"

 

["class"]=>

 

string(14) "SiteController"

 

}

没有参数,所以此处代码相当于是执行了SiteController->actionIndex();

在class SiteController 中可以看到actionIndex 的定义 

 public function actionIndex()
         {
                   echo 'Hello World';
         }

于是就看到屏幕上那一句Hello World ,整个程序也就跑完了。也许有人要问了,为什么输出一句话还这么复杂,不是脱了裤子打屁吗? (请允许我的粗俗);

如果是这么简单的需求,当然不可能这么干。举这个例子,只是说明yii的基础流程,为下面的复杂应用做一个过渡。

Yii作为一个优秀的oop框架,这个例子只是介绍了它的继承,接口,mvc中的vc特性,关于数据模型,我将在后面的分析中陆续给出。最终的目标,是利用yii框架简化我们的开发过程。

 

到此,我们整理一下流程中的主要继承关系:

 

复制代码
class CComponent {
    public function __set($name,$value){}
    public function __get($name){}
}

abstract class CModule extends CComponent {
    function __construct(){
        echo 'CModule';
    }
}

abstract class CApplication extends CModule{
    function __construct(){
        echo 'CApplication';
    }

    public function run(){
        $this->processRequest();
    }
}

class CWebApplication extends CApplication{
    public $defaultController='site';
    public $layout='main';
    public function processRequest(){
        //$this->runController($route);
    }
}


$app = new CWebApplication->run();
复制代码

这个继承关系很重要,后面的分析都会用到。

好了,今天的分析就在到了,如果有什么不妥的,请留言,如果觉得有帮助,请顺手点个推荐!

posted on   bjtqti  阅读(1323)  评论(3编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示