zoukankan      html  css  js  c++  java
  • yii源码分析3

    转载请注明:TheViper http://www.cnblogs.com/TheViper/

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

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

     1 <?php
     2 class CWebApplication extends CApplication {
     3     public $controllerNamespace;
     4     private $_controllerPath;
     5     private $_viewPath;
     6     private $_systemViewPath;
     7     private $_controller;
     8     public $controllerMap=array();
     9     public function processRequest() {//开始执行请求
    10         //获取urlManager组件,解析请求,得到controller/action这种格式的string,
    11         //并且将隐藏参数与请求的参数一一对应,匹配起来,写入$_REQUEST中
    12         $route = $this->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     //执行contronller
    22     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 segment
    47             {
    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         } else
    79             return $pathInfo;
    80     }
    81     public function getControllerPath() {
    82         if ($this->_controllerPath !== null)
    83             return $this->_controllerPath;
    84         else
    85             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 <?php
     2 class CController {
     3     protected $db;
     4     public $defaultAction = 'index';
     5     private $_id;
     6     private $_action;
     7     public function __construct($id, $module = null) {
     8         $this->_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里面有filter
    33     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             else
    50                 $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 <?php
     2 class CInlineAction extends CAction
     3 {
     4     //执行该动作
     5     public function run()
     6     {
     7         $method='action'.$this->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)//方法参数个数>0
    17             return $this->runWithParamsInternal($controller, $method, $params);
    18         else
    19             return $controller->$methodName();
    20     }
    21 }

    CAction

     1 <?php
     2 abstract class CAction extends CComponent
     3 {
     4     private $_id;
     5     private $_controller;
     6     public function __construct($controller,$id)
     7     {
     8         $this->_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         else
    26             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                 else
    42                     return false;
    43             }
    44             elseif($param->isDefaultValueAvailable())
    45             $ps[]=$param->getDefaultValue();
    46             else
    47                 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 <?php
     2 //过滤器列表
     3 class CFilterChain extends CList {
     4     public $controller;
     5     public $action;
     6     public $filterIndex = 0;
     7     public function __construct($controller, $action) {
     8         $this->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 ( "/{$actionID}/i", substr ( $filter, $pos + 1 ) ) > 0;
    20                     if (($filter [$pos] === '+') === $matched)
    21                         $filter = CInlineFilter::create ( $controller, trim ( substr ( $filter, 0, $pos ) ) );
    22                 } else
    23                     $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 ( "/{$actionID}/i", substr ( $filterClass, $pos + 1 ), $a );
    31                     $matched = preg_match ( "/{$actionID}/i", substr ( $filterClass, $pos + 1 ) ) > 0;
    32                     //如果是filterName+action,创建一个过滤器,否则忽略
    33                     if (($filterClass [$pos] === '+') === $matched) {
    34                         //解析出过滤器的类名
    35                         $filterClass = trim ( substr ( $filterClass, 0, $pos ) );
    36                     } else
    37                         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 )) {//过滤器列表个数不为0
    52             //取出过滤器实例
    53             $filter = $this->itemAt ( $this->filterIndex ++ );
    54             $filter->filter ( $this );
    55         } else
    56             $this->controller->runAction ( $this->action );
    57     }
    58 }

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

     1 <?php
     2 class CFilter extends CComponent {
     3     public function filter($filterChain) {
     4         //前置,后置方法
     5         if ($this->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 <?php
     2 class LoginFilter extends CFilter {
     3     protected function preFilter($filterChain) {
     4         if (! isset ( $_SESSION )) {
     5             session_start ();
     6         }
     7         if (isset ( $_SESSION ['user'] ))
     8             return true;
     9         else {
    10             setcookie ( "return", Yii::app ()->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

     

  • 相关阅读:
    Hibernate Tool建Entity
    MySQL新建用户和库表
    用友U8年度账结转 常用凭证丢失
    U890采购入库单修改供应商
    robocopy
    sql的left join 、right join 、inner join之间的区别
    Linux永久修改系统时间
    UF清log
    下拉式选单连动设定
    SQL取最大值编码(自动编码)
  • 原文地址:https://www.cnblogs.com/TheViper/p/4089495.html
Copyright © 2011-2022 走看看