在YII中,程序入口是一个继承CApplication的CWebApplication的应用程序,在一个web请求的整个过程中,控制器,模型和视图都是由Application进行创建和控制。首先我们来看一下CWebApplication的类的继承结构:
从上面我们可以看到CWebApplication本身也是一个CModue。在YII中,模块之间是一个树形结构。即每一个模块都可以包含多个子模块,每一个子模块可以继续包含子模块.其中APP为树的头节点,如图:
描述的不是很清楚,但是上面流程图还是很清晰的吧。
对于读过YII源码都直到,任何一个web请求都是通过CApplication::Run()函数开始,进入到CWebApplication::processRequest()。源码分别如下:
//CApplication public function run() { if($this->hasEventHandler('onBeginRequest')) $this->onBeginRequest(new CEvent($this)); $this->processRequest();//=========== if($this->hasEventHandler('onEndRequest')) $this->onEndRequest(new CEvent($this)); }
//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);//+++++++++++++++++++++ }程序真正的进行到相应模块,控制器,动作是在函数processRequest的$this->runController($route);中,该函数传入的参数为$route。何为 $route。其实$route是一个类似A/B/C/D格式的字符串,该字符串中可能包含了模块,控制器,动作和$_GET相关参数。该函数是也是定义在CWebApplication中。代码如下:
//CWebApplication 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))); }通过该函数的名称字面意思为"运行控制器"。因此首先第一步就是要创建一个控制器,即函数createController($route)。我们知道控制器的是包含在模块中,因此也在该函数中,通过分析$route可以得到相应的模块,以及模块中的控制器和相应的动作,即list($controller,$actionID)=$ca;上面的流程图即为该函数createController($route)的函数流程图。
现在应该清楚了一个模块是怎么被web用户所调用了吧。下面我们说一下程序怎么在应用程序中添加模块,当然可能是通过配置文件。。。废话。
上面我们知道模块之间是呈现树形结构,在每一个模块的basePath里面默认有一个modules目录来存放该模块的子模块,每一个子模块为一个子目录,一直这样的递归下去。对于web应用程序来说,把模块的程序文件分到相应的目录下面,不代表就就可以被用户所调用,需要通过配置文件进行配置,从而可以通过分析URL得到。在官方的文档也讲的怎么配置一个子模块。下面我们用例子来讲解怎么配置模块。
1.假设当前Web中只有一个YII::APP应用程序,当然它也是一个模块,访问的方式为http://localhost/index.php,此时在配置文件main.php中配置的modules如下:
'modules'=>array(),2.假设YII::APP模块有两个子模块gii和test,此时我们就可以通过http://localhost/index.php/gii和http://localhost/index.php/test分别进行访问,此时配置文件为:
'modules'=>array( 'gii'=>array( 'class'=>'system.gii.GiiModule', 'password'=>'chenze', ), 'test', ),3.此时我们假设gii模块下面还有一个子模块giiTest,此时我们就可以通过http://localhost/index.php/gii/giiTest进行访问,此时配置为:
'modules'=>array( 'gii'=>array( 'class'=>'system.gii.GiiModule', 'password'=>'chenze', 'modules'=>array( 'giiTest' ), ), 'test', ),就这样递归下去咯
1. 首先模块是一个CModule类型的组件(CComponent),即如下:
abstract class CModule extends CComponent一个组件对象CComponent是一个可以用来挂载在组件上事件数组CComponet::$_event[]和行为数组CComponent::$_behavie[]的对象。因此也可以在CModule对象上挂载和模块相关的事件和行为信息。
2. 一个CModule对象包含了一些基本信息,如下:
3.一个CModule对象包含了一些基本操作,如下:
4.在CModule对象的构造函数中定义了一个模块的初始化过程,因此一个继承的自定义模块的构造函数要调用父类的构造函数parent::__construct($id,$parent,$config=null)来初始化一个模块。
public function __construct($id,$parent,$config=null)//$id为模块ID,$parent为父模块ID { $this->_id=$id; $this->_parentModule=$parent; // 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']); } Yii::setPathOfAlias($id,$this->getBasePath());//把该模块的路径添加到Alias中,方便访问 //下面几个函数就是模块的初始化需要做的几件事情,可以通过重写preinit()和init()来自定义模块可以通过自定义相应的函数来定制和初始化模块的参数 $this->preinit();//预初始化,一般用来设置模块相关的行为对象$behaviors[],预加载对象$preload[],和其他配置。 $this->configure($config);//加载配置 $this->attachBehaviors($this->behaviors);//挂载行为对象 $this->preloadComponents();//加载CModule::$preload[]中的预加载组件 $this->init();//模块真正初始化,一般情况下,重写该函数完整模块初始化 }
YII有cli和web两种运行模式,所有CApplication,CModule都有相应在web环境的下的实现类。对于模块,即CWebModule类,该类是平时自定义模块时候的直接父类。该类主要定义了一些和一个web模块相关的参数,比如该模块的默认控制器和动作,页面布局对象等,下面我们具体来分析一下:
1. CWebModule中定义了上述和web相关的变量,即
2.CWebModule中定义的操作不多,对于一个自定义的模块一般只需要重写CModule中的函数就可以定制。CWebModule只有两个函数可以进行覆盖,即如下:
public function beforeControllerAction($controller,$action) { if(($parent=$this->getParentModule())===null) $parent=Yii::app(); return $parent->beforeControllerAction($controller,$action); } public function afterControllerAction($controller,$action) { if(($parent=$this->getParentModule())===null) $parent=Yii::app(); $parent->afterControllerAction($controller,$action); }
public function beforeControllerAction($controller, $action) { if(parent::beforeControllerAction($controller, $action)) { // this method is called before any module controller action is performed // you may place customized code here return true; } else return false; }