程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> yii源碼分析3,yii源碼分析

yii源碼分析3,yii源碼分析

編輯:關於PHP編程

yii源碼分析3,yii源碼分析


轉載請注明: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 ( "/\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                 } 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 ( "/\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                     } 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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved