Yii PHP 框架分析 (一)
作者:wdy

http://hi.baidu.com/delphiss/blog/item/f7da86d787adb72506088b4b.html

基于yii1.0.8的代码分析的。用了一个下午整理的,流水账,感兴趣的凑合着先看,国庆期间推出个整理修改版,然后再完成后两个部分(MVC和Yii的整体结构分析)。

1. 启动

网站的唯一入口程序 index.php :

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

上面的require_once($yii) 引用出了后面要用到的全局类Yii,Yii类是YiiBase类的完全继承:

class Yii extends YiiBase
{
}

系统的全局访问都是通过Yii类(即YiiBase类)来实现的,Yii类的成员和方法都是static类型。

2. 类加载

Yii利用PHP5提供的spl库来完成类的自动加载。在YiiBase.php 文件结尾处

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

将YiiBase类的静态方法autoload 注册为类加载器。 PHP autoload 的简单原理就是执行 new 创建对象或通过类名访问静态成员时,系统将类名传递给被注册的类加载器函数,类加载器函数根据类名自行找到对应的类文件并include 。

下面是YiiBase类的autoload方法:

public static function autoload($className)
{
// use include so that the error PHP file may appear
if(isset(self::$_coreClasses[$className]))
include(YII_PATH.self::$_coreClasses[$className]);
else if(isset(self::$_classes[$className]))
include(self::$_classes[$className]);
else
include($className.'.php');
}

可以看到YiiBase的静态成员$_coreClasses 数组里预先存放着Yii系统自身用到的类对应的文件路径:

private static $_coreClasses=array(
'CApplication' => '/base/CApplication.php',
'CBehavior' => '/base/CBehavior.php',
'CComponent' => '/base/CComponent.php',
...
)

非 coreClasse 的类注册在YiiBase的$_classes 数组中:
private static $_classes=array();

其他的类需要用Yii::import()讲类路径导入PHP include paths 中,直接
include($className.'.php')

3. CWebApplication的创建

回到前面的程序入口的 Yii::createWebApplication($config)->run();

public static function createWebApplication($config=null)
{
return new CWebApplication($config);
}

现在autoload机制开始工作了。

当系统 执行 new CWebApplication() 的时候,会自动 
include(YII_PATH.'/base/CApplication.php')

将main.php里的配置信息数组$config传递给CWebApplication创建出对象,并执行对象的run() 方法启动框架。

CWebApplication类的继承关系

CWebApplication -> CApplication -> CModule -> CComponent

$config先被传递给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']));
$this->preinit();
$this->initSystemHandlers();
$this->registerCoreComponents();
$this->configure($config);
$this->attachBehaviors($this->behaviors);
$this->preloadComponents();
$this->init();
}

Yii::setApplication($this); 将自身的实例对象赋给Yii的静态成员$_app,以后可以通过 Yii::app() 来取得。
后面一段是设置CApplication 对象的_basePath ,指向 proteced 目录。

Yii::setPathOfAlias('application',$this->getBasePath());
Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));

设置了两个系统路径别名 application 和 webroot,后面再import的时候可以用别名来代替实际的完整路径。别名配置存放在YiiBase的 $_aliases 数组中。

$this->preinit();
预初始化。preinit()是在 CModule 类里定义的,没有任何动作。

$this->initSystemHandlers() 方法内容:

/**
* Initializes the class autoloader and error handlers.
*/
protected function initSystemHandlers()
{
if(YII_ENABLE_EXCEPTION_HANDLER)
set_exception_handler(array($this,'handleException'));
if(YII_ENABLE_ERROR_HANDLER)
set_error_handler(array($this,'handleError'),error_reporting());
}

设置系统exception_handler和 error_handler,指向对象自身提供的两个方法。

4. 注册核心组件

$this->registerCoreComponents();
代码如下:

protected function registerCoreComponents()
{
parent::registerCoreComponents();
$components=array(
'urlManager'=>array(
'class'=>'CUrlManager',
),
'request'=>array(
'class'=>'CHttpRequest',
),
'session'=>array(
'class'=>'CHttpSession',
),
'assetManager'=>array(
'class'=>'CAssetManager',
),
'user'=>array(
'class'=>'CWebUser',
),
'themeManager'=>array(
'class'=>'CThemeManager',
),
'authManager'=>array(
'class'=>'CPhpAuthManager',
),
'clientScript'=>array(
'class'=>'CClientScript',
),
);
$this->setComponents($components);
}

注册了几个系统组件(Components)。
Components 是在 CModule 里定义和管理的,主要包括两个数组

private $_components=array();
private $_componentConfig=array();

每个 Component 都是 IApplicationComponent接口的实例,Componemt的实例存放在$_components 数组里,相关的配置信息存放在$_componentConfig数组里。配置信息包括Component 的类名和属性设置。

