CI加載流程小結,ci加載小結
無聊,決定水一把。
CI(CodeIgniter)是我最早接觸的一個框架,到現在也只是用了其中一點零碎的方法。一直想對其流程做個小結,卻總是因各種各樣的“理由”挨著。看見別人圖表齊上陣,沒那耐心,就從代碼說起吧,權當做個筆記,紀念一下。
看在線的用戶手冊,也知道,將CI下載下來(最新版本2.2.1),解壓到機子上,比如www目錄,可改個根目錄名(原名CodeIgniter-2.2-stable太長),初步目錄文件如下,當然這在是windows下面。

訪問下,如localhost/ci/index.php,就進入CI默認的Welcome頁面

如何一步步加載這個頁面的?首先訪問的是index.php腳本

![]()
1 <?php
2
3 /*
4 *---------------------------------------------------------------
5 * APPLICATION ENVIRONMENT
6 *---------------------------------------------------------------
7 *
8 * You can load different configurations depending on your
9 * current environment. Setting the environment also influences
10 * things like logging and error reporting.
11 *
12 * This can be set to anything, but default usage is:
13 *
14 * development
15 * testing
16 * production
17 *
18 * NOTE: If you change these, also change the error_reporting() code below
19 *
20 */
21 define('ENVIRONMENT', 'development');
22 /*
23 *---------------------------------------------------------------
24 * ERROR REPORTING
25 *---------------------------------------------------------------
26 *
27 * Different environments will require different levels of error reporting.
28 * By default development will show errors but testing and live will hide them.
29 */
30
31 if (defined('ENVIRONMENT'))
32 {
33 switch (ENVIRONMENT)
34 {
35 case 'development':
36 error_reporting(E_ALL);
37 break;
38
39 case 'testing':
40 case 'production':
41 error_reporting(0);
42 break;
43
44 default:
45 exit('The application environment is not set correctly.');
46 }
47 }
48
49 /*
50 *---------------------------------------------------------------
51 * SYSTEM FOLDER NAME
52 *---------------------------------------------------------------
53 *
54 * This variable must contain the name of your "system" folder.
55 * Include the path if the folder is not in the same directory
56 * as this file.
57 *
58 */
59 $system_path = 'system';
60
61 /*
62 *---------------------------------------------------------------
63 * APPLICATION FOLDER NAME
64 *---------------------------------------------------------------
65 *
66 * If you want this front controller to use a different "application"
67 * folder then the default one you can set its name here. The folder
68 * can also be renamed or relocated anywhere on your server. If
69 * you do, use a full server path. For more info please see the user guide:
70 * http://codeigniter.com/user_guide/general/managing_apps.html
71 *
72 * NO TRAILING SLASH!
73 *
74 */
75 $application_folder = 'application';
76
77 /*
78 * --------------------------------------------------------------------
79 * DEFAULT CONTROLLER
80 * --------------------------------------------------------------------
81 *
82 * Normally you will set your default controller in the routes.php file.
83 * You can, however, force a custom routing by hard-coding a
84 * specific controller class/function here. For most applications, you
85 * WILL NOT set your routing here, but it's an option for those
86 * special instances where you might want to override the standard
87 * routing in a specific front controller that shares a common CI installation.
88 *
89 * IMPORTANT: If you set the routing here, NO OTHER controller will be
90 * callable. In essence, this preference limits your application to ONE
91 * specific controller. Leave the function name blank if you need
92 * to call functions dynamically via the URI.
93 *
94 * Un-comment the $routing array below to use this feature
95 *
96 */
97 // The directory name, relative to the "controllers" folder. Leave blank
98 // if your controller is not in a sub-folder within the "controllers" folder
99 // $routing['directory'] = '';
100
101 // The controller class file name. Example: Mycontroller
102 // $routing['controller'] = '';
103
104 // The controller function you wish to be called.
105 // $routing['function'] = '';
106
107
108 /*
109 * -------------------------------------------------------------------
110 * CUSTOM CONFIG VALUES
111 * -------------------------------------------------------------------
112 *
113 * The $assign_to_config array below will be passed dynamically to the
114 * config class when initialized. This allows you to set custom config
115 * items or override any default config values found in the config.php file.
116 * This can be handy as it permits you to share one application between
117 * multiple front controller files, with each file containing different
118 * config values.
119 *
120 * Un-comment the $assign_to_config array below to use this feature
121 *
122 */
123 // $assign_to_config['name_of_config_item'] = 'value of config item';
124
125
126
127 // --------------------------------------------------------------------
128 // END OF USER CONFIGURABLE SETTINGS. DO NOT EDIT BELOW THIS LINE
129 // --------------------------------------------------------------------
130
131 /*
132 * ---------------------------------------------------------------
133 * Resolve the system path for increased reliability
134 * ---------------------------------------------------------------
135 */
136
137 // Set the current directory correctly for CLI requests
138 if (defined('STDIN'))
139 {
140 chdir(dirname(__FILE__));
141 }
142
143 if (realpath($system_path) !== FALSE)
144 {
145 $system_path = realpath($system_path).'/';
146 }
147
148 // ensure there's a trailing slash
149 $system_path = rtrim($system_path, '/').'/';
150
151 // Is the system path correct?
152 if ( ! is_dir($system_path))
153 {
154 exit("Your system folder path does not appear to be set correctly. Please open the following file and correct this: ".pathinfo(__FILE__, PATHINFO_BASENAME));
155 }
156
157 /*
158 * -------------------------------------------------------------------
159 * Now that we know the path, set the main path constants
160 * -------------------------------------------------------------------
161 */
162 // The name of THIS file
163 define('SELF', pathinfo(__FILE__, PATHINFO_BASENAME));
164
165 // The PHP file extension
166 // this global constant is deprecated.
167 define('EXT', '.php');
168
169 // Path to the system folder
170 define('BASEPATH', str_replace("\\", "/", $system_path));
171
172 // Path to the front controller (this file)
173 define('FCPATH', str_replace(SELF, '', __FILE__));
174
175 // Name of the "system folder"
176 define('SYSDIR', trim(strrchr(trim(BASEPATH, '/'), '/'), '/'));
177
178
179 // The path to the "application" folder
180 if (is_dir($application_folder))
181 {
182 define('APPPATH', $application_folder.'/');
183 }
184 else
185 {
186 if ( ! is_dir(BASEPATH.$application_folder.'/'))
187 {
188 exit("Your application folder path does not appear to be set correctly. Please open the following file and correct this: ".SELF);
189 }
190
191 define('APPPATH', BASEPATH.$application_folder.'/');
192 }
193
194 /*
195 * --------------------------------------------------------------------
196 * LOAD THE BOOTSTRAP FILE
197 * --------------------------------------------------------------------
198 *
199 * And away we go...
200 *
201 */
202 require_once BASEPATH.'core/CodeIgniter.php';
203
204 /* End of file index.php */
205 /* Location: ./index.php */
View Code
21行:首先定義一個ENVIRONMENT常量為development,即開發環境。
31-47行:switch語句,由於當前環境是development,所以是設置報告所有級別的錯誤。
49-59行:$system_path變量定義CI的默認的系統腳本目錄是 system,61-75行定義當前默認的供我們主要開發用的目錄為 application。
77-105行:全部注釋掉了,這裡是我們可以強制設置系統加載時默認的目錄名($routing['directory'])、控制器名($routing['directory'])和方法名($routing['directory']),雖然一般這些是設置在application\config\routes.php中(下圖),訪問的Welcome頁面也是通過這個默認控制器Welcome類進行的,這裡只是作為一個選擇性的方式,其實沒必要弄

