# 1、引言
## 1.1、Yii 简介
* Yii 的作者是美籍华人“薛强”,他原是 Prado 核心开发成员之一。2008 年薛强另起炉灶, 开发了 Yii 框架,于 2008 年 12 月 3 日发布了 Yii1.0 版本。
* Yii 是目前比较优秀的 PHP 框架之一,它的支持的特性包括:MVC、DAO/ActiveRecord、 I18N/L10N、caching、AJAX 支持、用户认证和基于角色的访问控制、脚手架、输入验证、部 件、事件、主题化以及 Web 服务等。
`Yii 的很多思想参考了其它一些比较优秀的 Web 框架(我们写东西时是不是也喜欢参考别人的? 有木有?嘿嘿,都喜欢站在别人的肩膀上干活!)`
## 1.2、本文内容与结构
本文对 Yii1.1.8 版本的源代码进行了深入的分析,本文的内容与结构为:
* 组件化与模块化:
* 对 Yii 的基于组件和事件驱动编程模式的基础类(CComponent)进行分析;
* 对组件化和模块化的工作原理进行分析;
* 对 WebApp 应用创建 Controller 流程等进行分析。
* 系统组件:对 Yii 框架自带的重要组件进行分析,主要包括:日志路由组件、Url 管理组 件、异常处理组件、Cache 组件、基于角色的访问控制组件等。
* 控制器层:控制器主要包含 Action 和 Filter,对 Action 与 Filter 的工作原理进行分析。
* 模型层:对 DAO 层、元数据和 Command 构造器、ORM 的原理进行分析
* 视图层:对视图层的渲染过程、Widget 和客户端脚本组件等进行分析
`本文档中的错误或不妥之处在所难免,殷切希望本文档的读者给予批评指正!`
# 2、组件化与模块化
## 2.1、框架加载和运行流程
* 首先从入口处分析index.php
```php
$yii=dirname(__FILE__).'/../framework/yii.php';
$config=dirname(__FILE__).'/protected/config/main.php';
require_once($yii); //引入yii框架
$application = Yii::createWebApplication($config) //先从这里追踪
$application->run();
```
$config 为配置文件,先看看如何加载配置文件内容的,
找到Yii类,该类没有任何内容,只是继承了Yiibase。
```
class Yii extends YiiBase{
}
```
* Yiibase.php
* 从Yiibase中找到了createWebApplication方法
调用createApplication并传递了参数'CWebApplication' 和 $config
```
public static function createWebApplication($config=null)
{
return self::createApplication('CWebApplication',$config);
}
```
* 返回并实例化 CWebApplication 对象,$config也传递了过去。
```
public static function createApplication($class,$config=null)
{
return new $class($config);
}
```
* CWebApplication.php,因为该类没有构造方法,所以找到父类的CApplication.php的构造方法
* 执行构造方法__construct
```
public function __construct($config=null)
{
Yii::setApplication($this); //把当前对象属性值赋值给Yiibase,这样便可通过Yii::app()调用main.php中的值
// set basePath at early as possible to avoid trouble
if(is_string($config))
$config=require($config); //引入main.php文件,并赋值给$config
//basePath为项目路径,检测路径是否正确,并赋值给$this->_basePath。
if(isset($config['basePath']))
{
$this->setBasePath($config['basePath']);
unset($config['basePath']);
}
else
$this->setBasePath('protected');
//初始化别名,用于import引入,如:Yii::import('system.web.*'); 或 'application.models.*'
Yii::setPathOfAlias('application',$this->getBasePath());
Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));
Yii::setPathOfAlias('ext',$this->getBasePath().DIRECTORY_SEPARATOR.'extensions');
$this->preinit();
//初始化类autoloader和错误处理程序。
$this->initSystemHandlers();
//注册核心应用程序组件。将db,messages等注册到CModule类的_componentConfig属性中
//例如:Yii::app()->db,Yii::app()->message
$this->registerCoreComponents();
//用指定的配置配置模块,即main.php中的所有配置注册到CModule中
$this->configure($config);
//将行为列表附加到组件。这里$this->behaviors为空数组
$this->attachBehaviors($this->behaviors);
//加载静态应用程序组件。
$this->preloadComponents();
$this->init();
}
```
* $this->registerCoreComponents();
```
//注册核心应用程序组件。将db,messages等注册到CModule类的_componentConfig属性中
protected function registerCoreComponents()
{
$components=array(
'coreMessages'=>array(
'class'=>'CPhpMessageSource',
'language'=>'en_us',
'basePath'=>YII_PATH.DIRECTORY_SEPARATOR.'messages',
),
'db'=>array(
'class'=>'CDbConnection',
),
'messages'=>array(
'class'=>'CPhpMessageSource',
),
'errorHandler'=>array(
'class'=>'CErrorHandler',
),
'securityManager'=>array(
'class'=>'CSecurityManager',
),
'statePersister'=>array(
'class'=>'CStatePersister',
),
'urlManager'=>array(
'class'=>'CUrlManager',
),
'request'=>array(
'class'=>'CHttpRequest',
),
'format'=>array(
'class'=>'CFormatter',
),
);
$this->setComponents($components);
}
```
* $this->configure($config);
```
//用指定的配置配置模块,即main.php中的所有配置注册到CModule中,
//配置中下标为class的注册到_componentConfig属性,其余的注册到_params属性
public function configure($config)
{
if(is_array($config))
{
foreach($config as $key=>$value)
$this->$key=$value;
}
}
```
* $this->preloadComponents();
```
//加载静态应用程序组件。main.php中
//reload'=>array('log'),,即预加载日志组件
//CLogRouter 对象信息(包含main.php中log['routes']的所有配置信息
//赋值给CModule->_components['CLogRouter ']
//当前可以调用Yii::log()来记录日志
protected function preloadComponents()
{
foreach($this->preload as $id)
$this->getComponent($id);
}
```
* $application->run();
```
//运行控制器
public function run()
{
if($this->hasEventHandler('onBeginRequest'))
$this->onBeginRequest(new CEvent($this));
register_shutdown_function(array($this,'end'),0,false);
//运行默认控制器,或获取url地址运行指定控制器
$this->processRequest();
if($this->hasEventHandler('onEndRequest'))
$this->onEndRequest(new CEvent($this));
}
```
* $this->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()); //获取url中的controller/action , 如message/list
//运行控制器
$this->runController($route);
}
```
* $this->runController($route);
```
public function runController($route)
{
//实例化控制器,如 new messageController
if(($ca=$this->createController($route))!==null)
{
list($controller,$actionID)=$ca;
$oldController=$this->_controller;
$this->_controller=$controller;
$controller->init();
//运行action方法
$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->run($actionID); 运行控制器中的actionID方法
```
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);
}
```
* 之后就会运行咱自己书写的控制机及方法