YII 的源码分析(-)

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

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

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

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

Index.php->

// include Yii bootstrap file

//引入启动文件

require_once(dirname(__FILE__).‘/../../framework/yii.php‘);

yii.php ->

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

//YiiBase是一个助手类,它服务于整个框架。 这里定义了许多重要的常量

require(dirname(__FILE__).‘/YiiBase.php‘);

//注册自动加载类

spl_autoload_register(array(‘YiiBase‘,‘autoload‘));

//导入接口类

require(YII_PATH.‘/base/interfaces.php‘);

//启动应用

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

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

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

  1. Yii
  2. createWebApplication
  3. run

Yii 这个东西是什么?

从yii.php 可以找到这样一行代码class Yii extends YiiBase

说明它就是YiiBase的继承类,而且作者的扩展是留空的,所以Yii就是YiiBase的一个引用而已。所以createWebApplication这个静态方法也得去YiiBase中查找了。

在YiiBase.php中,很容易就发现了这个方法:

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

这个CWebApplication类又在哪呢?它又是怎么引入的呢?

带着一系列的问题,我又回到了YiiBase.php

那里边定义了一个很长的数组,你可以找到:

‘CWebApplication‘ => ‘/web/CWebApplication.php‘,这就是自动加载类中的一员

关于它是如何实现自动加载的,可以查看spl_autoload_register的相关文档,此处就节外生枝了.

我们继续往CWebApplication这个里边深挖。打开/web/CWebApplication.php这个文件。

前面提到return new CWebApplication($config);根据我的经验,用了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();

}

$this下面的某些方法,在当前类是找不到的,因为它们可能是来自父类,最简单的方法就是ctrl+f搜索一下,没有就去类的声明处查看:

abstract class CApplication extends CModule

显然要进入CModule,如果此时还找不到想要方法,那么继续上一过程:

abstract class CModule extends CComponent

直到class CComponent

说明这就是当前这些家伙的老巢了。从代码的中注释中也可以看到:

CComponent is the base class for all components

说明我的想法是正确的,没错,它就是基类。

透过代码,我们可以发现,我们当前的应用,因为很多参数是空,所以很多逻辑都是直接跳过的。

看到这,$this的内容也大致明了啦。我们再回头看看

return new CWebApplication($config)->run();

通过前面的层层分析,此时的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(); 因为前面和后面部分都是注册事件相关的,当前条件下执行不到。

abstract public function processRequest(); 这个方法在当前类中是抽象的,所以肯定在它的子类中实现了。回去找CWebApplication:

         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 这样的神器出场。

显然,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 extends CController中可以看到actionIndex 的定义

 public function actionIndex()

         {

                   echo ‘Hello World‘;

         }

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

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

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

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

时间: 2024-08-02 13:55:15

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

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.异常

YII 的源码分析(二)

上一篇简单分析了一下yii的流程,从创建一个应用,到屏幕上输出结果.这一次我来一个稍复杂一点的,重点在输出上,不再是简单的一行"hello world",而是要经过view(视图)层的处理. 依然是demos目录,这次我们选择hangman,一个简单的猜字游戏.老规则,还是从入口处开始看. index.php: <?php // change the following paths if necessary $yii=dirname(__FILE__).'/../../frame

YII 的源码分析(三)

前面已经看完了启动一个yii程序所要经过的流程,以及渲染一个页面是怎么完成的.今天要分析的是yii是如何处理用户请求的.也就是控制和动作部分. 还是以helloworld为例演示这一过程.我们在地址栏输入http://localhost/study/yii/demos/helloworld/index.php,页面就显示了hello world. 前面的分析都是用的默认值,但是如果url有参数的时候,yii又是怎么处理的呢?带着这个问题,我们具体来分析一下. 在CWebApplication中有

yii源码分析4——非核心类的导入注册

转载请注明: TheViper http://www.cnblogs.com/TheViper  在yii源码分析1中说到spl_autoload_register注册给定的函数作为 __autoload 的实现,在这里是autoload(). public static function autoload($className) { include self::$_coreClasses [$className]; } 实际上这个autoload()是没有考虑非核心文件的引入的.比如,在app

jQuery源码分析系列(36) : Ajax - 类型转化器

什么是类型转化器? jQuery支持不同格式的数据返回形式,比如dataType为 xml, json,jsonp,script, or html 但是浏览器的XMLHttpRequest对象对数据的响应只有 responseText与responseXML 二种 所以现在我要定义dataType为jsonp,那么所得的最终数据是一个json的键值对,所以jQuery内部就会默认帮你完成这个转化工作 jQuery为了处理这种执行后数据的转化,就引入了类型转化器,如果没有指定类型就依据响应头Con

Yii2.0源码分析之——设置别名函数(setAlias)和获取别名函数(getAlias)

首先说说什么是别名.在Yii中有很多的路径,在开发的过程当前我们也会使用一些路径.一般来说都需要使用绝对路径,但绝对路径都很长.所以,为了方便的使用路径,可以在Yi中i给每个路径起个名称,这个名称就是别名.别名的格式: 别名必须以"@"字符开头,别名中还可以包含"/".如("@www"为根别名,"@www/test"就为子别名) 别名最后的目录分隔符("\"或者"/")都将去掉(如果有的

jQuery源码分析系列(35) : Ajax - jsonp的实现与原理

ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本 json核心就是:允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了. jquery ext dojo这类库的实现手段其实大同小异 在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但img.iframe.s

TeamTalk源码分析之login_server

login_server是TeamTalk的登录服务器,负责分配一个负载较小的MsgServer给客户端使用,按照新版TeamTalk完整部署教程来配置的话,login_server的服务端口就是8080,客户端登录服务器地址配置如下(这里是win版本客户端): 1.login_server启动流程 login_server的启动是从login_server.cpp中的main函数开始的,login_server.cpp所在工程路径为server\src\login_server.下表是logi

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A