108-129行:全部注釋掉,用於自定義配置變量(CUSTOM CONFIG VALUES),前一篇說過,任何後端project中,總有些配置信息,只是各個項目或框架加載方式不同,這個$assign_to_config數組就存放我們的自定義配置信息,如$assign_to_config['home'] = 'localhost'; ,之所以注釋掉,又是因為這只是一個可選的操作,CI的用戶自定義配置信息,一般放在application\config目錄下邊,以自動加載信息(autoload.php),普通配置信息(config.php)、常量(constants.php)、數據庫(database.php)等分開文件存儲,所以一般不會在這裡的去配置一個要用到的變量,$assign_to_config默認是沒有定義的。

從131行到index.php文件末尾主要是對一些路徑變量的定義。
137-141行:是為CLI(Command-Interface Line)的調用方式准備的,是直接在Mac/Linux系統上通過終端命令運行腳本,這個在CI中文官網(http://codeigniter.org.cn/user_guide/general/cli.html)也有介紹,如果定義了名為STDIN的常量,則將執行目錄改為當前文件所在目錄,當然前面沒有出現過STDIN這個常量的定義,這裡就不會執行了。

143-155行:確定框架存放系統腳本的目錄變量$system_path,也就是前面圖中的system目錄,這裡會檢測它的有效性,無效的話程序就掛在這裡了。
157-192行:定義若干主要目錄常量,分別是SELF:當前腳本的文件名、EXT:腳本擴展名、BASEPATH:system目錄的路徑、FCPATH:當前腳本所在的目錄、SYSDIR:system目錄的目錄名,不改動的話就是system。
179-194行:定義APPPATH常量,確定application所在的目錄,就是以後我們主要開發的地方,使用is_dir檢測,稍微注意的是is_dir可以檢測相對目錄,所以實際運行的是if裡邊的代碼,APPPATH得到的是相對路徑。
最後打印看看這些變(常)量的值都是啥,有的與存放目錄相關:

202行:加載BASEPATH.'core/CodeIgniter.php'腳本,就是system目錄下的核心類文件目錄下的文件,進入到CI的核心類目錄下的文件了。
=====================================================================================================

![]()
1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2 /**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.1.6 or newer
6 *
7 * @package CodeIgniter
8 * @author EllisLab Dev Team
9 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
10 * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
11 * @license http://codeigniter.com/user_guide/license.html
12 * @link http://codeigniter.com
13 * @since Version 1.0
14 * @filesource
15 */
16
17 // ------------------------------------------------------------------------
18
19 /**
20 * System Initialization File
21 *
22 * Loads the base classes and executes the request.
23 *
24 * @package CodeIgniter
25 * @subpackage codeigniter
26 * @category Front-controller
27 * @author EllisLab Dev Team
28 * @link http://codeigniter.com/user_guide/
29 */
30
31 /**
32 * CodeIgniter Version
33 *
34 * @var string
35 *
36 */
37 define('CI_VERSION', '2.2.1');
38
39 /**
40 * CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
41 *
42 * @var boolean
43 *
44 */
45 define('CI_CORE', FALSE);
46
47 /*
48 * ------------------------------------------------------
49 * Load the global functions
50 * ------------------------------------------------------
51 */
52 require(BASEPATH.'core/Common.php');
53
54 /*
55 * ------------------------------------------------------
56 * Load the framework constants
57 * ------------------------------------------------------
58 */
59 if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))
60 {
61 require(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
62 }
63 else
64 {
65 require(APPPATH.'config/constants.php');
66 }
67
68 /*
69 * ------------------------------------------------------
70 * Define a custom error handler so we can log PHP errors
71 * ------------------------------------------------------
72 */
73 set_error_handler('_exception_handler');
74
75 if ( ! is_php('5.3'))
76 {
77 @set_magic_quotes_runtime(0); // Kill magic quotes
78 }
79
80 /*
81 * ------------------------------------------------------
82 * Set the subclass_prefix
83 * ------------------------------------------------------
84 *
85 * Normally the "subclass_prefix" is set in the config file.
86 * The subclass prefix allows CI to know if a core class is
87 * being extended via a library in the local application
88 * "libraries" folder. Since CI allows config items to be
89 * overriden via data set in the main index. php file,
90 * before proceeding we need to know if a subclass_prefix
91 * override exists. If so, we will set this value now,
92 * before any classes are loaded
93 * Note: Since the config file data is cached it doesn't
94 * hurt to load it here.
95 */
96 if (isset($assign_to_config['subclass_prefix']) AND $assign_to_config['subclass_prefix'] != '')
97 {
98 get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
99 }
100
101 /*
102 * ------------------------------------------------------
103 * Set a liberal script execution time limit
104 * ------------------------------------------------------
105 */
106 if (function_exists("set_time_limit") == TRUE AND @ini_get("safe_mode") == 0)
107 {
108 @set_time_limit(300);
109 }
110
111 /*
112 * ------------------------------------------------------
113 * Start the timer... tick tock tick tock...
114 * ------------------------------------------------------
115 */
116 $BM =& load_class('Benchmark', 'core');
117 $BM->mark('total_execution_time_start');
118 $BM->mark('loading_time:_base_classes_start');
119
120 /*
121 * ------------------------------------------------------
122 * Instantiate the hooks class
123 * ------------------------------------------------------
124 */
125 $EXT =& load_class('Hooks', 'core');
126
127 /*
128 * ------------------------------------------------------
129 * Is there a "pre_system" hook?
130 * ------------------------------------------------------
131 */
132 $EXT->_call_hook('pre_system');
133
134 /*
135 * ------------------------------------------------------
136 * Instantiate the config class
137 * ------------------------------------------------------
138 */
139 $CFG =& load_class('Config', 'core');
140
141 // Do we have any manually set config items in the index.php file?
142 if (isset($assign_to_config))
143 {
144 $CFG->_assign_to_config($assign_to_config);
145 }
146
147 /*
148 * ------------------------------------------------------
149 * Instantiate the UTF-8 class
150 * ------------------------------------------------------
151 *
152 * Note: Order here is rather important as the UTF-8
153 * class needs to be used very early on, but it cannot
154 * properly determine if UTf-8 can be supported until
155 * after the Config class is instantiated.
156 *
157 */
158
159 $UNI =& load_class('Utf8', 'core');
160
161 /*
162 * ------------------------------------------------------
163 * Instantiate the URI class
164 * ------------------------------------------------------
165 */
166 $URI =& load_class('URI', 'core');
167
168 /*
169 * ------------------------------------------------------
170 * Instantiate the routing class and set the routing
171 * ------------------------------------------------------
172 */
173 $RTR =& load_class('Router', 'core');
174 $RTR->_set_routing();
175
176 // Set any routing overrides that may exist in the main index file
177 if (isset($routing))
178 {
179 $RTR->_set_overrides($routing);
180 }
181
182 /*
183 * ------------------------------------------------------
184 * Instantiate the output class
185 * ------------------------------------------------------
186 */
187 $OUT =& load_class('Output', 'core');
188
189 /*
190 * ------------------------------------------------------
191 * Is there a valid cache file? If so, we're done...
192 * ------------------------------------------------------
193 */
194 if ($EXT->_call_hook('cache_override') === FALSE)
195 {
196 if ($OUT->_display_cache($CFG, $URI) == TRUE)
197 {
198 exit;
199 }
200 }
201
202 /*
203 * -----------------------------------------------------
204 * Load the security class for xss and csrf support
205 * -----------------------------------------------------
206 */
207 $SEC =& load_class('Security', 'core');
208
209 /*
210 * ------------------------------------------------------
211 * Load the Input class and sanitize globals
212 * ------------------------------------------------------
213 */
214 $IN =& load_class('Input', 'core');
215
216 /*
217 * ------------------------------------------------------
218 * Load the Language class
219 * ------------------------------------------------------
220 */
221 $LANG =& load_class('Lang', 'core');
222
223 /*
224 * ------------------------------------------------------
225 * Load the app controller and local controller
226 * ------------------------------------------------------
227 *
228 */
229 // Load the base controller class
230 require BASEPATH.'core/Controller.php';
231
232 function &get_instance()
233 {
234 return CI_Controller::get_instance();
235 }
236
237
238 if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
239 {
240 require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
241 }
242
243 // Load the local application controller
244 // Note: The Router class automatically validates the controller path using the router->_validate_request().
245 // If this include fails it means that the default controller in the Routes.php file is not resolving to something valid.
246 if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'))
247 {
248 show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.');
249 }
250
251 include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');
252
253 // Set a mark point for benchmarking
254 $BM->mark('loading_time:_base_classes_end');
255
256 /*
257 * ------------------------------------------------------
258 * Security check
259 * ------------------------------------------------------
260 *
261 * None of the functions in the app controller or the
262 * loader class can be called via the URI, nor can
263 * controller functions that begin with an underscore
264 */
265 $class = $RTR->fetch_class();
266 $method = $RTR->fetch_method();
267
268 if ( ! class_exists($class)
269 OR strncmp($method, '_', 1) == 0
270 OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller')))
271 )
272 {
273 if ( ! empty($RTR->routes['404_override']))
274 {
275 $x = explode('/', $RTR->routes['404_override']);
276 $class = $x[0];
277 $method = (isset($x[1]) ? $x[1] : 'index');
278 if ( ! class_exists($class))
279 {
280 if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
281 {
282 show_404("{$class}/{$method}");
283 }
284
285 include_once(APPPATH.'controllers/'.$class.'.php');
286 }
287 }
288 else
289 {
290 show_404("{$class}/{$method}");
291 }
292 }
293
294 /*
295 * ------------------------------------------------------
296 * Is there a "pre_controller" hook?
297 * ------------------------------------------------------
298 */
299 $EXT->_call_hook('pre_controller');
300
301 /*
302 * ------------------------------------------------------
303 * Instantiate the requested controller
304 * ------------------------------------------------------
305 */
306 // Mark a start point so we can benchmark the controller
307 $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');
308
309 $CI = new $class();
310
311 /*
312 * ------------------------------------------------------
313 * Is there a "post_controller_constructor" hook?
314 * ------------------------------------------------------
315 */
316 $EXT->_call_hook('post_controller_constructor');
317
318 /*
319 * ------------------------------------------------------
320 * Call the requested method
321 * ------------------------------------------------------
322 */
323 // Is there a "remap" function? If so, we call it instead
324 if (method_exists($CI, '_remap'))
325 {
326 $CI->_remap($method, array_slice($URI->rsegments, 2));
327 }
328 else
329 {
330 // is_callable() returns TRUE on some versions of PHP 5 for private and protected
331 // methods, so we'll use this workaround for consistent behavior
332 if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI))))
333 {
334 // Check and see if we are using a 404 override and use it.
335 if ( ! empty($RTR->routes['404_override']))
336 {
337 $x = explode('/', $RTR->routes['404_override']);
338 $class = $x[0];
339 $method = (isset($x[1]) ? $x[1] : 'index');
340 if ( ! class_exists($class))
341 {
342 if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
343 {
344 show_404("{$class}/{$method}");
345 }
346
347 include_once(APPPATH.'controllers/'.$class.'.php');
348 unset($CI);
349 $CI = new $class();
350 }
351 }
352 else
353 {
354 show_404("{$class}/{$method}");
355 }
356 }
357
358 // Call the requested method.
359 // Any URI segments present (besides the class/function) will be passed to the method for convenience
360 call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));
361 }
362
363
364 // Mark a benchmark end point
365 $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
366
367 /*
368 * ------------------------------------------------------
369 * Is there a "post_controller" hook?
370 * ------------------------------------------------------
371 */
372 $EXT->_call_hook('post_controller');
373
374 /*
375 * ------------------------------------------------------
376 * Send the final rendered output to the browser
377 * ------------------------------------------------------
378 */
379 if ($EXT->_call_hook('display_override') === FALSE)
380 {
381 $OUT->_display();
382 }
383
384 /*
385 * ------------------------------------------------------
386 * Is there a "post_system" hook?
387 * ------------------------------------------------------
388 */
389 $EXT->_call_hook('post_system');
390
391 /*
392 * ------------------------------------------------------
393 * Close the DB connection if one exists
394 * ------------------------------------------------------
395 */
396 if (class_exists('CI_DB') AND isset($CI->db))
397 {
398 $CI->db->close();
399 }
400
401
402 /* End of file CodeIgniter.php */
403 /* Location: ./system/core/CodeIgniter.php */
View Code
在CodeIgniter中,可以看到開頭的英文描述,該腳本時系統初始化文件,主要作用是裝載基類和執行請求。
31-45行:定義了CI_VERSION常量,描述當前框架版本,CI_CORE常量,目前我也不清楚沒探究過,注釋是CI的分支,啥意思?
52行:加載系統核心目錄下的Common.php文件,Load the global functions,記得前一篇中說到,一般一個項目會將很多公共方法放在一個腳本中加載進來,通常取名Utilities.php,也可是Common.php,這裡的Common.php也是這個意思,如它的解釋是“加載全局函數”,即這裡的函數都是後邊直接拿來用的。在這個腳本中有兩個重要的方法(目前來說)一個是get_config,單獨拿出來如下

