欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

yii框架源码分析(三)

程序员文章站 2022-05-19 23:28:39
...
转载请注明:TheViper http://www.cnblogs.com/TheViper/

上一篇说到CWebApplication中的¥route=$this->getUrlManager ()->parseUrl ($this->getRequest());,得到$route=controler/actionid。

这篇说他后面的$this->runController ( $route );

 1 php  2class CWebApplication extends CApplication {  3public$controllerNamespace;  4private$_controllerPath;  5private$_viewPath;  6private$_systemViewPath;  7private$_controller;  8public$controllerMap=array();  9publicfunction processRequest() {//开始执行请求 10 //获取urlManager组件,解析请求,得到controller/action这种格式的string, 11 //并且将隐藏参数与请求的参数一一对应,匹配起来,写入$_REQUEST中12$route = $this->getUrlManager ()->parseUrl ($this->getRequest()); 13$this->runController ( $route ); 14 } 15publicfunction getRequest() {//获取request组件16return$this->getComponent ( 'request' ); 17 } 18protectedfunction registerCoreComponents() {//注册核心组件19 parent::registerCoreComponents (); 20 } 21//执行contronller22publicfunction runController($route) { 23if (($ca = $this->createController ( $route )) !== null) { 24list ( $controller, $actionID ) = $ca; 25$oldController = $this->_controller; 26$this->_controller = $controller; 27$controller->init ();//钩子,在执行action方法前调用,子类去实现28$controller->run ( $actionID );//开始转入controller类中action方法的执行29$this->_controller = $oldController; 30 } 31 } 32//创建controller类实例,从controller/action这种格式的string中解析出$controller, $actionID 33publicfunction createController($route, $owner = null) { 34if ($owner === null) 35$owner = $this; 36if (($route = trim ( $route, '/' )) === '') 37$route = $owner->defaultController; 3839$route .= '/'; 40while ( ($pos = strpos ( $route, '/' )) !== false ) { 41$id = substr ( $route, 0, $pos ); 42if (! preg_match ( '/^\w+$/', $id )) 43returnnull; 44$id = strtolower ( $id ); 45$route = ( string ) substr ( $route, $pos + 1 ); 46if (! isset ( $basePath )) // first segment47 { 48$basePath = $owner->getControllerPath (); 49$controllerID = ''; 50 } else { 51$controllerID .= '/'; 52 } 53$className = ucfirst ( $id ) . 'Controller'; 54$classFile = $basePath . DIRECTORY_SEPARATOR . $className . '.php'; 5556if (is_file ( $classFile )) { 57if (! class_exists ( $className, false )) 58require ($classFile); 59if (class_exists ( $className, false ) && is_subclass_of ( $className, 'CController' )) { 60$id [0] = strtolower ( $id [0] ); 61returnarray ( 62new$className ( $controllerID . $id, $owner === $this ? null : $owner ), 63$this->parseActionParams ( $route ) 64 ); 65 } 66returnnull; 67 } 68$controllerID .= $id; 69$basePath .= DIRECTORY_SEPARATOR . $id; 70 } 71 } 72protectedfunction parseActionParams($pathInfo) { 73if (($pos = strpos ( $pathInfo, '/' )) !== false) { 74$manager = $this->getUrlManager ();//再次获取urlManager,在上面第一次调用中已经导入。75$manager->parsePathInfo ( ( string ) substr ( $pathInfo, $pos + 1 ) ); 76$actionID = substr ( $pathInfo, 0, $pos ); 77return$manager->caseSensitive ? $actionID : strtolower ( $actionID ); 78 } else79return$pathInfo; 80 } 81publicfunction getControllerPath() { 82if ($this->_controllerPath !== null) 83return$this->_controllerPath; 84else85return$this->_controllerPath = $this->getBasePath () . DIRECTORY_SEPARATOR . 'controllers'; 86 } 87//两个钩子,子类去实现88publicfunction beforeControllerAction($controller, $action) { 89returntrue; 90 } 91publicfunction afterControllerAction($controller, $action) { 92 } 93protectedfunction init() { 94 parent::init (); 95 } 96 }

$ca = $this->createController ( $route ));createController的作用是将$route中的controller和action分离出来,并创建controller实例。

最后返回controller实例和actionid.

然后回到CWebApplication的runController($route),$controller->init ();在controller初始化时执行,这个需要在子类中重写,比如:

 1class VideoController extends CController {  2publicfunction init() {  3$this->db = Yii::app ()->db;  4 }  5publicfunction actionBroadcast() {  6$b = $this->db->query ( "", array (1 ) );  7$this->render ( "u_broadcast", array (  8 'b' => $b [0] ;  9 ) ); 10 } 11 }    

这样在VideoController中便可以用$this->db调用db组件了。

$controller->run ( $actionID );转入Ccontroller.

 1 php  2class CController {  3protected$db;  4public$defaultAction = 'index';  5private$_id;  6private$_action;  7publicfunction __construct($id, $module = null) {  8$this->_id = $id;  9 } 10publicfunction init() { 11 } 12//过滤方法,子类重写13publicfunction filters() { 14returnarray (); 15 } 16publicfunction run($actionID) { 17//创建action实例18if (($action = $this->createAction ( $actionID )) !== null) { 19$parent = Yii::app (); 20if ($parent->beforeControllerAction ( $this, $action )) { 21$this->runActionWithFilters ( $action, $this->filters () ); 22$parent->afterControllerAction ( $this, $action ); 23 } 24 } 25 } 26publicfunction refresh($terminate = true, $anchor = '') { 27$this->redirect ( Yii::app ()->getRequest ()->getUrl () . $anchor, $terminate ); 28 } 29publicfunction redirect($url, $terminate = true, $statusCode = 302) { 30 Yii::app ()->getRequest ()->redirect ( $url, $terminate, $statusCode ); 31 } 32//如果controller里面有filter33publicfunction runActionWithFilters($action, $filters) { 34if (empty ( $filters )) 35$this->runAction ( $action ); 36else { 37$priorAction = $this->_action; 38$this->_action = $action; 39 CFilterChain::create ( $this, $action, $filters )->run (); 40$this->_action = $priorAction; 41 } 42 } 43publicfunction runAction($action) { 44$priorAction = $this->_action; 45$this->_action = $action; 46if ($this->beforeAction ( $action )) { 47if ($action->runWithParams ( $this->getActionParams () ) === false) 48$this->invalidActionParams ( $action ); 49else50$this->afterAction ( $action ); 51 } 52$this->_action = $priorAction; 53 } 54//渲染视图55publicfunction render($view, $data = array()) { 56if (isset ( $data )) 57extract ( $data ); 58include VIEWS_DIR . "/" . $this->_id . "/" . $view . ".php"; 59 } 60publicfunction renderFile($file, $data = array()) { 61if (isset ( $data )) 62extract ( $data ); 63include VIEWS_DIR . "/" . $file; 64 } 65//跳转到另一个controller/action,不过浏览器的地址没有变66publicfunction forward($route) { 67if (strpos ( $route, '/' ) === false) 68$this->run ( $route ); 69else { 70//不在同一个controller里面,重新创建71 Yii::app ()->runController ( $route ); 72 } 73 } 74publicfunction getActionParams() { 75return$_GET; 76 } 77publicfunction createAction($actionID) { 78if ($actionID === '') 79$actionID = $this->defaultAction; 80if (method_exists ( $this, 'action' . $actionID ) && strcasecmp ( $actionID, 's' )) 81returnnew CInlineAction ( $this, $actionID ); 82 } 83publicfunction getAction() { 84return$this->_action; 85 } 86publicfunction setAction($value) { 87$this->_action = $value; 88 } 89publicfunction getId() { 90return$this->_id; 91 } 92//两个钩子93protectedfunction beforeAction($action) { 94returntrue; 95 } 96protectedfunction afterAction($action) { 97 } 98 }

$this->createAction ( $actionID );创建action实例.

然后是runActionWithFilters($action, $filters);如果没有filter(),直接runAction($action)。

$action->runWithParams ( $this->getActionParams () ).$action是CInlineAction实例。

 1 php  2class CInlineAction extends CAction  3{  4//执行该动作 5publicfunction run()  6 {  7$method='action'.$this->getId();  8$this->getController()->$method();  9 } 10//执行带提供的请求的参数的动作11publicfunction runWithParams($params) 12 { 13$methodName='action'.$this->getId();//拼接action方法14$controller=$this->getController(); 15$method=new ReflectionMethod($controller, $methodName);//反射16if($method->getNumberOfParameters()>0)//方法参数个数>017return$this->runWithParamsInternal($controller, $method, $params); 18else19return$controller->$methodName(); 20 } 21 }

CAction

 1 php  2abstractclass CAction extends CComponent  3{  4private$_id;  5private$_controller;  6publicfunction __construct($controller,$id)  7 {  8$this->_controller=$controller;  9$this->_id=$id; 10 } 11publicfunction getController() 12 { 13return$this->_controller; 14 } 15publicfunction getId() 16 { 17return$this->_id; 18 } 19//运行带有请求参数的对象。 这个方法通过CController::runAction()内部调用20publicfunction runWithParams($params) 21 { 22$method=new ReflectionMethod($this, 'run'); 23if($method->getNumberOfParameters()>0) 24return$this->runWithParamsInternal($this, $method, $params); 25else26return$this->run(); 27 } 28//执行一个带有命名参数的对象的方法29protectedfunction runWithParamsInternal($object, $method, $params) 30 { 31$ps=array(); 32foreach($method->getParameters() as$i=>$param) 33 { 34$name=$param->getName(); 35if(isset($params[$name])) 36 { 37if($param->isArray()) 38$ps[]=is_array($params[$name]) ? $params[$name] : array($params[$name]); 39elseif(!is_array($params[$name])) 40$ps[]=$params[$name]; 41else42returnfalse; 43 } 44elseif($param->isDefaultValueAvailable()) 45$ps[]=$param->getDefaultValue(); 46else47returnfalse; 48 } 49$method->invokeArgs($object,$ps);//反射,执行50returntrue; 51 } 52 }

这两个类都很简单,就是执行controller类中的action方法。

回到上面的runActionWithFilters($action, $filters);如果有filter(),CFilterChain::create ( $this, $action, $filters )->run ();

显然,如果有filter的话必须在执行action方法前,就设置好filter过滤器列表。

CFilterChain就是将类似于'application.filters.LoginFilter+upload_video' 这种配置解析成过滤器链。

过滤器链的每一项是一个CInlineFilter或CFilter实例。

 1 php  2//过滤器列表 3class CFilterChain extends CList {  4public$controller;  5public$action;  6public$filterIndex = 0;  7publicfunction __construct($controller, $action) {  8$this->controller = $controller;  9$this->action = $action; 10 } 11//创建过滤器列表12publicstaticfunction create($controller, $action, $filters) { 13$chain = new CFilterChain ( $controller, $action ); 14$actionID = $action->getId (); 15foreach ( $filtersas$filter ) { 16if (is_string ( $filter )) // filterName [+|- action1 action2]17 { 18if (($pos = strpos ( $filter, '+' )) !== false || ($pos = strpos ( $filter, '-' )) !== false) { 19$matched = preg_match ( "/\b{$actionID}\b/i", substr ( $filter, $pos + 1 ) ) > 0; 20if (($filter [$pos] === '+') === $matched) 21$filter = CInlineFilter::create ( $controller, trim ( substr ( $filter, 0, $pos ) ) ); 22 } else23$filter = CInlineFilter::create ( $controller, $filter ); 24 } elseif (is_array ( $filter )) // array('path.to.class [+|- action1, action2]','param1'=>'value1',...)25 { 26$filterClass = $filter [0]; 27unset ( $filter [0] ); 28//开始解析过滤器配置29if (($pos = strpos ( $filterClass, '+' )) !== false || ($pos = strpos ( $filterClass, '-' )) !== false) { 30preg_match ( "/\b{$actionID}\b/i", substr ( $filterClass, $pos + 1 ), $a ); 31$matched = preg_match ( "/\b{$actionID}\b/i", substr ( $filterClass, $pos + 1 ) ) > 0; 32//如果是filterName+action,创建一个过滤器,否则忽略33if (($filterClass [$pos] === '+') === $matched) { 34//解析出过滤器的类名35$filterClass = trim ( substr ( $filterClass, 0, $pos ) ); 36 } else37continue; 38 } 39$filter ['class'] = $filterClass; 40$filter = Yii::createComponent ( $filter ); 41 } 4243if (is_object ( $filter )) { 44$filter->init (); 45$chain->add ( $filter );//list添加过滤器46 } 47 } 48return$chain; 49 } 50publicfunction run() { 51if ($this->offsetExists ( $this->filterIndex )) {//过滤器列表个数不为0 52 //取出过滤器实例53$filter = $this->itemAt ( $this->filterIndex ++ ); 54$filter->filter ( $this ); 55 } else56$this->controller->runAction ( $this->action ); 57 } 58 }

'application.filters.LoginFilter+upload_video' 这种配置会创建CFilter实例。

 1 php  2class CFilter extends CComponent {  3publicfunction filter($filterChain) {  4//前置,后置方法 5if ($this->preFilter ( $filterChain )) {  6$filterChain->run ();  7$this->postFilter ( $filterChain );  8 }  9 } 10//钩子11publicfunction init() { 12 } 13protectedfunction preFilter($filterChain) { 14returntrue; 15 } 16protectedfunction postFilter($filterChain) { 17 } 18 }

然后是上面的CFilterChain::create ( $this, $action, $filters )->run ();中的run(),如果请求被解析成的action是upload_video,yii就会取出LoginFilter实例。

比如我的LoginFilter

 1 php  2class LoginFilter extends CFilter {  3protectedfunction preFilter($filterChain) {  4if (! isset ( $_SESSION )) {  5session_start ();  6 }  7if (isset ( $_SESSION ['user'] ))  8returntrue;  9else { 10setcookie ( "return", Yii::app ()->getRequest ()->getUrl (), time () + 360, '/' ); 11 Yii::app ()->getRequest ()->redirect ( 'http://localhost/youtube/login', true, 302 ); 12returnfalse; 13 } 14 } 15protectedfunction postFilter($filterChain) { 16 } 17} 18 ?>

里面就一个前置和后置,表示对于需要启用过滤器的action方法,分别在执行action方法之前和之后执行自己定义的preFilter,postFilter方法。

然后是$filterChain->run ();这里很容易出错。

其实是再次用CFilterChain里面的run(),注意到里面的$this->filterIndex++,这有点像递归.

如果过滤器要过滤对个action,就像这样去CFilter,然后$filterChain->run ();返回CFilterChain,同时$this->filterIndex++,然后继续$filterChain->run ();。。。。。。

对于我的'application.filters.LoginFilter+upload_video',只过滤upload_video这一个action,所以当返回CFilterChain时,$this->filterIndex已经变成1了,而过滤器列表只有一个过滤器实例,所以这次就会走$this->controller->runAction ( $this->action );了,这就和没设置过滤器时走的$this->runAction ( $action );一样了。

过滤器分析完后,就是什么数据操作之类的,最后是渲染视图render()。这就太简单了,extract($data),然后在include下视图文件就可以了。

还有forward()方法,就是跳转到另一个controller/action,实质就是返回CWebApplication的runController再来一遍上面分析的过程。

最后附上,裁剪的yii http://files.cnblogs.com/TheViper/framework.zip

以上就介绍了yii框架源码分析(三),包括了yii框架源码方面的内容,希望对PHP教程有兴趣的朋友有所帮助。