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

yii源碼分析1,yii源碼分析

編輯:關於PHP編程

yii源碼分析1,yii源碼分析


轉載請注明:TheViper http://www.cnblogs.com/TheViper/

本文將對yii中的mvc,路由器,filter,組件機制等最主要的部分進行自己的一點淺析,力求說明自己做一個php mvc不是那麼的遙不可及,其實是很簡單的。

源碼基於yii 1.13,為了方便說明,我對其進行了大量的裁剪,不過還是讓他保有上面的那些最重要的功能。裁剪下來,其實沒有幾個文件了,而且每個文件代碼最多100多行,避免因為代碼太多而懶得看。

 

所謂的mvc都是讓所有請求從一個地方進去,通過對請求,配置的解析,分發到對應的類方法中。

 首先當然是入口文件,index.php.

1 <?php
2 $app = "app";
3 $yii = dirname ( __FILE__ ) . '/framework/yii.php';
4 $config = dirname ( __FILE__ ) . '/app/protected/config/main.php';//載入配置
5 require_once ($yii);
6 Yii::createWebApplication ( $config )->run ();

引入yii.php

1 <?php
2 define ( "VIEWS_DIR", "$app/protected/views/" );
3 define ( "CONTROLLERS_DIR", "$app/protected/controllers/" );
4 require(dirname(__FILE__).'/YiiBase.php');
5 class Yii extends YiiBase
6 {
7 }

