yii源码分析3,yii源码分析转载请注明:theviper http://www.cnblogs.com/theviper/
上一篇说到cwebapplication中的¥route=$this->geturlmanager ()->parseurl ($this->getrequest());,得到$route=controler/actionid。
这篇说他后面的$this->runcontroller ( $route );
1 geturlmanager ()->parseurl ($this->getrequest());13 $this->runcontroller ( $route );14 }15 public function getrequest() {//获取request组件16 return $this->getcomponent ( 'request' );17 }18 protected function registercorecomponents() {//注册核心组件19 parent::registercorecomponents ();20 }21 //执行contronller22 public function runcontroller($route) {23 if (($ca = $this->createcontroller ( $route )) !== null) {24 list ( $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 33 public function createcontroller($route, $owner = null) {34 if ($owner === null)35 $owner = $this;36 if (($route = trim ( $route, '/' )) === '')37 $route = $owner->defaultcontroller;38 39 $route .= '/';40 while ( ($pos = strpos ( $route, '/' )) !== false ) {41 $id = substr ( $route, 0, $pos );42 if (! preg_match ( '/^\w+$/', $id ))43 return null;44 $id = strtolower ( $id );45 $route = ( string ) substr ( $route, $pos + 1 );46 if (! 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';55 56 if (is_file ( $classfile )) {57 if (! class_exists ( $classname, false ))58 require ($classfile);59 if (class_exists ( $classname, false ) && is_subclass_of ( $classname, 'ccontroller' )) {60 $id [0] = strtolower ( $id [0] );61 return array (62 new $classname ( $controllerid . $id, $owner === $this ? null : $owner ),63 $this->parseactionparams ( $route )64 );65 }66 return null;67 }68 $controllerid .= $id;69 $basepath .= directory_separator . $id;70 }71 }72 protected function parseactionparams($pathinfo) {73 if (($pos = strpos ( $pathinfo, '/' )) !== false) {74 $manager = $this->geturlmanager ();//再次获取urlmanager,在上面第一次调用中已经导入。75 $manager->parsepathinfo ( ( string ) substr ( $pathinfo, $pos + 1 ) );76 $actionid = substr ( $pathinfo, 0, $pos );77 return $manager->casesensitive ? $actionid : strtolower ( $actionid );78 } else79 return $pathinfo;80 }81 public function getcontrollerpath() {82 if ($this->_controllerpath !== null)83 return $this->_controllerpath;84 else85 return $this->_controllerpath = $this->getbasepath () . directory_separator . 'controllers';86 }87 //两个钩子,子类去实现88 public function beforecontrolleraction($controller, $action) {89 return true;90 }91 public function aftercontrolleraction($controller, $action) {92 }93 protected function init() {94 parent::init ();95 }96 }
$ca = $this->createcontroller ( $route ));createcontroller的作用是将$route中的controller和action分离出来,并创建controller实例。
最后返回controller实例和actionid.
然后回到cwebapplication的runcontroller($route),$controller->init ();在controller初始化时执行,这个需要在子类中重写,比如:
1 class videocontroller extends ccontroller { 2 public function init() { 3 $this->db = yii::app ()->db; 4 } 5 public function 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 _id = $id; 9 }10 public function init() {11 }12 //过滤方法,子类重写13 public function filters() {14 return array ();15 }16 public function run($actionid) {17 //创建action实例18 if (($action = $this->createaction ( $actionid )) !== null) {19 $parent = yii::app ();20 if ($parent->beforecontrolleraction ( $this, $action )) {21 $this->runactionwithfilters ( $action, $this->filters () );22 $parent->aftercontrolleraction ( $this, $action );23 }24 }25 }26 public function refresh($terminate = true, $anchor = '') {27 $this->redirect ( yii::app ()->getrequest ()->geturl () . $anchor, $terminate );28 }29 public function redirect($url, $terminate = true, $statuscode = 302) {30 yii::app ()->getrequest ()->redirect ( $url, $terminate, $statuscode );31 }32 //如果controller里面有filter33 public function runactionwithfilters($action, $filters) {34 if (empty ( $filters ))35 $this->runaction ( $action );36 else {37 $prioraction = $this->_action;38 $this->_action = $action;39 cfilterchain::create ( $this, $action, $filters )->run ();40 $this->_action = $prioraction;41 }42 }43 public function runaction($action) {44 $prioraction = $this->_action;45 $this->_action = $action;46 if ($this->beforeaction ( $action )) {47 if ($action->runwithparams ( $this->getactionparams () ) === false)48 $this->invalidactionparams ( $action );49 else50 $this->afteraction ( $action );51 }52 $this->_action = $prioraction;53 }54 //渲染视图55 public function render($view, $data = array()) {56 if (isset ( $data ))57 extract ( $data );58 include views_dir . / . $this->_id . / . $view . .php;59 }60 public function renderfile($file, $data = array()) {61 if (isset ( $data ))62 extract ( $data );63 include views_dir . / . $file;64 }65 //跳转到另一个controller/action,不过浏览器的地址没有变66 public function forward($route) {67 if (strpos ( $route, '/' ) === false)68 $this->run ( $route );69 else {70 //不在同一个controller里面,重新创建71 yii::app ()->runcontroller ( $route );72 }73 }74 public function getactionparams() {75 return $_get;76 }77 public function createaction($actionid) {78 if ($actionid === '')79 $actionid = $this->defaultaction;80 if (method_exists ( $this, 'action' . $actionid ) && strcasecmp ( $actionid, 's' ))81 return new cinlineaction ( $this, $actionid );82 }83 public function getaction() {84 return $this->_action;85 }86 public function setaction($value) {87 $this->_action = $value;88 }89 public function getid() {90 return $this->_id;91 }92 //两个钩子93 protected function beforeaction($action) {94 return true;95 }96 protected function afteraction($action) {97 }98 }
$this->createaction ( $actionid );创建action实例.
然后是runactionwithfilters($action, $filters);如果没有filter(),直接runaction($action)。
$action->runwithparams ( $this->getactionparams () ).$action是cinlineaction实例。
1 getid(); 8 $this->getcontroller()->$method(); 9 }10 //执行带提供的请求的参数的动作11 public function runwithparams($params)12 {13 $methodname='action'.$this->getid();//拼接action方法14 $controller=$this->getcontroller();15 $method=new reflectionmethod($controller, $methodname);//反射16 if($method->getnumberofparameters()>0)//方法参数个数>017 return $this->runwithparamsinternal($controller, $method, $params);18 else19 return $controller->$methodname();20 }21 }
caction
1 _controller=$controller; 9 $this->_id=$id;10 }11 public function getcontroller()12 {13 return $this->_controller;14 }15 public function getid()16 {17 return $this->_id;18 }19 //运行带有请求参数的对象。 这个方法通过ccontroller::runaction()内部调用20 public function runwithparams($params)21 {22 $method=new reflectionmethod($this, 'run');23 if($method->getnumberofparameters()>0)24 return $this->runwithparamsinternal($this, $method, $params);25 else26 return $this->run();27 }28 //执行一个带有命名参数的对象的方法29 protected function runwithparamsinternal($object, $method, $params)30 {31 $ps=array();32 foreach($method->getparameters() as $i=>$param)33 {34 $name=$param->getname();35 if(isset($params[$name]))36 {37 if($param->isarray())38 $ps[]=is_array($params[$name]) ? $params[$name] : array($params[$name]);39 elseif(!is_array($params[$name]))40 $ps[]=$params[$name];41 else42 return false;43 }44 elseif($param->isdefaultvalueavailable())45 $ps[]=$param->getdefaultvalue();46 else47 return false;48 }49 $method->invokeargs($object,$ps);//反射,执行50 return true;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 controller = $controller; 9 $this->action = $action;10 }11 //创建过滤器列表12 public static function create($controller, $action, $filters) {13 $chain = new cfilterchain ( $controller, $action );14 $actionid = $action->getid ();15 foreach ( $filters as $filter ) {16 if (is_string ( $filter )) // filtername [+|- action1 action2]17 {18 if (($pos = strpos ( $filter, '+' )) !== false || ($pos = strpos ( $filter, '-' )) !== false) {19 $matched = preg_match ( /\b{$actionid}\b/i, substr ( $filter, $pos + 1 ) ) > 0;20 if (($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];27 unset ( $filter [0] );28 //开始解析过滤器配置29 if (($pos = strpos ( $filterclass, '+' )) !== false || ($pos = strpos ( $filterclass, '-' )) !== false) {30 preg_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,创建一个过滤器,否则忽略33 if (($filterclass [$pos] === '+') === $matched) {34 //解析出过滤器的类名35 $filterclass = trim ( substr ( $filterclass, 0, $pos ) );36 } else37 continue;38 }39 $filter ['class'] = $filterclass;40 $filter = yii::createcomponent ( $filter );41 }42 43 if (is_object ( $filter )) {44 $filter->init ();45 $chain->add ( $filter );//list添加过滤器46 }47 }48 return $chain;49 }50 public function run() {51 if ($this->offsetexists ( $this->filterindex )) {//过滤器列表个数不为052 //取出过滤器实例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 prefilter ( $filterchain )) { 6 $filterchain->run (); 7 $this->postfilter ( $filterchain ); 8 } 9 }10 //钩子11 public function init() {12 }13 protected function prefilter($filterchain) {14 return true;15 }16 protected function postfilter($filterchain) {17 }18 }
然后是上面的cfilterchain::create ( $this, $action, $filters )->run ();中的run(),如果请求被解析成的action是upload_video,yii就会取出loginfilter实例。
比如我的loginfilter
1 getrequest ()->geturl (), time () + 360, '/' );11 yii::app ()->getrequest ()->redirect ( 'http://localhost/youtube/login', true, 302 );12 return false;13 }14 }15 protected function 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
http://www.bkjia.com/phpjc/909747.htmlwww.bkjia.comtruehttp://www.bkjia.com/phpjc/909747.htmltecharticleyii源码分析3,yii源码分析 转载请注明:theviperhttp://www.cnblogs.com/theviper/ 上一篇说到cwebapplication中的¥route=$this-geturlmanager ()-parseurl ($this-ge...