![]()
1 <?php
2 /**
3 * Loads the main config.php file
4 *
5 * This function lets us grab the config file even if the Config class
6 * hasn't been instantiated yet
7 *
8 * @access private
9 * @return array
10 */
11 if ( ! function_exists('get_config'))
12 {
13 function &get_config($replace = array())
14 {
15 static $_config;
16
17 if (isset($_config))
18 {
19 return $_config[0];
20 }
21
22 // Is the config file in the environment folder?
23 if ( ! defined('ENVIRONMENT') OR ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
24 {
25 $file_path = APPPATH.'config/config.php';
26 }
27
28 // Fetch the config file
29 if ( ! file_exists($file_path))
30 {
31 exit('The configuration file does not exist.');
32 }
33
34 require($file_path);
35
36 // Does the $config array exist in the file?
37 if ( ! isset($config) OR ! is_array($config))
38 {
39 exit('Your config file does not appear to be formatted correctly.');
40 }
41
42 // Are any values being dynamically replaced?
43 if (count($replace) > 0)
44 {
45 foreach ($replace as $key => $val)
46 {
47 if (isset($config[$key]))
48 {
49 $config[$key] = $val;
50 }
51 }
52 }
53
54 $_config[0] =& $config;
55 return $_config[0];
56 }
57 }
View Code
注釋說它加載主要的config.php文件,它使得我們能抓取到配置文件,即便配置類還未被實例化。在CI中,有專門的核心配置類CI_Config來加載配置信息,而這裡的get_config方法也能獲得主要配置信息,注意是主要配置信息,在application/config目錄下有很多其他的配置信息文件(前面在自定義配置變量時也說過CI將配置信息分為了很多文件),其中有一個config.php文件就是get_config能獲取到的,這個文件存放的就是基本信息,如果你還想獲取其他的配置信息,貌似就要用配置類了。所以如果想添加節本配置信息就在這個裡邊。
如果是第一次調用get_config方法,先聲明靜態變量$_config,如果已定義則直接返回它的索引為0的子數組。然後查看APPPATH/config/ENVIRONMENT/config.php文件是否存在(前面打印已知ENVIRONMENT常量值,未改動就是development,原始的框架中沒有這個目錄,所以這裡加載的是application/config/config.php(只加載了這一個,其他的配置文件沒有),可以打開看看config.php中定義了一個$config數組,一些基本定義如基礎鏈接、鏈接後綴、編碼、語言、緩存、日志、鉤子等等。如果傳入一個關聯數組,它會將鍵-值(臨時)加入$_config中。總之,get_config方法主要得到的是config.php中定義的數組變量。
與get_config相關的config_item方法則是得到這個數組變量中的某一項。
另一個比較重要的方法是load_class:

![]()
1 <?php
2 /**
3 * Class registry
4 *
5 * This function acts as a singleton. If the requested class does not
6 * exist it is instantiated and set to a static variable. If it has
7 * previously been instantiated the variable is returned.
8 *
9 * @access public
10 * @param string the class name being requested
11 * @param string the directory where the class should be found
12 * @param string the class name prefix
13 * @return object
14 */
15 if ( ! function_exists('load_class'))
16 {
17 function &load_class($class, $directory = 'libraries', $prefix = 'CI_')
18 {
19 static $_classes = array();
20
21 // Does the class exist? If so, we're done...
22 if (isset($_classes[$class]))
23 {
24 return $_classes[$class];
25 }
26
27 $name = FALSE;
28
29 // Look for the class first in the local application/libraries folder
30 // then in the native system/libraries folder
31 foreach (array(APPPATH, BASEPATH) as $path)
32 {
33 if (file_exists($path.$directory.'/'.$class.'.php'))
34 {
35 $name = $prefix.$class;
36
37 if (class_exists($name) === FALSE)
38 {
39 require($path.$directory.'/'.$class.'.php');
40 }
41
42 break;
43 }
44 }
45
46 // Is the request a class extension? If so we load it too
47 if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'))
48 {
49 $name = config_item('subclass_prefix').$class;
50
51 if (class_exists($name) === FALSE)
52 {
53 require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php');
54 }
55 }
56
57 // Did we find the class?
58 if ($name === FALSE)
59 {
60 // Note: We use exit() rather then show_error() in order to avoid a
61 // self-referencing loop with the Excptions class
62 exit('Unable to locate the specified class: '.$class.'.php');
63 }
64
65 // Keep track of what we just loaded
66 is_loaded($class);
67
68 $_classes[$class] = new $name();
69 return $_classes[$class];
70 }
71 }
View Code
先看它的注釋:這個方法作為一個單例,如果被請求的類沒有出現過,則該類會被實例化為一個static variable,如果先前被實例化過則直接返回它。它的三個參數分別是請求的類名、所在目錄,類名前綴。可以看到,目錄默認是libraries,在application和system中均有它,它就是存放我們自定義的類庫或者CI自帶的類庫的地方,就是自定義工具和CI提供的工具,如日歷類、加密類、Ftp類、日志類、Session會話類、Email郵件收發類、JavaScript類、ZIP壓縮類等等。或許你已經注意到這裡返回的是引用而非值,就像它將加載的類作為靜態變量一樣,這些細節地方最終提高了整個系統的訪問速度。
大致流程:先定義一個靜態數組,若數組中已有該類直接返回。先後掃描APPPATH和BASEPATH(前面已知這倆常量值)文件夾下的$directory(默認值是libraries)目錄下的$class.php文件是否存在,存在則加上CI的標准類前綴CI_(第三個參數的默認值),在檢查類存在與否,存在則require該文件(從這裡可知,class_exists()在判斷類是否存在時並不需要先加載該類文件),一旦文件出現則加載它,並break跳出。注意掃描順序,先APPPATH後BASEPATH,假如只傳第一個參數類名,則優先在我們自己開發的application目錄libraries中尋找,然後才去system目錄的libraries下邊。
由於我們可以對CI的核心類進行擴展(繼承它們),所以在掃描完APPPATH和BASEPATH的核心類(名稱以CI_為前綴)目錄後,還要掃描APPPATH的libraries下邊是否有自定義的擴展類(默認以MY_為前綴),有的話也要加載它們,然後實例化一個對應對象(有擴展類是擴展類)存入$_classes靜態數組並返回該對象。
對Common.php有大致了解後回到CodeIgniter.php腳本。
54-66行:加載APPPATH.'config/constants.php'腳本,constants.php如同名字一樣放的是framework constants,集中定義了一些常量,所以我們在添加常量時就可以放到這裡邊來定義。

68-78行:首先定義了一個自定義錯誤處理方法_exception_handler。判斷php版本,非5.3關閉magic_quotes引用,這個配置在5.3版本已棄用,提高安全性。
80-99行:這裡就是將前面說過的$assign_to_config自定義配置信息數組臨時加到$_config數組中,通過get_config方法實現,前面說過$assign_to_config默認是沒有定義的,這裡的if語句也不會運行。
101-109行:設置自定義腳本最大執行時間為300秒(略長,跑日志的話得更長)
111-118行:加載核心類Benchmark,設置兩個標記點。Benchmark基准測試類,就是測試某個開始標記到結束標記之間占用的內存大小、執行時間等信息,測試嘛,當然它要結合CI中一個叫分析器的東西使用。
120-132行:加載核心類Hooks,鉤子,設置了一個系統開始執行的鉤子(實際未執行,因為application/config/config.php關於它的配置信息默認設置為false,即不啟用鉤子)。它就就相當於一個觸發器,在某個東西要執行前開始執行某些代碼,比如控制器加載前、加載後等,一旦控制器加載就運行指定的代碼。在這裡,它嘗試調用一個pre_system(系統執行前)的擴展,默認不執行。
134-145行:加載核心類Config,配置類,它用來加載其他需需要的配置信息,並且它再次加載$assign_to_config數組中配置信息如果該數組定義了的話。
147-159行:加載核心類Utf8,編碼類。
161-166行:加載核心類URI,路由。
168-180行:加載核心類Router,路徑處理類,_set_routing方法設置好訪問路徑。如果路徑配置數組$routing(前面提到默認是注釋掉的)定義了的話,將覆蓋默認的路由配置。如果你輸入了不存在的腳本路徑,在這一步就停住,開始報404了,當然還得Router裡邊的方法處理。
Router類裡面,URI作為它的一個成員存在,實際處理方法在URI類中,熟悉點的都知道CI的訪問方式默認是段(segment)的形式,據說更有利於搜索引擎。一個簡單的訪問方式是這樣的localhost/ci/index.php/Controller/Function/Arguments,它們將訪問的形式解析為需要的控制器,調用的方法,以及提供的參數列表,當然也可啟用傳統的查詢字符串形式。具體方法略復雜。
187行:加載核心類Output。
189-200行:通過Hooks類和Output類檢測有無緩存,有的話直接輸出緩存頁面,跳出腳本了。這也是在CI的介紹中應用程序流程圖部分,當路徑處理完後,若有緩存直接輸出的原因。

207行:加載核心類Security。
214行:加載核心類Input。
221行:加載核心類Lang,語言處理。
229-235行:加載核心類Controller,它是所有控制器的基類,而get_instance全局方法也能得到它的實例,Controller的牛逼之處在於,它將前面所有通過load_calss載入的libraries(默認)目錄(APPPATH和BASEPATH)中的工具庫全部實例化為對象,並作為它的屬性成員。所以這裡的get_instance方法得到的實例也被CI稱為超級對象(super object),因為通過這個對象就可以獲取所有通過前面加載的對象實例。
238-242行:加載自定義的,對上一步的核心類CI_Controller的擴展類的文件,默認就是MY_Controller,當然前提是如果你擴展了的的話。
243-251行:通過核心類Router的實例,提取當前訪問的控制器所在的目錄和類名,不存在則報錯,存在則加載它,這裡就加載了默認的welcome控制器文件。當然如果你自己定義了控制器類文件並訪問,也是在這裡被include進來的(通過Router類提取子目錄$RTR->fetch_directory(),若存在,提取類名$RTR->fetch_class()來找),大概在246行的if語句塊,就是檢查這個類文件是否存在。
252行:設置一個基准測試結束標記,標記加載基本核心類結束(這些測試默認不會執行)。
256-292行:安全檢查。先通過Router類取得類名和要執行的方法名,if條件檢查3項內容。1. 上面的243-251行是找到了控制器對應的腳本,並且加載了它,但是假如這只是一個名字匹配的空腳本呢?裡邊什麼都沒寫就不行了,於是要檢查類的定義是否存在(class_exists),2. 以下劃線_開頭的方法名不能執行,直接報錯,當然這是CI自己的的規則,也就是說無論你的類定義的以_開頭的方法即使是公有訪問屬性也不行(除了一個_remap),3. 當類中的方法根控制器核心類中的方法同名時也不行。定義方法名時有個印象就行了。進入if中就很可能會404了。
298行:Hooks類嘗試調用一個pre_controller(控制器執行前)的擴展,默認沒有。
301-309行:基准測試類設置一個起點標記,目的在於測試控制器執行的時長(默認不顯示測試信息),並且實例化前面加載的控制器類,默認的就是Welcome。
315行:Hooks嘗試執行post_controller_constructor(所調用的控制器類構造完成後)的擴展,默認沒有。
317-364行:開始調用指定的控制器類的指定方法(當然這裡是默認控制器Welcome的默認方法index)。看看這個流程,首先一個if判斷,如果你的控制器類中有方法_remap,只調用它了,所以前面說以下劃線開頭的方法除了_remap,這也是CI的一個類的方法的規則,有了這個重映射方法,只調它。默認的Welcome控制器中沒有_remap方法,進入else,else中還有個if,再次判斷,我們調用的方法是否在這個控制器類中,如果不在的話注定要404了,只是404的調用腳本稍有不同。假如我們得application/config/routes.php文件中的routes['404_override']選項不為空(它就是我們自定義的404錯誤頁腳本路徑),將去解析它指定的目錄中類名與方法名,如果類定義不存在且文件(自定義404文件)也不存在,就直接調調show_404展示CI默認的404頁,只是類定義不存在的話就加載該文件,刪除原對象,再new一個新的404對象(348行),當然因類定義不存在,這裡理論上是要報錯的;假如routes['404_override']選項為空,那麼直接啟用show_404方法。這個show_404是公用方法,自然是在system/core目錄下的Common.php腳本裡定義的。
如果我們調用的方法在這個控制器定義中,就要運行這行了:call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));,調用$CI實例的$method方法,參數就是後邊的數組(URI核心類對象的成員rsegments,它被重新索引,從下標2開始是解析的所調用方法的各個參數),$CI就是我們得控制器類實例,$method是對應調用方法。至此,才真正的調用了一個控制器的方法(默認Welcome的index方法),而這還是最簡單的情況>3<
它然後就是進入Welcome控制器類調用index方法加載一個默認的頁面了,就是開頭的歡迎頁。在index加載歡迎頁($this->load->view(...))又加載了核心類
CodeIgniter.php後面剩下的幾行
364行:設置一個基准測試標記點,控制器執行結束標記。
378-381行:如果調用Hooks鉤子在輸出覆蓋(display_override)的擴展失敗的話,做最後到浏覽器的信息輸出(這個輸出主要做一些寫入緩存,整個方法執行、頁面加載等的時間、內存等的統計,頭信息的設置、日志的記錄等等...)。調用默認方法的話實際上從這開始到CodeIgniter.php結束沒執行。
388行:嘗試調用Hooks鉤子擴展的,在系統執行結束時。
390-398行:如果還有數據庫類實例的,關閉掉它的連接。
CodeIgniter.php結束。
OK,來看看調用一個默認的Welcome控制器默認方法都間接加載了哪些文件

可以看到有CI介紹的系統類清單全在裡邊。
但是最後的Loader類好像沒有在CodeIgniter中明確加載,確實,它是在實例化Welcome類時,調它的父類CI_Controller的構造函數裡邊通過load_class加載的。
如果輸入一個錯誤的鏈接訪問如localhost/ci/index.php/welcome/func,404是當然的

多加載了一個Exception類,這個小細節就體現了CI的原則,“組件的導入和函數的執行只有在被要求的時候才執行,而不是在全局范圍”。所以CI還是很不錯的一個框架。
時間又被拉長了。。。
日後再補下其他的,主要是數據庫和緩存文件的加載麻煩點,其他的還行。