原來yii是個空的類啊,去看YiiBase.

  1 <?php
  2 defined ( 'YII_PATH' ) or define ( 'YII_PATH', dirname ( __FILE__ ) );
  3 class YiiBase {
  4     public static $classMap = array ();
  5     public static $enableIncludePath = true;
  6     private static $_aliases = array (
  7             'system' => YII_PATH 
  8     ); // alias => path
  9     private static $_imports = array (); // alias => class name or directory
 10     private static $_includePaths; // list of include paths
 11     private static $_app;
 12     private static $_logger;
 13     public static function createWebApplication($config = null) {
 14         return self::createApplication ( 'CWebApplication', $config );
 15     }
 16     public static function createApplication($class, $config = null) {
 17         return new $class ( $config );
 18     }
 19     public static function app() {
 20         return self::$_app;
 21     }
 22     //別名路徑
 23     public static function getPathOfAlias($alias) {
 24         if (isset ( self::$_aliases [$alias] ))
 25             return self::$_aliases [$alias];
 26         elseif (($pos = strpos ( $alias, '.' )) !== false) {
 27             $rootAlias = substr ( $alias, 0, $pos );
 28             if (isset ( self::$_aliases [$rootAlias] ))
 29                 return self::$_aliases [$alias] = rtrim ( self::$_aliases [$rootAlias] . DIRECTORY_SEPARATOR . str_replace ( '.', DIRECTORY_SEPARATOR, substr ( $alias, $pos + 1 ) ), '*' . DIRECTORY_SEPARATOR );
 30         }
 31         return false;
 32     }
 33     public static function setPathOfAlias($alias, $path) {
 34         if (empty ( $path ))
 35             unset ( self::$_aliases [$alias] );
 36         else
 37             self::$_aliases [$alias] = rtrim ( $path, '\\/' );
 38     }
 39     public static function setApplication($app) {
 40         if (self::$_app === null || $app === null)
 41             self::$_app = $app;
 42     }
 43     public static function import($alias, $forceInclude = false) {
 44         if (isset ( self::$_imports [$alias] )) // previously imported
 45             return self::$_imports [$alias];
 46         
 47         if (class_exists ( $alias, false ) || interface_exists ( $alias, false ))
 48             return self::$_imports [$alias] = $alias;
 49         if (($pos = strrpos ( $alias, '.' )) === false)         // a simple class name
 50         {
 51             // try to autoload the class with an autoloader if $forceInclude is true
 52             if ($forceInclude && (Yii::autoload ( $alias, true ) || class_exists ( $alias, true )))
 53                 self::$_imports [$alias] = $alias;
 54             return $alias;
 55         }
 56         
 57         $className = ( string ) substr ( $alias, $pos + 1 );
 58         $isClass = $className !== '*';
 59         
 60         if ($isClass && (class_exists ( $className, false ) || interface_exists ( $className, false )))
 61             return self::$_imports [$alias] = $className;
 62         
 63         if (($path = self::getPathOfAlias ( $alias )) !== false) {
 64             if ($isClass) {
 65                 if ($forceInclude) {
 66                     if (is_file ( $path . '.php' ))
 67                         require ($path . '.php');
 68                     else
 69                         throw new CException ( Yii::t ( 'yii', 'Alias "{alias}" is invalid. Make sure it points to an existing PHP file and the file is readable.', array (
 70                                 '{alias}' => $alias 
 71                         ) ) );
 72                     self::$_imports [$alias] = $className;
 73                 } else
 74                     self::$classMap [$className] = $path . '.php';
 75                 return $className;
 76             } else             // a directory
 77             {
 78                 if (self::$_includePaths === null) {
 79                     self::$_includePaths = array_unique ( explode ( PATH_SEPARATOR, get_include_path () ) );
 80                     if (($pos = array_search ( '.', self::$_includePaths, true )) !== false)
 81                         unset ( self::$_includePaths [$pos] );
 82                 }
 83                 
 84                 array_unshift ( self::$_includePaths, $path );
 85                 
 86                 if (self::$enableIncludePath && set_include_path ( '.' . PATH_SEPARATOR . implode ( PATH_SEPARATOR, self::$_includePaths ) ) === false)
 87                     self::$enableIncludePath = false;
 88                 
 89                 return self::$_imports [$alias] = $path;
 90             }
 91         }
 92     }
 93     //創建組件實例
 94     public static function createComponent($config) {
 95         if (is_string ( $config )) {
 96             $type = $config;
 97             $config = array ();
 98         } elseif (isset ( $config ['class'] )) {
 99             $type = $config ['class'];
100             unset ( $config ['class'] );
101         }
102         if (! class_exists ( $type, false )) {
103             $type = Yii::import ( $type, true );
104         }
105         if (($n = func_num_args ()) > 1) {
106             $args = func_get_args ();
107             if ($n === 2)
108                 $object = new $type ( $args [1] );
109             elseif ($n === 3)
110                 $object = new $type ( $args [1], $args [2] );
111             elseif ($n === 4)
112                 $object = new $type ( $args [1], $args [2], $args [3] );
113             else {
114                 unset ( $args [0] );
115                 $class = new ReflectionClass ( $type );
116                 // Note: ReflectionClass::newInstanceArgs() is available for PHP 5.1.3+
117                 // $object=$class->newInstanceArgs($args);
118                 $object = call_user_func_array ( array (
119                         $class,
120                         'newInstance' 
121                 ), $args );
122             }
123         } else
124             $object = new $type ();
125         foreach ( $config as $key => $value )
126             $object->$key = $value;
127         
128         return $object;
129     }
130     //按需加載相應的php
131     public static function autoload($className) {
132         include self::$_coreClasses [$className];
133     }
134     private static $_coreClasses = array (
135             'CApplication' => '/base/CApplication.php',
136             'CModule' => '/base/CModule.php',
137             'CWebApplication' => '/base/CWebApplication.php',
138             'CUrlManager' => 'CUrlManager.php',
139             'CComponent' => '/base/CComponent.php'
140         ,    'CUrlRule' => 'CUrlRule.php',
141             'CController' => 'CController.php',
142             'CInlineAction' => '/actions/CInlineAction.php',
143             'CAction' => '/actions/CAction.php',
144             'CFilterChain' => '/filters/CFilterChain.php',
145             'CFilter' => '/filters/CFilter.php',
146             'CList' => '/collections/CList.php',
147             'CHttpRequest' => 'CHttpRequest.php',
148             'CDb' => 'CDb.php',
149             'CInlineFilter' => 'filters/CInlineFilter.php' 
150     );
151 }
152 
153 spl_autoload_register ( array (
154         'YiiBase',
155         'autoload' 
156 ) );

看似很多,其實就三個地方注意下就可以了

1.spl_autoload_register,用這個就可以實現傳說中的按需加載相應的php了,坑爹啊。