CWebApplication 对象注册了以下几个Component:urlManager, request,session,assetManager,user,themeManager,authManager,clientScript。CWebApplication的parent 注册了以下几个Component:coreMessages,db,messages,errorHandler,securityManager,statePersister。

Component 在YiiPHP里是个非常重要的东西,它的特征是可以通过 CModule 的 __get() 和 __set() 方法来访问。 Component 注册的时候并不会创建对象实例,而是在程序里被第一次访问到的时候,由CModule 来负责(实际上就是 Yii::app())创建。

5. 处理 $config 配置

继续, $this->configure($config);
configure() 还是在CModule 里:

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

实际上是把$config数组里的每一项传给 CModule 的 父类 CComponent __set() 方法。

public function __set($name,$value)
{
$setter='set'.$name;
if(method_exists($this,$setter))
$this->$setter($value);
else if(strncasecmp($name,'on',2)===0
&& method_exists($this,$name))
{
//duplicating getEventHandlers() here for performance
$name=strtolower($name);
if(!isset($this->_e[$name]))
$this->_e[$name]=new CList;
$this->_e[$name]->add($value);
}
else if(method_exists($this,'get'.$name))
throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',
array('{class}'=>get_class($this), '{property}'=>$name)));
else
throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
array('{class}'=>get_class($this), '{property}'=>$name)));
}
}

我们来看看:
if(method_exists($this,$setter))
根据这个条件,$config 数组里的basePath, params, modules, import, components 都被传递给相应的 setBasePath(), setParams() 等方法里进行处理。

6、$config 之 import

其中 import 被传递给 CModule 的 setImport:

public function setImport($aliases)
{
foreach($aliases as $alias)
Yii::import($alias);
}
Yii::import($alias)里的处理:
public static function import($alias,$forceInclude=false)
{
// 先判断$alias是否存在于YiiBase::$_imports[] 中,已存在的直接return, 避免重复import。
if(isset(self::$_imports[$alias])) // previously imported
return self::$_imports[$alias];
// $alias类已定义,记入$_imports[],直接返回
if(class_exists($alias,false))
return self::$_imports[$alias]=$alias;
// 类似 urlManager 这样的已定义于$_coreClasses[]的类,或不含.的直接类名,记入$_imports[],直接返回
if(isset(self::$_coreClasses[$alias]) || ($pos=strrpos($alias,'.'))===false) // a simple class name
{
self::$_imports[$alias]=$alias;
if($forceInclude)
{
if(isset(self::$_coreClasses[$alias])) // a core class
require(YII_PATH.self::$_coreClasses[$alias]);
else
require($alias.'.php');
}
return $alias;
}
// 产生一个变量 $className,为$alias最后一个.后面的部分
// 这样的:'x.y.ClassNamer'
// $className不等于 '*', 并且ClassNamer类已定义的, ClassNamer' 记入 $_imports[],直接返回
if(($className=(string)substr($alias,$pos+1))!=='*' && class_exists($className,false))
return self::$_imports[$alias]=$className;
// 取得 $alias 里真实的路径部分并且路径有效
if(($path=self::getPathOfAlias($alias))!==false)
{
// $className!=='*',$className 记入 $_imports[]
if($className!=='*')
{
self::$_imports[$alias]=$className;
if($forceInclude)
require($path.'.php');
else
self::$_classes[$className]=$path.'.php';
return $className;
}
// $alias是'system.web.*'这样的已*结尾的路径,将路径加到include_path中
else // a directory
{
set_include_path(get_include_path().PATH_SEPARATOR.$path);
return self::$_imports[$alias]=$path;
}
}
else
throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing directory or file.',
array('{alias}'=>$alias)));
}

7. $config 之 components

$config 数组里的 $components 被传递给CModule 的setComponents($components)

public function setComponents($components)
{
foreach($components as $id=>$component)
{
if($component instanceof IApplicationComponent)
$this->setComponent($id,$component);
else if(isset($this->_componentConfig[$id]))
$this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component);
else
$this->_componentConfig[$id]=$component;
}
}
$componen是IApplicationComponen的实例的时候,直接赋值:
$this->setComponent($id,$component),
public function setComponent($id,$component)
{
$this->_components[$id]=$component;
if(!$component->getIsInitialized())
$component->init();
}

如果$id已存在于_componentConfig[]中(前面注册的coreComponent),将$component 属性加进入。
其他的component将component属性存入_componentConfig[]中。

8. $config 之 params

这个很简单

public function setParams($value)
{
$params=$this->getParams();
foreach($value as $k=>$v)
$params->add($k,$v);
}

configure 完毕!

9. attachBehaviors

$this->attachBehaviors($this->behaviors);
空的,没动作

预创建组件对象