2.createComponent($config)這個方法是yii組件調用的核心。在配置中注冊的所有組件都是通過它獲取組件類的實例的。比如配置:

 1 <?php
 2 return array (
 3         'basePath' => dirname ( __FILE__ ) . DIRECTORY_SEPARATOR . '..',
 4         
 5         'import' => array (
 6                 'application.util.*' 
 7         ),
 8         
 9         'components' => array (
10                 'db' => array (
11                         'class' => 'CDb',
12                         'driver' => 'mysql',
13                         'hostname' => 'localhost',
14                         'username' => 'root',
15                         'password' => '',
16                         'database' => 'youtube' 
17                 ),
18                 'urlManager' => array (
19                         'urlFormat' => 'path',
20                         'rules' => array (
21                                 'comment_reply/<a:\d+>/<ci:\d+>' => 'reply/load_comment_reply',
22                                 'b/<id:\d+>' => array (
23                                         'video/broadcast',
24                                         'urlSuffix' => '.html' 
25                                 ),
26                                 'c/<list_start:\d+>' => 'video/list_more_video',
27                                 'u/reg' => 'user/reg',
28                                 'v/upload' => 'video/upload_video',
29                                 'login' => 'user/to_login',
30                                 'show_chanel/<chanel_id:\d+>' => 'show/chanel' ,
31                                 'show/<show_id:\d+>' => 'show/show',
32                         ) 
33                 ) 
34         ) 
35 );
36 ?> 

這個文件就返回了個map,裡面components中的db,urlManager便是我注冊的系統中的組件,裡面的array便是組件的參數.

從源碼中看到$type = $config ['class'];$object = new $type;就創建了注冊的類實例了。

3.import($alias, $forceInclude = false)。作用:導入一個類或一個目錄。導入一個類就像包含相應的類文件。 主要區別是導入一個類比較輕巧, 它僅在類文件首次引用時包含。這個也是yii的一個核心優化點。

這個在上面createComponent($config)中有用到,

if (!class_exists ( $type, false )) {
  $type = Yii::import ( $type, true );
}

如果$type類沒有定義,就去導入。有些組件,核心在yii中多次create調用,這樣就保證了僅在類文件首次引用時導入。

下面分析index.php中Yii::createWebApplication ( $config )的調用過程。

這個調用是去了CWebApplication。

 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 }

沒有構造方法,構造方法在父類CApplication裡面。

 

 1 <?php
 2 abstract class CApplication extends CModule {
 3     private $_id;
 4     private $_basePath;
 5     abstract public function processRequest();
 6     public function __construct($config = null) {
 7         if (is_string ( $config ))
 8             $config = require ($config);
 9         Yii::setApplication ( $this );//保存整個app實例
10         if (isset ( $config ['basePath'] )) {
11             $this->setBasePath ( $config ['basePath'] );
12             unset ( $config ['basePath'] );
13         } else
14             $this->setBasePath ( 'protected' );
15         //設置別名,後面就可以用application表示basePath了
16         Yii::setPathOfAlias ( 'application', $this->getBasePath () );
17         //鉤子,模塊 預 初始化時執行,子類實現。不過這時,配置還沒有寫入框架
18         $this->preinit ();
19         $this->registerCoreComponents ();
20         //父類實現
21         $this->configure ( $config );
22         //加載靜態應用組件
23         $this->preloadComponents ();
24         //這才開始初始化模塊
25         $this->init ();
26     }
27     protected function registerCoreComponents() {
28         $components = array (
29                 'request' => array (
30                         'class' => 'CHttpRequest'
31                 ),
32                 'urlManager' => array (
33                         'class' => 'CUrlManager'
34                 )
35         );
36 
37         $this->setComponents ( $components );//父類實現
38     }
39     public function run() {
40         $this->processRequest ();
41     }
42     public function getId() {
43         if ($this->_id !== null)
44             return $this->_id;
45         else
46             return $this->_id = sprintf ( '%x', crc32 ( $this->getBasePath () . $this->name ) );
47     }
48     public function setId($id) {
49         $this->_id = $id;
50     }
51     public function getBasePath() {
52         return $this->_basePath;
53     }
54     public function setBasePath($path) {
55         if (($this->_basePath = realpath ( $path )) === false || ! is_dir ( $this->_basePath ))
56             return;
57     }
58     public function getDb() {
59         return $this->getComponent ( 'db' );//父類實現
60     }
61     public function getUrlManager() {
62         return $this->getComponent ( 'urlManager' );
63     }
64     public function getController() {
65         return null;
66     }
67     public function getBaseUrl($absolute = false) {
68         return $this->getRequest ()->getBaseUrl ( $absolute );
69     }
70 }

__construct裡面注釋寫的很詳細了,值得注意的是registerCoreComponents ()。前面說了那麼多,那麼Yii::createWebApplication ( $config )到底是做什麼的。

其實最終目的對這個裁剪過的yii而言就是注冊核心組件。就這麼簡單.

setComponents ( $components )在父類CModule裡面.

 1 <?php
 2 abstract class CModule extends CComponent {
 3     public $preload = array ();
 4     public $behaviors = array ();
 5     private $_id;
 6     private $_parentModule;
 7     private $_basePath;
 8     private $_modulePath;
 9     private $_params;
10     private $_modules = array ();
11     private $_moduleConfig = array ();
12     private $_components = array ();
13     private $_componentConfig = array ();
14     //重寫是為了方便直接用 application.組件 這種方式直接獲取組件
15     public function __get($name) {
16         if ($this->hasComponent ( $name ))
17             return $this->getComponent ( $name );
18         else
19             return parent::__get ( $name );
20     }
21     public function __isset($name) {
22         if ($this->hasComponent ( $name ))
23             return $this->getComponent ( $name ) !== null;
24         else
25             return parent::__isset ( $name );
26     }
27     public function hasComponent($id) {
28         return isset ( $this->_components [$id] ) || isset ( $this->_componentConfig [$id] );
29     }
30     //
31     public function getComponent($id, $createIfNull = true) {
32         if (isset ( $this->_components [$id] ))
33             return $this->_components [$id];
34         else if (isset ( $this->_componentConfig [$id] ) && $createIfNull) {
35             $config = $this->_componentConfig [$id];
36             $component = Yii::createComponent ( $config );//YiiBase,返回組件實例
37             $component->init ();//鉤子,調用子類重寫的init方法
38             //將組件寫入數組保存,並返回
39             return $this->_components [$id] = $component;
40         }
41     }
42     public function setComponent($id, $component, $merge = true) {
43         //組件寫入數組保存
44         if (isset ( $this->_componentConfig [$id] ) && $merge) {
45             
46             $this->_componentConfig [$id] = self::mergeArray ( $this->_componentConfig [$id], $component );
47         } else {
48             
49             $this->_componentConfig [$id] = $component;
50         }
51     }
52     public static function mergeArray($a, $b) {
53         $args = func_get_args ();
54         $res = array_shift ( $args );
55         while ( ! empty ( $args ) ) {
56             $next = array_shift ( $args );
57             foreach ( $next as $k => $v ) {
58                 if (is_integer ( $k ))
59                     isset ( $res [$k] ) ? $res [] = $v : $res [$k] = $v;
60                 elseif (is_array ( $v ) && isset ( $res [$k] ) && is_array ( $res [$k] ))
61                     $res [$k] = self::mergeArray ( $res [$k], $v );
62                 else
63                     $res [$k] = $v;
64             }
65         }
66         return $res;
67     }
68     public function setComponents($components, $merge = true) {
69         foreach ( $components as $id => $component )
70             $this->setComponent ( $id, $component, $merge );
71     }
72     //子類CApplication調用,用來為模塊指定配置
73     public function configure($config) {
74         if (is_array ( $config )) {
75             foreach ( $config as $key => $value )
76                 $this->$key = $value;
77         }
78     }
79     protected function preloadComponents() {
80         foreach ( $this->preload as $id )
81             $this->getComponent ( $id );
82     }
83     //又是兩個鉤子
84     protected function preinit() {
85     }
86     protected function init() {
87     }
88 }

看到所謂的注冊組件就是寫入數組保存,getComponent()的時候就是用前面講到的YiiBase裡面的createComponent($config)返回組件實例。就這麼簡單。

而CApplication裡面的什麼getDb(),getUrlManager()也是在調用getComponent()。

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