$this->preloadComponents();
protected function preloadComponents()
{
foreach($this->preload as $id)
$this->getComponent($id);
}

getComponent() 判断_components[] 数组里是否有 $id的实例,如果没有,就根据_componentConfig[$id]里的配置来创建组件对象,调用组件的init()方法,然后存入_components[$id]中。

10. init()

$this->init();

函数内:$this->getRequest();
创建了Reques 组件并初始化。

11. run()

public function run()
{
$this->onBeginRequest(new CEvent($this));
$this->processRequest();
$this->onEndRequest(new CEvent($this));
}

Yii PHP 框架分析 (一)的更多相关文章

  1. Yii PHP 框架分析(二)

    Yii PHP 框架分析(二)作者:wdy http://hi.baidu.com/delphiss/blog/item/54597af595085ad3f3d38552.html Yii是基于组件( ...

  2. Yii PHP 框架分析(四)

    作者:wdy http://hi.baidu.com/delphiss/blog/item/c15b314f05f9dfc0d0c86a26.html Yii应用的入口脚本最后一句启动了WebAppl ...

  3. Yii PHP 框架分析(三)

    作者:wdy http://hi.baidu.com/delphiss/blog/item/357663d152c0aa85a1ec9c44.html Yii应用的入口脚本引用出了Yii类,Yii类的 ...

  4. Android/Linux下CGroup框架分析及其使用

    1 cgroup介绍 CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制.记录.隔离进程组(process groups)所使用的资源( ...

  5. 几款开源的hybird移动app框架分析

    几款开源的Hybrid移动app框架分析 Ionic Onsen UI 与 ionic 相比 jQuery Mobile Mobile Angular UI 结论 很多移动开发者喜欢使用原生代码开发, ...

  6. 深入浅出 - Android系统移植与平台开发(十一) - Sensor HAL框架分析之一

    作者:唐老师,华清远见嵌入式学院讲师. 1. Sensor的概念 Sensor即传感器,在当前智能手机上大量存在:G-Sensor.LightsSensor. ProximitySensor.Temp ...

  7. 深入浅出 - Android系统移植与平台开发(八)- HAL Stub框架分析

    作者:唐老师,华清远见嵌入式学院讲师. 1. HAL Stub框架分析 HAL stub的框架比较简单,三个结构体.两个常量.一个函数,简称321架构,它的定义在:@hardware/libhardw ...

  8. openwrt: Makefile 框架分析

    openwrt: Makefile 框架分析 原文链接:blog.chinaunix.net/uid-26675482-id-4704952.html 本篇的主要目的是想通过分析Makefile,了解 ...

  9. Android 核心分析 之六 IPC框架分析 Binder,Service,Service manager

    IPC框架分析 Binder,Service,Service manager 我首先从宏观的角度观察Binder,Service,Service Manager,并阐述各自的概念.从Linux的概念空 ...

随机推荐

  1. Class TBoundlabel not found and so on..

    Class TBoundlabel not found when you put a labeledit into a panel of CategoryPanel then you'll found ...

  2. 安装Cocoa 新的依赖管理工具Carthage

    Cocoa的依赖管理器,我们已经有了CocoaPods,非常好用,那么为什么还要创建这样一个项目呢?本文翻译自Carthage的Github的README.md,带大家来了解一下这个工具有何不同之处. ...

  3. Servlet和Tomcat部署

    今天有空写个Servlet来玩一下.用EditPlus写一个简单的Servlet,FirstServlet.java,如下: import java.io.IOException; import ja ...

  4. Js Carousel

    http://getbootstrap.com/javascript/#carousel http://owlgraphic.com/owlcarousel/#demo https://www.mob ...

  5. 15个Docker基本命令及用法

    Docker入门教程:15个Docker基本命令及用法   本文中,我们将学习15个Docker命令以及命令的用法和功能,并通过实践学习它是如何工作的. AD:51CTO 网+ 第十二期沙龙:大话数据 ...

  6. 我的pch文件

    /** *  1. RGB背景色 */ #define PPCOLOR_RGB(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue: ...

  7. django开发总结:

    一,关于setting目录中的“DEBUG” DEBUG=False 把DEBUG从True改成False后就会出现(必需指定404和500错语页面,如上图的目录结构)找不到页面的错误.原因是DEBU ...

  8. POJ2103 Jackpot

    Description The Great Dodgers company has recently developed a brand-new playing machine. You put a ...

  9. 【整理】各种Python的IDE(集成开发环境)的总结和对比

    原地址:http://www.tuicool.com/articles/rMVJNn 原文  http://www.crifan.com/summary_common_python_ide_pyscr ...

  10. 【LA3523】 Knights of the Round Table (点双连通分量+染色问题?)

    Being a knight is a very attractive career: searching for the Holy Grail, saving damsels in distress ...