CI加載流程小結

  無聊,決定水一把。php

  CI(CodeIgniter)是我最先接觸的一個框架,到如今也只是用了其中一點零碎的方法。一直想對其流程作個小結,卻老是因各類各樣的「理由」挨着。看見別人圖表齊上陣,沒那耐心,就從代碼提及吧,權當作個筆記,記念一下。html

  看在線的用戶手冊,也知道,將CI下載下來(最新版本2.2.1),解壓到機子上,好比www目錄,可改個根目錄名(原名CodeIgniter-2.2-stable太長),初步目錄文件以下,固然這在是windows下面。前端

     

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

  

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

  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,因此是設置報告全部級別的錯誤。bootstrap

  49-59行:$system_path變量定義CI的默認的系統腳本目錄是 system,61-75行定義當前默認的供咱們主要開發用的目錄爲 application。windows

  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默認是沒有定義的。api

     

   從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仍是很不錯的一個框架。

  時間又被拉長了。。。

  往後再補下其餘的,主要是數據庫和緩存文件的加載麻煩點,其餘的還行。  

 ====================================================================================================

  補充下CI_Controller核心類,前面,說過在加載咱們本身的控制器類時,首先就要加載核心控制器類並會調用它的構造函數初始化它,仍是看看它,複習下

 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  * CodeIgniter Application Controller Class
21  *
22  * This class object is the super class that every library in
23  * CodeIgniter will be assigned to.
24  *
25  * @package        CodeIgniter
26  * @subpackage    Libraries
27  * @category    Libraries
28  * @author        EllisLab Dev Team
29  * @link        http://codeigniter.com/user_guide/general/controllers.html
30  */
31 class CI_Controller {
32 
33     private static $instance;
34 
35     /**
36      * Constructor
37      */
38     public function __construct()
39     {
40         self::$instance =& $this;
41 
42         // Assign all the class objects that were instantiated by the
43         // bootstrap file (CodeIgniter.php) to local class variables
44         // so that CI can run as one big super object.
45         foreach (is_loaded() as $var => $class)
46         {
47             $this->$var =& load_class($class);
48         }
49 
50         $this->load =& load_class('Loader', 'core');
51 
52         $this->load->initialize();
53         
54         log_message('debug', "Controller Class Initialized");
55     }
56     
57     public static function &get_instance()
58     {
59         return self::$instance;
60     }
61 }
62 // END Controller class
63 
64 /* End of file Controller.php */
65 /* Location: ./system/core/Controller.php */
View Code

  看它的構造函數,先循環is_loaded方法返回的數組(參見核心類文件夾system/core/Common.php),前面說過每當經過load_class加載一個核心類時,load_class內部調用is_loaded,傳遞類名將加載過的核心類對象放入一個靜態數組,並返回該靜態數組,因此這裏調用is_loaded返回加載的核心類數組並循環它,將它們一一置爲CI_Controller類的屬性成員($this->$var =& load_class($class);),而後直接$this->load =& load_class('Loader', 'core');將加載類Loader也置爲它的成員。

  下邊的get_instance方法返回本類實例,而它是被全局get_instance方法調用的(system/core/CodeIgniter.php),所以全局方法get_instance直接得到了CI_Controller的實例,所以能夠調用這些核心類的方法,因此get_instance纔有超級方法的稱呼。

  在CI_Controller構造的方法的倒數第二句:$this->load->initialize();,那些庫、輔助函數、語言、第三方包(package)、配置文件等等,若是是須要自動加載的,就是在這裏初始化加載的,固然它們雖是自動加載,仍是經過咱們手動加載好比$this->load->model('someModel')的形式,調用的是model方法加載Model類,自動加載的Model類最後也是經過它來加載的。

  回憶下,CI有一個這樣的自動加載形式,即不是經過臨時手動來調用的,而是當這個框架來加載時就已經加載或初始化的一些類或者文件,在application/config能夠找到一個autoload.php(第一次看我還覺得是放的__autoload()方法的文件,名字相似),打開它能夠看到就是定義一個$autoload數組,以要加載的文件類型名做爲鍵,對應的子數組元素存放文件名(是類的話根類名相關),要加載幾個則向該數組中添加幾個,典型的方式是:

    

  添加以後,將須要自動加載的文件放在指定目錄就給自動加載了。

  知道這麼一種機制後,再來看看CI_Controller中的$this->load->initialize();,來到Loder核心類,展(tui)開(dao)看看,在Loader中,有這麼一些屬性成員,典型的如這麼兩種

  protected $_ci_model_paths = array();

  protected $_ci_models = array();

  前一個是要加載的Model類所在的路徑,後一個是存放已加載的Model類,它們不必定是對應的,即有一個加載變量的數組就要有個路徑數組,也可能名字不是對應命名的。

   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  * Loader Class
  21  *
  22  * Loads views and files
  23  *
  24  * @package        CodeIgniter
  25  * @subpackage    Libraries
  26  * @author        EllisLab Dev Team
  27  * @category    Loader
  28  * @link        http://codeigniter.com/user_guide/libraries/loader.html
  29  */
  30 class CI_Loader {
  31 
  32     // All these are set automatically. Don't mess with them.
  33     /**
  34      * Nesting level of the output buffering mechanism
  35      *
  36      * @var int
  37      * @access protected
  38      */
  39     protected $_ci_ob_level;
  40     /**
  41      * List of paths to load views from
  42      *
  43      * @var array
  44      * @access protected
  45      */
  46     protected $_ci_view_paths        = array();
  47     /**
  48      * List of paths to load libraries from
  49      *
  50      * @var array
  51      * @access protected
  52      */
  53     protected $_ci_library_paths    = array();
  54     /**
  55      * List of paths to load models from
  56      *
  57      * @var array
  58      * @access protected
  59      */
  60     protected $_ci_model_paths        = array();
  61     /**
  62      * List of paths to load helpers from
  63      *
  64      * @var array
  65      * @access protected
  66      */
  67     protected $_ci_helper_paths        = array();
  68     /**
  69      * List of loaded base classes
  70      * Set by the controller class
  71      *
  72      * @var array
  73      * @access protected
  74      */
  75     protected $_base_classes        = array(); // Set by the controller class
  76     /**
  77      * List of cached variables
  78      *
  79      * @var array
  80      * @access protected
  81      */
  82     protected $_ci_cached_vars        = array();
  83     /**
  84      * List of loaded classes
  85      *
  86      * @var array
  87      * @access protected
  88      */
  89     protected $_ci_classes            = array();
  90     /**
  91      * List of loaded files
  92      *
  93      * @var array
  94      * @access protected
  95      */
  96     protected $_ci_loaded_files        = array();
  97     /**
  98      * List of loaded models
  99      *
 100      * @var array
 101      * @access protected
 102      */
 103     protected $_ci_models            = array();
 104     /**
 105      * List of loaded helpers
 106      *
 107      * @var array
 108      * @access protected
 109      */
 110     protected $_ci_helpers            = array();
 111     /**
 112      * List of class name mappings
 113      *
 114      * @var array
 115      * @access protected
 116      */
 117     protected $_ci_varmap            = array('unit_test' => 'unit',
 118                                             'user_agent' => 'agent');
 119 
 120     /**
 121      * Constructor
 122      *
 123      * Sets the path to the view files and gets the initial output buffering level
 124      */
 125     public function __construct()
 126     {
 127         $this->_ci_ob_level  = ob_get_level();
 128         $this->_ci_library_paths = array(APPPATH, BASEPATH);
 129         $this->_ci_helper_paths = array(APPPATH, BASEPATH);
 130         $this->_ci_model_paths = array(APPPATH);
 131         $this->_ci_view_paths = array(APPPATH.'views/'    => TRUE);
 132 
 133         log_message('debug', "Loader Class Initialized");
 134     }
 135 
 136     // --------------------------------------------------------------------
 137 
 138     /**
 139      * Initialize the Loader
 140      *
 141      * This method is called once in CI_Controller.
 142      *
 143      * @param     array
 144      * @return     object
 145      */
 146     public function initialize()
 147     {
 148         $this->_ci_classes = array();
 149         $this->_ci_loaded_files = array();
 150         $this->_ci_models = array();
 151         $this->_base_classes =& is_loaded();
 152 
 153         $this->_ci_autoloader();
 154 
 155         return $this;
 156     }
 157 
 158     // --------------------------------------------------------------------
 159 
 160     /**
 161      * Is Loaded
 162      *
 163      * A utility function to test if a class is in the self::$_ci_classes array.
 164      * This function returns the object name if the class tested for is loaded,
 165      * and returns FALSE if it isn't.
 166      *
 167      * It is mainly used in the form_helper -> _get_validation_object()
 168      *
 169      * @param     string    class being checked for
 170      * @return     mixed    class object name on the CI SuperObject or FALSE
 171      */
 172     public function is_loaded($class)
 173     {
 174         if (isset($this->_ci_classes[$class]))
 175         {
 176             return $this->_ci_classes[$class];
 177         }
 178 
 179         return FALSE;
 180     }
 181 
 182     // --------------------------------------------------------------------
 183 
 184     /**
 185      * Class Loader
 186      *
 187      * This function lets users load and instantiate classes.
 188      * It is designed to be called from a user's app controllers.
 189      *
 190      * @param    string    the name of the class
 191      * @param    mixed    the optional parameters
 192      * @param    string    an optional object name
 193      * @return    void
 194      */
 195     public function library($library = '', $params = NULL, $object_name = NULL)
 196     {
 197         if (is_array($library))
 198         {
 199             foreach ($library as $class)
 200             {
 201                 $this->library($class, $params);
 202             }
 203 
 204             return;
 205         }
 206 
 207         if ($library == '' OR isset($this->_base_classes[$library]))
 208         {
 209             return FALSE;
 210         }
 211 
 212         if ( ! is_null($params) && ! is_array($params))
 213         {
 214             $params = NULL;
 215         }
 216 
 217         $this->_ci_load_class($library, $params, $object_name);
 218     }
 219 
 220     // --------------------------------------------------------------------
 221 
 222     /**
 223      * Model Loader
 224      *
 225      * This function lets users load and instantiate models.
 226      *
 227      * @param    string    the name of the class
 228      * @param    string    name for the model
 229      * @param    bool    database connection
 230      * @return    void
 231      */
 232     public function model($model, $name = '', $db_conn = FALSE)
 233     {
 234         if (is_array($model))
 235         {
 236             foreach ($model as $babe)
 237             {
 238                 $this->model($babe);
 239             }
 240             return;
 241         }
 242 
 243         if ($model == '')
 244         {
 245             return;
 246         }
 247 
 248         $path = '';
 249 
 250         // Is the model in a sub-folder? If so, parse out the filename and path.
 251         if (($last_slash = strrpos($model, '/')) !== FALSE)
 252         {
 253             // The path is in front of the last slash
 254             $path = substr($model, 0, $last_slash + 1);
 255 
 256             // And the model name behind it
 257             $model = substr($model, $last_slash + 1);
 258         }
 259 
 260         if ($name == '')
 261         {
 262             $name = $model;
 263         }
 264 
 265         if (in_array($name, $this->_ci_models, TRUE))
 266         {
 267             return;
 268         }
 269 
 270         $CI =& get_instance();
 271         if (isset($CI->$name))
 272         {
 273             show_error('The model name you are loading is the name of a resource that is already being used: '.$name);
 274         }
 275 
 276         $model = strtolower($model);
 277 
 278         foreach ($this->_ci_model_paths as $mod_path)
 279         {
 280             if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
 281             {
 282                 continue;
 283             }
 284 
 285             if ($db_conn !== FALSE AND ! class_exists('CI_DB'))
 286             {
 287                 if ($db_conn === TRUE)
 288                 {
 289                     $db_conn = '';
 290                 }
 291 
 292                 $CI->load->database($db_conn, FALSE, TRUE);
 293             }
 294 
 295             if ( ! class_exists('CI_Model'))
 296             {
 297                 load_class('Model', 'core');
 298             }
 299 
 300             require_once($mod_path.'models/'.$path.$model.'.php');
 301 
 302             $model = ucfirst($model);
 303 
 304             $CI->$name = new $model();
 305 
 306             $this->_ci_models[] = $name;
 307             return;
 308         }
 309 
 310         // couldn't find the model
 311         show_error('Unable to locate the model you have specified: '.$model);
 312     }
 313 
 314     // --------------------------------------------------------------------
 315 
 316     /**
 317      * Database Loader
 318      *
 319      * @param    string    the DB credentials
 320      * @param    bool    whether to return the DB object
 321      * @param    bool    whether to enable active record (this allows us to override the config setting)
 322      * @return    object
 323      */
 324     public function database($params = '', $return = FALSE, $active_record = NULL)
 325     {
 326         // Grab the super object
 327         $CI =& get_instance();
 328 
 329         // Do we even need to load the database class?
 330         if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db))
 331         {
 332             return FALSE;
 333         }
 334 
 335         require_once(BASEPATH.'database/DB.php');
 336 
 337         if ($return === TRUE)
 338         {
 339             return DB($params, $active_record);
 340         }
 341 
 342         // Initialize the db variable.  Needed to prevent
 343         // reference errors with some configurations
 344         $CI->db = '';
 345 
 346         // Load the DB class
 347         $CI->db =& DB($params, $active_record);
 348     }
 349 
 350     // --------------------------------------------------------------------
 351 
 352     /**
 353      * Load the Utilities Class
 354      *
 355      * @return    string
 356      */
 357     public function dbutil()
 358     {
 359         if ( ! class_exists('CI_DB'))
 360         {
 361             $this->database();
 362         }
 363 
 364         $CI =& get_instance();
 365 
 366         // for backwards compatibility, load dbforge so we can extend dbutils off it
 367         // this use is deprecated and strongly discouraged
 368         $CI->load->dbforge();
 369 
 370         require_once(BASEPATH.'database/DB_utility.php');
 371         require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility.php');
 372         $class = 'CI_DB_'.$CI->db->dbdriver.'_utility';
 373 
 374         $CI->dbutil = new $class();
 375     }
 376 
 377     // --------------------------------------------------------------------
 378 
 379     /**
 380      * Load the Database Forge Class
 381      *
 382      * @return    string
 383      */
 384     public function dbforge()
 385     {
 386         if ( ! class_exists('CI_DB'))
 387         {
 388             $this->database();
 389         }
 390 
 391         $CI =& get_instance();
 392 
 393         require_once(BASEPATH.'database/DB_forge.php');
 394         require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge.php');
 395         $class = 'CI_DB_'.$CI->db->dbdriver.'_forge';
 396 
 397         $CI->dbforge = new $class();
 398     }
 399 
 400     // --------------------------------------------------------------------
 401 
 402     /**
 403      * Load View
 404      *
 405      * This function is used to load a "view" file.  It has three parameters:
 406      *
 407      * 1. The name of the "view" file to be included.
 408      * 2. An associative array of data to be extracted for use in the view.
 409      * 3. TRUE/FALSE - whether to return the data or load it.  In
 410      * some cases it's advantageous to be able to return data so that
 411      * a developer can process it in some way.
 412      *
 413      * @param    string
 414      * @param    array
 415      * @param    bool
 416      * @return    void
 417      */
 418     public function view($view, $vars = array(), $return = FALSE)
 419     {
 420         return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
 421     }
 422 
 423     // --------------------------------------------------------------------
 424 
 425     /**
 426      * Load File
 427      *
 428      * This is a generic file loader
 429      *
 430      * @param    string
 431      * @param    bool
 432      * @return    string
 433      */
 434     public function file($path, $return = FALSE)
 435     {
 436         return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
 437     }
 438 
 439     // --------------------------------------------------------------------
 440 
 441     /**
 442      * Set Variables
 443      *
 444      * Once variables are set they become available within
 445      * the controller class and its "view" files.
 446      *
 447      * @param    array
 448      * @param     string
 449      * @return    void
 450      */
 451     public function vars($vars = array(), $val = '')
 452     {
 453         if ($val != '' AND is_string($vars))
 454         {
 455             $vars = array($vars => $val);
 456         }
 457 
 458         $vars = $this->_ci_object_to_array($vars);
 459 
 460         if (is_array($vars) AND count($vars) > 0)
 461         {
 462             foreach ($vars as $key => $val)
 463             {
 464                 $this->_ci_cached_vars[$key] = $val;
 465             }
 466         }
 467     }
 468 
 469     // --------------------------------------------------------------------
 470 
 471     /**
 472      * Get Variable
 473      *
 474      * Check if a variable is set and retrieve it.
 475      *
 476      * @param    array
 477      * @return    void
 478      */
 479     public function get_var($key)
 480     {
 481         return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
 482     }
 483 
 484     // --------------------------------------------------------------------
 485 
 486     /**
 487      * Load Helper
 488      *
 489      * This function loads the specified helper file.
 490      *
 491      * @param    mixed
 492      * @return    void
 493      */
 494     public function helper($helpers = array())
 495     {
 496         foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper)
 497         {
 498             if (isset($this->_ci_helpers[$helper]))
 499             {
 500                 continue;
 501             }
 502 
 503             $ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.'.php';
 504 
 505             // Is this a helper extension request?
 506             if (file_exists($ext_helper))
 507             {
 508                 $base_helper = BASEPATH.'helpers/'.$helper.'.php';
 509 
 510                 if ( ! file_exists($base_helper))
 511                 {
 512                     show_error('Unable to load the requested file: helpers/'.$helper.'.php');
 513                 }
 514 
 515                 include_once($ext_helper);
 516                 include_once($base_helper);
 517 
 518                 $this->_ci_helpers[$helper] = TRUE;
 519                 log_message('debug', 'Helper loaded: '.$helper);
 520                 continue;
 521             }
 522 
 523             // Try to load the helper
 524             foreach ($this->_ci_helper_paths as $path)
 525             {
 526                 if (file_exists($path.'helpers/'.$helper.'.php'))
 527                 {
 528                     include_once($path.'helpers/'.$helper.'.php');
 529 
 530                     $this->_ci_helpers[$helper] = TRUE;
 531                     log_message('debug', 'Helper loaded: '.$helper);
 532                     break;
 533                 }
 534             }
 535 
 536             // unable to load the helper
 537             if ( ! isset($this->_ci_helpers[$helper]))
 538             {
 539                 show_error('Unable to load the requested file: helpers/'.$helper.'.php');
 540             }
 541         }
 542     }
 543 
 544     // --------------------------------------------------------------------
 545 
 546     /**
 547      * Load Helpers
 548      *
 549      * This is simply an alias to the above function in case the
 550      * user has written the plural form of this function.
 551      *
 552      * @param    array
 553      * @return    void
 554      */
 555     public function helpers($helpers = array())
 556     {
 557         $this->helper($helpers);
 558     }
 559 
 560     // --------------------------------------------------------------------
 561 
 562     /**
 563      * Loads a language file
 564      *
 565      * @param    array
 566      * @param    string
 567      * @return    void
 568      */
 569     public function language($file = array(), $lang = '')
 570     {
 571         $CI =& get_instance();
 572 
 573         if ( ! is_array($file))
 574         {
 575             $file = array($file);
 576         }
 577 
 578         foreach ($file as $langfile)
 579         {
 580             $CI->lang->load($langfile, $lang);
 581         }
 582     }
 583 
 584     // --------------------------------------------------------------------
 585 
 586     /**
 587      * Loads a config file
 588      *
 589      * @param    string
 590      * @param    bool
 591      * @param     bool
 592      * @return    void
 593      */
 594     public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
 595     {
 596         $CI =& get_instance();
 597         $CI->config->load($file, $use_sections, $fail_gracefully);
 598     }
 599 
 600     // --------------------------------------------------------------------
 601 
 602     /**
 603      * Driver
 604      *
 605      * Loads a driver library
 606      *
 607      * @param    string    the name of the class
 608      * @param    mixed    the optional parameters
 609      * @param    string    an optional object name
 610      * @return    void
 611      */
 612     public function driver($library = '', $params = NULL, $object_name = NULL)
 613     {
 614         if ( ! class_exists('CI_Driver_Library'))
 615         {
 616             // we aren't instantiating an object here, that'll be done by the Library itself
 617             require BASEPATH.'libraries/Driver.php';
 618         }
 619 
 620         if ($library == '')
 621         {
 622             return FALSE;
 623         }
 624 
 625         // We can save the loader some time since Drivers will *always* be in a subfolder,
 626         // and typically identically named to the library
 627         if ( ! strpos($library, '/'))
 628         {
 629             $library = ucfirst($library).'/'.$library;
 630         }
 631 
 632         return $this->library($library, $params, $object_name);
 633     }
 634 
 635     // --------------------------------------------------------------------
 636 
 637     /**
 638      * Add Package Path
 639      *
 640      * Prepends a parent path to the library, model, helper, and config path arrays
 641      *
 642      * @param    string
 643      * @param     boolean
 644      * @return    void
 645      */
 646     public function add_package_path($path, $view_cascade=TRUE)
 647     {
 648         $path = rtrim($path, '/').'/';
 649 
 650         array_unshift($this->_ci_library_paths, $path);
 651         array_unshift($this->_ci_model_paths, $path);
 652         array_unshift($this->_ci_helper_paths, $path);
 653 
 654         $this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths;
 655 
 656         // Add config file path
 657         $config =& $this->_ci_get_component('config');
 658         array_unshift($config->_config_paths, $path);
 659     }
 660 
 661     // --------------------------------------------------------------------
 662 
 663     /**
 664      * Get Package Paths
 665      *
 666      * Return a list of all package paths, by default it will ignore BASEPATH.
 667      *
 668      * @param    string
 669      * @return    void
 670      */
 671     public function get_package_paths($include_base = FALSE)
 672     {
 673         return $include_base === TRUE ? $this->_ci_library_paths : $this->_ci_model_paths;
 674     }
 675 
 676     // --------------------------------------------------------------------
 677 
 678     /**
 679      * Remove Package Path
 680      *
 681      * Remove a path from the library, model, and helper path arrays if it exists
 682      * If no path is provided, the most recently added path is removed.
 683      *
 684      * @param    type
 685      * @param     bool
 686      * @return    type
 687      */
 688     public function remove_package_path($path = '', $remove_config_path = TRUE)
 689     {
 690         $config =& $this->_ci_get_component('config');
 691 
 692         if ($path == '')
 693         {
 694             $void = array_shift($this->_ci_library_paths);
 695             $void = array_shift($this->_ci_model_paths);
 696             $void = array_shift($this->_ci_helper_paths);
 697             $void = array_shift($this->_ci_view_paths);
 698             $void = array_shift($config->_config_paths);
 699         }
 700         else
 701         {
 702             $path = rtrim($path, '/').'/';
 703             foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var)
 704             {
 705                 if (($key = array_search($path, $this->{$var})) !== FALSE)
 706                 {
 707                     unset($this->{$var}[$key]);
 708                 }
 709             }
 710 
 711             if (isset($this->_ci_view_paths[$path.'views/']))
 712             {
 713                 unset($this->_ci_view_paths[$path.'views/']);
 714             }
 715 
 716             if (($key = array_search($path, $config->_config_paths)) !== FALSE)
 717             {
 718                 unset($config->_config_paths[$key]);
 719             }
 720         }
 721 
 722         // make sure the application default paths are still in the array
 723         $this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
 724         $this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
 725         $this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
 726         $this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
 727         $config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
 728     }
 729 
 730     // --------------------------------------------------------------------
 731 
 732     /**
 733      * Loader
 734      *
 735      * This function is used to load views and files.
 736      * Variables are prefixed with _ci_ to avoid symbol collision with
 737      * variables made available to view files
 738      *
 739      * @param    array
 740      * @return    void
 741      */
 742     protected function _ci_load($_ci_data)
 743     {
 744         // Set the default data variables
 745         foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
 746         {
 747             $$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val];
 748         }
 749 
 750         $file_exists = FALSE;
 751 
 752         // Set the path to the requested file
 753         if ($_ci_path != '')
 754         {
 755             $_ci_x = explode('/', $_ci_path);
 756             $_ci_file = end($_ci_x);
 757         }
 758         else
 759         {
 760             $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
 761             $_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view;
 762 
 763             foreach ($this->_ci_view_paths as $view_file => $cascade)
 764             {
 765                 if (file_exists($view_file.$_ci_file))
 766                 {
 767                     $_ci_path = $view_file.$_ci_file;
 768                     $file_exists = TRUE;
 769                     break;
 770                 }
 771 
 772                 if ( ! $cascade)
 773                 {
 774                     break;
 775                 }
 776             }
 777         }
 778 
 779         if ( ! $file_exists && ! file_exists($_ci_path))
 780         {
 781             show_error('Unable to load the requested file: '.$_ci_file);
 782         }
 783 
 784         // This allows anything loaded using $this->load (views, files, etc.)
 785         // to become accessible from within the Controller and Model functions.
 786 
 787         $_ci_CI =& get_instance();
 788         foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
 789         {
 790             if ( ! isset($this->$_ci_key))
 791             {
 792                 $this->$_ci_key =& $_ci_CI->$_ci_key;
 793             }
 794         }
 795 
 796         /*
 797          * Extract and cache variables
 798          *
 799          * You can either set variables using the dedicated $this->load_vars()
 800          * function or via the second parameter of this function. We'll merge
 801          * the two types and cache them so that views that are embedded within
 802          * other views can have access to these variables.
 803          */
 804         if (is_array($_ci_vars))
 805         {
 806             $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
 807         }
 808         extract($this->_ci_cached_vars);
 809 
 810         /*
 811          * Buffer the output
 812          *
 813          * We buffer the output for two reasons:
 814          * 1. Speed. You get a significant speed boost.
 815          * 2. So that the final rendered template can be
 816          * post-processed by the output class.  Why do we
 817          * need post processing?  For one thing, in order to
 818          * show the elapsed page load time.  Unless we
 819          * can intercept the content right before it's sent to
 820          * the browser and then stop the timer it won't be accurate.
 821          */
 822         ob_start();
 823 
 824         // If the PHP installation does not support short tags we'll
 825         // do a little string replacement, changing the short tags
 826         // to standard PHP echo statements.
 827 
 828         if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
 829         {
 830             echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
 831         }
 832         else
 833         {
 834             include($_ci_path); // include() vs include_once() allows for multiple views with the same name
 835         }
 836 
 837         log_message('debug', 'File loaded: '.$_ci_path);
 838 
 839         // Return the file data if requested
 840         if ($_ci_return === TRUE)
 841         {
 842             $buffer = ob_get_contents();
 843             @ob_end_clean();
 844             return $buffer;
 845         }
 846 
 847         /*
 848          * Flush the buffer... or buff the flusher?
 849          *
 850          * In order to permit views to be nested within
 851          * other views, we need to flush the content back out whenever
 852          * we are beyond the first level of output buffering so that
 853          * it can be seen and included properly by the first included
 854          * template and any subsequent ones. Oy!
 855          *
 856          */
 857         if (ob_get_level() > $this->_ci_ob_level + 1)
 858         {
 859             ob_end_flush();
 860         }
 861         else
 862         {
 863             $_ci_CI->output->append_output(ob_get_contents());
 864             @ob_end_clean();
 865         }
 866     }
 867 
 868     // --------------------------------------------------------------------
 869 
 870     /**
 871      * Load class
 872      *
 873      * This function loads the requested class.
 874      *
 875      * @param    string    the item that is being loaded
 876      * @param    mixed    any additional parameters
 877      * @param    string    an optional object name
 878      * @return    void
 879      */
 880     protected function _ci_load_class($class, $params = NULL, $object_name = NULL)
 881     {
 882         // Get the class name, and while we're at it trim any slashes.
 883         // The directory path can be included as part of the class name,
 884         // but we don't want a leading slash
 885         $class = str_replace('.php', '', trim($class, '/'));
 886 
 887         // Was the path included with the class name?
 888         // We look for a slash to determine this
 889         $subdir = '';
 890         if (($last_slash = strrpos($class, '/')) !== FALSE)
 891         {
 892             // Extract the path
 893             $subdir = substr($class, 0, $last_slash + 1);
 894 
 895             // Get the filename from the path
 896             $class = substr($class, $last_slash + 1);
 897         }
 898 
 899         // We'll test for both lowercase and capitalized versions of the file name
 900         foreach (array(ucfirst($class), strtolower($class)) as $class)
 901         {
 902             $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php';
 903 
 904             // Is this a class extension request?
 905             if (file_exists($subclass))
 906             {
 907                 $baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php';
 908 
 909                 if ( ! file_exists($baseclass))
 910                 {
 911                     log_message('error', "Unable to load the requested class: ".$class);
 912                     show_error("Unable to load the requested class: ".$class);
 913                 }
 914 
 915                 // Safety:  Was the class already loaded by a previous call?
 916                 if (in_array($subclass, $this->_ci_loaded_files))
 917                 {
 918                     // Before we deem this to be a duplicate request, let's see
 919                     // if a custom object name is being supplied.  If so, we'll
 920                     // return a new instance of the object
 921                     if ( ! is_null($object_name))
 922                     {
 923                         $CI =& get_instance();
 924                         if ( ! isset($CI->$object_name))
 925                         {
 926                             return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
 927                         }
 928                     }
 929 
 930                     $is_duplicate = TRUE;
 931                     log_message('debug', $class." class already loaded. Second attempt ignored.");
 932                     return;
 933                 }
 934 
 935                 include_once($baseclass);
 936                 include_once($subclass);
 937                 $this->_ci_loaded_files[] = $subclass;
 938 
 939                 return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
 940             }
 941 
 942             // Lets search for the requested library file and load it.
 943             $is_duplicate = FALSE;
 944             foreach ($this->_ci_library_paths as $path)
 945             {
 946                 $filepath = $path.'libraries/'.$subdir.$class.'.php';
 947 
 948                 // Does the file exist?  No?  Bummer...
 949                 if ( ! file_exists($filepath))
 950                 {
 951                     continue;
 952                 }
 953 
 954                 // Safety:  Was the class already loaded by a previous call?
 955                 if (in_array($filepath, $this->_ci_loaded_files))
 956                 {
 957                     // Before we deem this to be a duplicate request, let's see
 958                     // if a custom object name is being supplied.  If so, we'll
 959                     // return a new instance of the object
 960                     if ( ! is_null($object_name))
 961                     {
 962                         $CI =& get_instance();
 963                         if ( ! isset($CI->$object_name))
 964                         {
 965                             return $this->_ci_init_class($class, '', $params, $object_name);
 966                         }
 967                     }
 968 
 969                     $is_duplicate = TRUE;
 970                     log_message('debug', $class." class already loaded. Second attempt ignored.");
 971                     return;
 972                 }
 973 
 974                 include_once($filepath);
 975                 $this->_ci_loaded_files[] = $filepath;
 976                 return $this->_ci_init_class($class, '', $params, $object_name);
 977             }
 978 
 979         } // END FOREACH
 980 
 981         // One last attempt.  Maybe the library is in a subdirectory, but it wasn't specified?
 982         if ($subdir == '')
 983         {
 984             $path = strtolower($class).'/'.$class;
 985             return $this->_ci_load_class($path, $params);
 986         }
 987 
 988         // If we got this far we were unable to find the requested class.
 989         // We do not issue errors if the load call failed due to a duplicate request
 990         if ($is_duplicate == FALSE)
 991         {
 992             log_message('error', "Unable to load the requested class: ".$class);
 993             show_error("Unable to load the requested class: ".$class);
 994         }
 995     }
 996 
 997     // --------------------------------------------------------------------
 998 
 999     /**
1000      * Instantiates a class
1001      *
1002      * @param    string
1003      * @param    string
1004      * @param    bool
1005      * @param    string    an optional object name
1006      * @return    null
1007      */
1008     protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL)
1009     {
1010         // Is there an associated config file for this class?  Note: these should always be lowercase
1011         if ($config === NULL)
1012         {
1013             // Fetch the config paths containing any package paths
1014             $config_component = $this->_ci_get_component('config');
1015 
1016             if (is_array($config_component->_config_paths))
1017             {
1018                 // Break on the first found file, thus package files
1019                 // are not overridden by default paths
1020                 foreach ($config_component->_config_paths as $path)
1021                 {
1022                     // We test for both uppercase and lowercase, for servers that
1023                     // are case-sensitive with regard to file names. Check for environment
1024                     // first, global next
1025                     if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
1026                     {
1027                         include($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
1028                         break;
1029                     }
1030                     elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
1031                     {
1032                         include($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
1033                         break;
1034                     }
1035                     elseif (file_exists($path .'config/'.strtolower($class).'.php'))
1036                     {
1037                         include($path .'config/'.strtolower($class).'.php');
1038                         break;
1039                     }
1040                     elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php'))
1041                     {
1042                         include($path .'config/'.ucfirst(strtolower($class)).'.php');
1043                         break;
1044                     }
1045                 }
1046             }
1047         }
1048 
1049         if ($prefix == '')
1050         {
1051             if (class_exists('CI_'.$class))
1052             {
1053                 $name = 'CI_'.$class;
1054             }
1055             elseif (class_exists(config_item('subclass_prefix').$class))
1056             {
1057                 $name = config_item('subclass_prefix').$class;
1058             }
1059             else
1060             {
1061                 $name = $class;
1062             }
1063         }
1064         else
1065         {
1066             $name = $prefix.$class;
1067         }
1068 
1069         // Is the class name valid?
1070         if ( ! class_exists($name))
1071         {
1072             log_message('error', "Non-existent class: ".$name);
1073             show_error("Non-existent class: ".$class);
1074         }
1075 
1076         // Set the variable name we will assign the class to
1077         // Was a custom class name supplied?  If so we'll use it
1078         $class = strtolower($class);
1079 
1080         if (is_null($object_name))
1081         {
1082             $classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class];
1083         }
1084         else
1085         {
1086             $classvar = $object_name;
1087         }
1088 
1089         // Save the class name and object name
1090         $this->_ci_classes[$class] = $classvar;
1091 
1092         // Instantiate the class
1093         $CI =& get_instance();
1094         if ($config !== NULL)
1095         {
1096             $CI->$classvar = new $name($config);
1097         }
1098         else
1099         {
1100             $CI->$classvar = new $name;
1101         }
1102     }
1103 
1104     // --------------------------------------------------------------------
1105 
1106     /**
1107      * Autoloader
1108      *
1109      * The config/autoload.php file contains an array that permits sub-systems,
1110      * libraries, and helpers to be loaded automatically.
1111      *
1112      * @param    array
1113      * @return    void
1114      */
1115     private function _ci_autoloader()
1116     {
1117         if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
1118         {
1119             include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
1120         }
1121         else
1122         {
1123             include(APPPATH.'config/autoload.php');
1124         }
1125 
1126         if ( ! isset($autoload))
1127         {
1128             return FALSE;
1129         }
1130 
1131         // Autoload packages
1132         if (isset($autoload['packages']))
1133         {
1134             foreach ($autoload['packages'] as $package_path)
1135             {
1136                 $this->add_package_path($package_path);
1137             }
1138         }
1139 
1140         // Load any custom config file
1141         if (count($autoload['config']) > 0)
1142         {
1143             $CI =& get_instance();
1144             foreach ($autoload['config'] as $key => $val)
1145             {
1146                 $CI->config->load($val);
1147             }
1148         }
1149 
1150         // Autoload helpers and languages
1151         foreach (array('helper', 'language') as $type)
1152         {
1153             if (isset($autoload[$type]) AND count($autoload[$type]) > 0)
1154             {
1155                 $this->$type($autoload[$type]);
1156             }
1157         }
1158 
1159         // A little tweak to remain backward compatible
1160         // The $autoload['core'] item was deprecated
1161         if ( ! isset($autoload['libraries']) AND isset($autoload['core']))
1162         {
1163             $autoload['libraries'] = $autoload['core'];
1164         }
1165 
1166         // Load libraries
1167         if (isset($autoload['libraries']) AND count($autoload['libraries']) > 0)
1168         {
1169             // Load the database driver.
1170             if (in_array('database', $autoload['libraries']))
1171             {
1172                 $this->database();
1173                 $autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
1174             }
1175 
1176             // Load all other libraries
1177             foreach ($autoload['libraries'] as $item)
1178             {
1179                 $this->library($item);
1180             }
1181         }
1182 
1183         // Autoload models
1184         if (isset($autoload['model']))
1185         {
1186             $this->model($autoload['model']);
1187         }
1188     }
1189 
1190     // --------------------------------------------------------------------
1191 
1192     /**
1193      * Object to Array
1194      *
1195      * Takes an object as input and converts the class variables to array key/vals
1196      *
1197      * @param    object
1198      * @return    array
1199      */
1200     protected function _ci_object_to_array($object)
1201     {
1202         return (is_object($object)) ? get_object_vars($object) : $object;
1203     }
1204 
1205     // --------------------------------------------------------------------
1206 
1207     /**
1208      * Get a reference to a specific library or model
1209      *
1210      * @param     string
1211      * @return    bool
1212      */
1213     protected function &_ci_get_component($component)
1214     {
1215         $CI =& get_instance();
1216         return $CI->$component;
1217     }
1218 
1219     // --------------------------------------------------------------------
1220 
1221     /**
1222      * Prep filename
1223      *
1224      * This function preps the name of various items to make loading them more reliable.
1225      *
1226      * @param    mixed
1227      * @param     string
1228      * @return    array
1229      */
1230     protected function _ci_prep_filename($filename, $extension)
1231     {
1232         if ( ! is_array($filename))
1233         {
1234             return array(strtolower(str_replace('.php', '', str_replace($extension, '', $filename)).$extension));
1235         }
1236         else
1237         {
1238             foreach ($filename as $key => $val)
1239             {
1240                 $filename[$key] = strtolower(str_replace('.php', '', str_replace($extension, '', $val)).$extension);
1241             }
1242 
1243             return $filename;
1244         }
1245     }
1246 }
1247 
1248 /* End of file Loader.php */
1249 /* Location: ./system/core/Loader.php */
View Code

  125行:它的構造函數,主要是對幾個類型文件的路徑變量初始化,如library庫文件的是array(APPPATH, BASEPATH),預示着它會到這兩個目下取搜索,model數組是array(APPPATH),就只在供咱們開發的APPPATH下邊去搜索加載。

  146行:就是它的initialize()函數,主要作兩件事,前面幾行將即將放要加載的類或文件的數組置空,如$this->_ci_models = array();,對裝載基礎類的變量填滿核心類,如$this->_base_classes =& is_loaded();(前面說過is_loaded可返回全部加載過的核心類),後面的 $this->_ci_autoloader(); 則開始執行自動CI須要自動加載的文件的流程了。

  1115行:轉到_ci_autoloader()的定義,看看它的註釋,意思是config/autoload.php包含了一個數組,容許子系統(sub-systems)、庫、輔助函數等的自動加載。

  它先試圖加載APPPATH.'config/'.ENVIRONMENT.'/autoload.php'(這兩個變量參考前面),默認CI的文件目錄中是沒有development這個目錄的, 因此不管你把他放在哪一個目錄下,再在它下面尋找autoload文件確定是不存在的,而後,它加載的是APPPATH.'config/autoload.php',供咱們開發的application目錄下的配置信息目錄下的autoload.php。

  1131行:成功找到autoload.php並include後,第一個加載的腳本類型是package(包),package這個鍵的元素應該是目錄,例如APPPATH.'third_party',也就是第三方的擴展包,若是要用這個,那麼就得在application下新建一個third_party目錄,裏邊存放這些東西,,在$autoload['package']中添加對應目錄字符串,名字能夠改。foreach調用了add_package_path方法,看看它在幹啥。

  646行:先將傳進來的$path變量,即存放package的目錄,分別添加到_ci_library_paths(存放庫文件目錄的數組,後面相似)、_ci_model_paths、_ci_helper_paths的開頭,包括存放視圖的數組也加進去了在第三方包目錄下的對應的視圖變量array($path.'view'=>TRUE),關於這個存放視圖的數組成員,還沒看過它是幹啥的。而後經過_ci_get_component獲得Config核心類的對象,將$path加入到它的成員_config_paths數組中,這個數組原有的元素也是個路徑:APPPATH。$path變量是「第三方」目錄(APPPATH.'third_party'),從這裏連續的將這個$path變量加到Model、Helper等數組中,這個第三方的package大有獨立系統的意思,本來咱們本身開發的話只是加載application目錄下邊的models、helpers、views等,這裏好像是這些文件也要到$path目錄下再去找一遍,固然咯,前提第你的application下面要有個third_party目錄,而且裏面也放了一些文件。回到1131行 // Autoload packages 下邊的if就是將路徑$path加到Loader類的路徑成員變量中。

  1140行:載入任何自定義的配置文件。檢查$autoload['config']是否有元素,經過超級方法get_instance來調用核心類Config,並經過它的成員函數load加載這些配置文件。注意它主要是加載application/config下的文件。

  1150行:加載helper和language文件。分別調用了helper和language成員方法,就至關於咱們在控制器中這樣用:$this->load->helper(...),因此這個_ci_autoloader方法實際調用仍是最後、公共的加載方法。

  494行:以helper爲例,看它加載時都幹了啥。輔助函數用到的概率也算多的,好比你本身單獨就數組的一些特殊處理做爲一個輔助函數文件。首先加載輔助函數傳過來的參數能夠是單個文件名字符串或多個名稱組成的數組變量,調用_ci_prep_filename這個方法返回的數組進行foreach,1230行的_ci_prep_filename函數功能是對傳入的輔助函數文件名去掉末尾的.php擴展名,加上_helper後綴。因此當咱們加載輔助函數時直接是這樣$this->load->helper('array');就行,傳全名反而錯了。

  

  回到helper函數,若是已經加載過,continue至下一個helper變量。首先找的是這樣的:$ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.'.php',子類前綴默認是MY_,這裏的$helper已是通過上面處理過的,而看變量名猜獲得就是對已有的輔助函數的擴展,如上邊的array_helper.php是在system/helpers目錄下,我如今要對它擴展,就定義個MY_array_helper.php就好了,注意輔助函數只是定義一些函數,不是單個類。若是輔助函數擴展出現了,就要先加載原有的基礎輔助函數文件(BASEPATH.'helpers/'.$helper.'.php',在系統目錄下),所以518-513行對基礎文件進行檢查。只有基礎文件也出現了才都include(實際用include_once)它們。假如我沒有擴展,就要走到524行,循環_ci_helper_paths這個屬性成員來尋找,前面提到CI的Loader核心類的一些成員,是已經加載過得類弄到一個數組中,這是一種類型變量,而後是另一種將各類類型文件即將出現的目錄弄到一塊兒,是另一種類型的,毫無疑問,它裏面就是helper可能存在的目錄集合。它初始化是這樣的:

    

  前面在看到加載package時也看到,若是存在package且$autoload寫入了對應的目錄名,這些目錄是會加到_ci_helper_paths數組前面的,固然默認沒有這個。因此在524行foreach這個變量時,首先找APPPATH.'helpers/'.$helper.'.php',再找BATHPATH.'helpers/'.$helper.'.php',因此就是說,在application/helpers目錄下邊不都是擴展原來的,新寫的也能夠。找到了就include,而且加入到裝載已經加載過的輔助函數的數組_ci_helpers中(再次加載時就直接返回),沒找到就show_error展現錯誤頁面。

  569行:回到自動加載函數_ci_autoloader的1151行,加載language腳本。同理在$autoload['language']下面若是定義有一些須要加載的語言腳本名稱,則foreach每個元素,而後跳到569行執行language函數。在language函數裏面。先經過get_instance()函數得到超級對象,也就是控制器基類CI_Controller,直接調用核心類Lang對象成員(前面的打印結果中能夠到,語言核心類做爲基礎類已經被加載完畢)的load方法加載,傳入數組就掃描加載它們,那就進入到CI_Lang類這種看看這個load方法的大體流程。

  傳送到system/core/Lang.php裏邊,66行:首先若是有.php擴展名就去掉,而後給文件名加上_lang後綴,因此寫語言腳本時在命名時就要加上_lang,而加載時傳入參數你也沒必要寫好比eng_lang,它會給你加,有也會去掉再加,因此徹底不必。而後鏈接上.php後綴,在CI_Lang核心類中有一個成員變量is_loaded會記錄下全部已經加載過的語言文件,因此接下來先檢查是否被加載過,若加載直接返回。而後得到config文件中的$config變量(Common.php中的get_config全局方法,加載application/config/config.php),而後根據$config['language']的值(默認是english)肯定$deft_lang和$idiom的值,先無論它們來幹什麼,接下來就是尋找language file並加載它們。若是傳入第二個參數$idiom,它將在好比application/language/$idiom這個目錄下去尋找語言文件,因此這個參數決定了你將加載什麼類型的文件,固然你不穿默認就是$config['language']的值。

    

   注意到CI_Lang類的load方法的最後一個參數$alt_path表示一個可選的路徑,因此接下來會以它來拼一個路徑尋找語言文件(file_exists($alt_path.'language/'.$idiom.'/'.$langfile),$alt_path爲空,$idom默認爲english),這裏不存在這個路徑文件,走else,調用Loader類的get_package_paths方法返回一個路徑數組,這個數組默認是array(APPPATH,BASEPATH),轉了一圈仍是先找application/language/下的語言文件,再找system/language/下的,因此若是自定義語言文件,大能夠放在前者目錄下就好了,能夠少找一步。一旦找到了就加載並break退出循環,若是須要返回變量(122行)則返回$lang (這是定義語言文件時所用),不然將記錄已加載過得語言文件(127),將新加入的$lang變量merge到Lang類的language成員中。

  Lang還有個方法line,這個就是返回$lang這個數組中的某一個元素對應的值,好比傳入'lovely',它將返回$lang['lovely']的值。

  綜上,語言文件的加載就是在找application/language和system/language兩個目錄,定義文件時好比如今定義一個漢語的放在遷移個目錄下,能夠這樣:如今application/language下新建一個目錄chinese,我如今翻譯湖北話,文件名定義爲hubei_lang.php,裏邊能夠是$lang['have a meal'] = '七飯';,將該文件放在application/language/chinese目錄下,而後在控制器某方法中,載入語言文件$this->lang->load('hubei', 'chinese'); echo $this->lang->line('have a meal');。一個湖北方言版的網頁就此誕生-_-#。這就是CI所謂的「國際化」,使用不一樣的語言呈現同一頁面。

  這就是Loader中的language方法,對語言文件加載和使用的簡單流程。

  回到Loader核心類,1152行到1158行,就是加載輔助函數和語言腳本。

  1162行:檢查$autoload中是否有core爲鍵名的元素,若是有,將它賦給libraries爲鍵名的元素,這大概是CI之前將工具庫的類的鍵名稱爲core,後來棄用,改成libraries。

  1168行:開始加載library,先檢查$autoload['libraries']是否有值database,即自動加載數據庫,這裏就調用了Loader類的database方法,而後將database元素從$autoload['libraries']去除。數據庫加載單說。

  1178行:開始加載全部須要自動加載的library。開始調用Loader的library方法。

  195行:library函數的定義。第一個參數是待加載的庫的名稱,第2、三個參數默認爲null。庫名參數能夠是單個string或者包含多個元素的數組(多維也行)。若是是數組,則要加載多個,這裏使用了遞歸,若是是數組掃描數組繼續調用該方法。以單個string庫名爲例。若是第一個$library參數爲空或者已經存在於Loader的數據成員_base_classes(它記錄着Loader類經過load_class加載過的類,因此library實際上和core文件夾中的核心類在這兒都歸爲了基礎類)中,則直接返回false。第二個參數默認空,走217行,調用成員方法_ci_load_class執行加載。

  880行:_ci_load_class函數定義。

  首先,去.php擴展名。而後解析子目錄,從890行到898行。關於子目錄,就是容許在傳遞給library方法的類文件名時能夠包含路徑,例如如今A公司針對XML開發了幾個類,針對Mail也開發了有,B公司也作了這些,它們各自使用方式稍有差異,各有好壞,把它們均放在application/libraries下面時,咱們就會先建一個A目錄,裏邊放A的各個類,再建個B目錄,再放B的各個類,所以能夠在加載時這樣$this->load->library('A/Xml_1');,這裏的A就是子目錄。890行到898行作的工做就是提取子目錄賦給$subdir變量,提取類名賦給$class變量。

  從901行的foreach開始就開始加載了。首先將你傳入的名稱轉爲全小寫或首字母大寫,來查找指定目錄下的文件,因此你的library文件名稱要麼全小寫或首字母大寫,最好跟CI原有方式保持一致。先是造成一個子類文件路徑:$subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php';,這個子類是在application/libraries下邊的,之因此叫子類是由於它是CI本身的library類的擴展,即CI容許咱們隊原有的工具庫進行擴展繼承,添加咱們本身想要的內容。若是這個子類文件存在的話,造成一個基類文件路徑$baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php';,基類就沒有什麼子目錄了,CI的基類不容許你這麼幹,擴展或改變library類只能放在application/libraries下面,人家是這麼規定的。接下來檢測基類出現的可能性,若是不存在就報錯,子類有父類沒有固然錯了。若是存在就要來一次安全檢查:若是這個文件之前加載過,library方法傳入了第三個參數$object_name,這個對象名若是不爲空,先調get_instance獲取超級對象控制器基類,而且$object_name不是超級對象的成員,就調用Loader類另外一個方法_ci_init_class來實例化一次這個類$class(實例化後會加入到控制器基類的屬性成員),而後就return了。若是這個文件之前沒有加載過,鮮藕加載父類文件和子類文件,並將該文件路徑加入到Loader類的一個屬性成員中,以記錄這個路徑的文件被加載過一次(因此前面有檢查這個文件是否之前加載過)。

    

  子類、父類文件均加載成功,接下來就調_ci_init_class實例化該類,返回。

  944行:注意前面是子類存在的流程,若是不存在呢,剛開始下載一個CI程序,容許默認方法固然沒有子類。固然也不會有子目錄,$subdir就爲空。

  掃描Loader類屬性成員_ci_library_paths(默認array(APPPATH,BASEPATH))來造成文件路徑加載library類,這裏的文件路徑就正常多了$filepath = $path.'libraries/'.$subdir.$class.'.php';,$subdir默認空,這還是在找application/libraries和system/libraries裏邊的文件。若是這個文件之前被加載過則又一次進行安全檢查,而後繼續在include_once這個文件而且實例化該類,返回。

  982行:這是一個比較人性化的處理,它假設你在傳參時少傳了文件名,子目錄不存在,就給深刻一層再加載一次(遞歸),返回。就是加入傳入$this->load->library('Xml');,加載的是system/libraries/xml/Xml.php,大體是這樣。

  若是還能往下走,那就不正常了,報錯,996行結束。

  回到_ci_autoloader函數,即將加載的是Model模型類,這也是_ci_autoloader自動加載的最後一種類型,它調用的也是model函數,就像咱們在控制器類中加載Model同樣:$this->load->model('info_model')。

====================================================================================

  既然是加載Model,前面又打算把database數據庫的加載單說,那我索性就最日常的,一個普通的控制器類,裏邊加載一個Model,在Model裏邊加載數據庫,並讀取數據返回,再加載view視圖,這個過程何不就此回憶、梳理一下~

  一個簡單的自定義控制器類

     

  對應的Model類

     

  設置好數據庫選項,一遍鏈接數據庫

     

  視圖

     

  控制器類的加載前面已經說過,就從Model類的加載提及。須要注意的一點是,在咱們本身定義的,不管是Controller或Model類時,若是有構造函數須要強制(最好是)調用下父類的構造函數(parent::__construct()),前面說過CI_Controller的構造函數都要作些重要的準備工做,若是沒有執行很可能出問題(好像不是可能而是必定會),好比CI_Controller會將核心類所有設置爲控制器的屬性成員,所以咱們才能夠$this->load->....這樣去調用。

  上面的自定義控制類中,最早加載的是staff_model模型類,回到system/core/Loader.php中看看model方法。

  從大概223行起就是model方法的定義,第一個參數$model就是即將加載的Model類的文件名,它可能包含子路徑(在application/models下面的子目錄,視圖多了咱們可能會把它們分類存放),第二個參數$name容許爲傳入的類取一個別名,默認爲空則表示按照CI默認對Model類的命名規則來,即設置爲Controller類的成員後將名稱所有轉爲小寫,第三個參數$db_conn肯定要不要在加載Model時鏈接數據庫,默認爲false則不鏈接,若是爲true則會鏈接默認的數據庫配置選項所指向的數據庫,這個默認的數據庫配置選項就是application/config/database.php中的default數組元素(下圖),而上面自定義是咱們指定其餘的數據庫鏈接信息。

     

  初看到這個model的定義是否是很眼熟,是的,在前面說library的加載時也是這樣的。首先檢查$model是否是數組,如果數組遞歸調用該方法,$model爲空直接return,250-258行就是解析出子目錄(放在application/models的子文件夾下)和只包含類名的文件名,這根library基本同樣,$path是子目錄,$model是文件名。若是未指定$name則與$model同樣。若是這個類已存在於CI_Loader類的成員變量_ci_models中,則直接返回,這個_ci_models是個數組,Loader類每加載一個Model類,均會在_ci_models中有相應的記錄,固然這只是針對一次獨立的訪問而言,就像靜態變量同樣。

  接下來270行調用超級方法,看看它是否跟CI_Controller中已存在的核心類重名,重名報錯。接着會將類名轉爲全小寫,從278行開始掃描成員變量_ci_model_paths加載Model類文件。

  在Loader的構造函數中_ci_model_paths被初始化爲array(APPPATH),意味着是在application下面去尋找的。首先檢查$mod_path.'models/'.$path.$model.'.php'(APPPATH.'models/'.$path.$model.'php')是否有該文件,有的話檢查$db_conn參數真假,爲真則要鏈接數據庫了,調用Loader的database方法,這個仍是下面單說。再次檢查CI_Model類的定義是否存在,走到這裏就是基類CI_Model定義也在,自定義Model類文件也在,因而require_once正式加載這個Model文件。

  而後類名變量轉爲首字母大寫,實例化並做爲控制器的屬性成員,注意這個屬性成員的變量名是$name,意味着model方法的第二個參數$name爲空時,咱們在如$this->a_model調用時a_model名字必定是全小寫的,固然在實例化時類名是首字母大寫的($CI->$name = new $model();),而後將這個類加入到已加載的類名稱的集合數組中($this->_ci_models[] = $name;),加載完了直接return。若是跳到foreach循環外邊去了,只有一個結果:error。

  model方法就是以上。

  回到自定義的控制器類staff_info中,調用staff_model類的getStaffsInfo方法,就是對數據庫的簡單查詢,生成結果返回,看看這個$this->load->database...方法對數據庫的加載如何執行。

  定位到Loader.php文件大約324行,database方法的定義。

  先調get_instance方法獲取超級對象,而後檢查CI_DB這個類是否出現過(聯合一些條件)以判斷是否須要加載數據庫類,符合if條件則直接return。而後require文件BASEPATH.'database/DB.php',若是傳入的第二個參數$return爲真的話就返回一個由調用DB構造函數的構造的對象(直接return DB(...),還不是return new DB(...)),因此這裏的DB是個方法,舉的例子中第二個參數爲TRUE,因此返回一個對象。若是$return爲假的話,就實例化某個對象,而且做爲核心控制器類對象的成員了,正是這樣咱們才能夠$this->db->***這樣用。這個數據庫對象的實例化就根這個BASEPATH.'database/DB.php'腳本里的方法相關了。

  打開system/database/DB.php,只有一個DB方法。

  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  * Initialize the database
 21  *
 22  * @category    Database
 23  * @author        EllisLab Dev Team
 24  * @link        http://codeigniter.com/user_guide/database/
 25  * @param     string
 26  * @param     bool    Determines if active record should be used or not
 27  */
 28 function &DB($params = '', $active_record_override = NULL)
 29 {
 30     // Load the DB config file if a DSN string wasn't passed
 31     if (is_string($params) AND strpos($params, '://') === FALSE)
 32     {
 33         // Is the config file in the environment folder?
 34         if ( ! defined('ENVIRONMENT') OR ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/database.php'))
 35         {
 36             if ( ! file_exists($file_path = APPPATH.'config/database.php'))
 37             {
 38                 show_error('The configuration file database.php does not exist.');
 39             }
 40         }
 41 
 42         include($file_path);
 43 
 44         if ( ! isset($db) OR count($db) == 0)
 45         {
 46             show_error('No database connection settings were found in the database config file.');
 47         }
 48 
 49         if ($params != '')
 50         {
 51             $active_group = $params;
 52         }
 53 
 54         if ( ! isset($active_group) OR ! isset($db[$active_group]))
 55         {
 56             show_error('You have specified an invalid database connection group.');
 57         }
 58 
 59         $params = $db[$active_group];
 60     }
 61     elseif (is_string($params))
 62     {
 63 
 64         /* parse the URL from the DSN string
 65          *  Database settings can be passed as discreet
 66          *  parameters or as a data source name in the first
 67          *  parameter. DSNs must have this prototype:
 68          *  $dsn = 'driver://username:password@hostname/database';
 69          */
 70 
 71         if (($dns = @parse_url($params)) === FALSE)
 72         {
 73             show_error('Invalid DB Connection String');
 74         }
 75 
 76         $params = array(
 77                             'dbdriver'    => $dns['scheme'],
 78                             'hostname'    => (isset($dns['host'])) ? rawurldecode($dns['host']) : '',
 79                             'username'    => (isset($dns['user'])) ? rawurldecode($dns['user']) : '',
 80                             'password'    => (isset($dns['pass'])) ? rawurldecode($dns['pass']) : '',
 81                             'database'    => (isset($dns['path'])) ? rawurldecode(substr($dns['path'], 1)) : ''
 82                         );
 83 
 84         // were additional config items set?
 85         if (isset($dns['query']))
 86         {
 87             parse_str($dns['query'], $extra);
 88 
 89             foreach ($extra as $key => $val)
 90             {
 91                 // booleans please
 92                 if (strtoupper($val) == "TRUE")
 93                 {
 94                     $val = TRUE;
 95                 }
 96                 elseif (strtoupper($val) == "FALSE")
 97                 {
 98                     $val = FALSE;
 99                 }
100 
101                 $params[$key] = $val;
102             }
103         }
104     }
105 
106     // No DB specified yet?  Beat them senseless...
107     if ( ! isset($params['dbdriver']) OR $params['dbdriver'] == '')
108     {
109         show_error('You have not selected a database type to connect to.');
110     }
111 
112     // Load the DB classes.  Note: Since the active record class is optional
113     // we need to dynamically create a class that extends proper parent class
114     // based on whether we're using the active record class or not.
115     // Kudos to Paul for discovering this clever use of eval()
116 
117     if ($active_record_override !== NULL)
118     {
119         $active_record = $active_record_override;
120     }
121 
122     require_once(BASEPATH.'database/DB_driver.php');
123 
124     if ( ! isset($active_record) OR $active_record == TRUE)
125     {
126         require_once(BASEPATH.'database/DB_active_rec.php');
127 
128         if ( ! class_exists('CI_DB'))
129         {
130             eval('class CI_DB extends CI_DB_active_record { }');
131         }
132     }
133     else
134     {
135         if ( ! class_exists('CI_DB'))
136         {
137             eval('class CI_DB extends CI_DB_driver { }');
138         }
139     }
140 
141     require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php');
142 
143     // Instantiate the DB adapter
144     $driver = 'CI_DB_'.$params['dbdriver'].'_driver';
145     $DB = new $driver($params);
146 
147     if ($DB->autoinit == TRUE)
148     {
149         $DB->initialize();
150     }
151 
152     if (isset($params['stricton']) && $params['stricton'] == TRUE)
153     {
154         $DB->query('SET SESSION sql_mode="STRICT_ALL_TABLES"');
155     }
156 
157     return $DB;
158 }
159 
160 
161 
162 /* End of file DB.php */
163 /* Location: ./system/database/DB.php */
View Code

  大約跟着DB方法走一遍,首先判斷傳入的第一個參數是字符串且其中沒有'//'這樣的字符,而後判斷雖然ENVIRONMENT這個常量定義了,但默認狀況下APPPATH.'config/'.ENVIRONMENT.'/database.php'這個路徑的文件是不存在的。因而不出意外的話會加載APPPATH.'config/database.php'這個腳本,這是數據庫配置信息的腳本,也就是前面舉的例子中配置數據庫鏈接選項所在的文件。檢驗$db變量是否存在,database.php這個配置文件中就是定義的$db這個數組。而後判斷傳入database方法的第一個參數不爲空的話賦給$active_group(該變量定義在數據庫配置文件中)。而後判斷database.php腳本是否確實定義了$db[$active_group]這個元素,所謂的$active_group就是咱們配置好的,能夠用來鏈接須要的數據庫的一套變量,規定爲數組形式,整個數組的鍵名是$active_group,例如上邊的'test',我鏈接的是test數組。最後將咱們的配置信息數組賦給$params參數。

  若是第一個if不成立,進入elseif,前提是傳入的第一個參數是字符串,接着使用parse_url方法解析這個字符串,一看就知道parse_url專門解析相似於http://www.****的url,,爲什麼在這兒解析傳入的參數?看到註釋裏說DSN的時候就明白了,第一個參數還能夠傳入DSN鏈接字符串,這個能夠看看PDO驅動的構造函數傳入參數狀況,很像,也可看看手冊中parse_url解析後都返回些啥。

  在這裏,解析完後,CI獲得的是scheme、host、user、pass、path這些選項,因而這個DSN字符串多是這樣的:mysql://root:password@localhost/test,重點是咱們要經過這個字符串解析後獲得dbdriver(驅動類型)、hostname(主機名)、username(用戶名)、password(密碼多少)、database(連的哪一個數據庫),測試下

              

  右邊是結果,注意path選項在CI中去掉了/。這樣也獲得了想要的信息,而不是經過加載數據庫配置文件信息獲得的,效果同樣,不用也無所謂,反正配置文件信息更清楚嘛。

  再來看這個elseif,首先parse_url解析$params,結果爲false直接報錯。而後解析相應選項,而後檢索解析出的數組$dsn中是否有查詢字符串($dsn['query']),由於是用解析url的方法,URL中固然能夠有以get形式存在的查詢字符串,在這裏再使用解析查詢字符串的方法parse_str,將查詢字符串轉化爲數組形式,是bool型的轉爲字符串值"TRUE"和"FALSE",其餘照舊。而後檢查dbdriver選項的有效性,要使用哪一種驅動?mysql?mssql?odbc?在這裏就決定了,不經過的話固然就show_error了。

  接下來從大約112行起,就是加載數據庫類了。看看英文註釋:由於active record類是可選的($active_record_override這個參數是非必傳),咱們須要動態的擴展一個合理的父類,基因而否用到active record類,團隊中的人發現了eval這個巧妙的方法。

  先檢查$active_record_override這個參數(通常是不傳這個參數的),存在的話將賦給$active_record。若是稍微用過CI的話,這個名字應該不陌生,在CI的數據庫中有個玩意兒叫Active  Record類(或者叫模式),CI就是靠它來最終對各個對應的數據庫類裏邊的方法調用從而進行讀寫操做的。

  加載system/database/DB_driver.php腳本(require_once(BASEPATH.'database/DB_driver.php')),這是一個總的驅動類文件,看看它的簡略註釋:這是一個基於平臺的數據庫基類,這個類不會被直接調用,固然,它是做爲一個具體數據庫類的適配器,由這個具體的數據庫類來擴展和實例化它。看到這兒也還不怎麼清楚它要幹啥。

  接着一個if-else語句,若是$active_record未定義或者爲真,就加載system/database/DB_active_rec.php腳本,而後不出意外的話會運行這行 :eval('class CI_DB extends CI_DB_active_record { }')。eval這個函數的妙處就是直接把字符串當作php代碼來執行,看它會發生什麼直接去掉參數兩邊的引號就行,就是這樣:

  class CI_DB extends CI_DB_active_record 

  {  }

  這是定義了一個CI_DB類,它繼承自CI_DB_active_record,就是剛加載的文件,而CI_DB_active_record是繼承自CI_DB_driver(DB_driver.php中定義)的,再看看system/database下面還有其餘的文件,定義了其餘的類。

   而後會加載system/database/drivers/mysql/mysql_driver.php(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver.php')。

  接着實例化mysql驅動類,$driver = 'CI_DB_'.$params['dbdriver'].'_driver';  $DB = new $driver($params);

   147行是執行一個初始化方法,這個autoinit屬性和initialize方法在CI_DB_driver類中(繼承了幾個類,得找一下在哪一個裏邊~),作的主要是鏈接數據庫。

  OK,這裏有幾個類顯得有點亂,以最經常使用的mysql爲例,箭頭所指爲子類(UML懶得畫了):

      

  在system/database目錄下面,能夠看到幾個文件DB_active_rec.php、DB_driver.php、DB_forge.php、DB_result.php、DB_utility.php,分別對應上圖上靠上面的類,其中一個CI_DB類只是個空殼,過渡一下。咱們最經常使用的就是最左邊那條線下來的,而咱們用到的mysql的驅動方法分佈在這幾個繼承的類和它自己中(如query定義是在DB_driver.php中),固然也多是子類override了父類的方法,我沒所有看過。在database文件夾下邊有個drivers子文件夾,裏邊放的就是各類數據庫驅動對應的類文件,其中的mysql子目錄就是上圖中最下面的一排類。

  這些全部的驅動都從system/database/DB.php中的DB方法開始加載和構造,好比例子中的查詢,調用Loader類中的database方法後,加載DB.php,加載數據庫配置文件,而後依次按照繼承順序加載DB_driver.php、DB_active_rec.php、mysql_driver.php。

=====================================================================================

  回到最初舉的例子的staff_model中,load玩數據庫後,我執行了query方法,它的定義在DB_driver.php中大約251行,接着調用了query方法,你會發現mysql_driver等幾個腳本中均沒有這個方法,足夠耐心的話,你能夠看一下這個query方法。在query方法中調用了一個load_rdriver方法

  估計你能看出來它加載了CI_DB_result類和CI_DB_mysql_result類,這是對父子類,最後query返回的就是這個CI_DB_mysql_result類,即query生成的結果其實是一個單獨的result類在處理,result_array(定義在DB_result.php中),free_result兩個類都有,CI_DB_mysql_result中是真正的釋放操做。釋放結後就返回結果集,數據庫類的調用結束。

  對於其餘類型數據庫驅動,基本跟這同樣。

  在staff_model中獲取完數據後,返回staff_info控制器類中,調用Loader核心類的view方法加載、展示視圖。回到Loader.php文件,大約418行,第一個參數是需加載視圖的文件名稱(可包含子目錄),第二個參數是傳遞給視圖腳本的變量數組,第三個參數默認爲false,傳遞true的話,CI將不會加載視圖(因此看不到),而是將視圖生成字符串內容返回。而後調用Loader類的_ci_load方法。在傳遞給_ci_load參數時,將前三個參數整合到了一個數組中,視圖文件與鍵_ci_view對應,要使用的變量與鍵_ci_vars對應(如果對象將轉爲數組),是否只返回視圖與鍵_ci_return對應。

  Loader.php大約742行,則是一個protected訪問屬性的方法,只能在繼承類中使用,還要注意一點帶有下劃線的前綴的方法,即使是公有訪問屬性的,也不能經過對象調用,這是CI的規則。一步步來看_ci_load方法。

  首先是一個foreach掃描,將對應的鍵和值,定義爲變量的名和值。而後是if檢查$_ci_path這個變量是否有值,CI的view方法裏傳入_ci_load參數時是沒有以_ci_path爲鍵的元素的,所以走else。$_ci_ext變量獲取傳入視圖文件名的擴展名,不寫擴展名的話這裏就是空串了,$_ci_file獲取這個視圖文件名,若是你傳入視圖時沒有寫.php後綴,我上面也沒寫,這裏會給加上該擴展,因此徹底沒必要加擴展名。

  接下來是else中的一個foreach掃描,所作的工做就是尋找須要加載的視圖,並肯定其存在性。能夠看到它掃描的是CI_Loader類的_ci_view_paths變量成員,這個成員在初始化是:array(APPPATH.'views/' => TRUE),約莫能夠知道它是到application目錄裏邊去找了。經過對這個數組的循環,$_ci_path = $view_file.$_ci_file;拼接一個視圖的相對路徑出來,肯定存在則break跳出循環。若是不存在的話就走到779行的if報錯了。

  接着從787-794行,獲取超級對象CI_Controller實例,掃描它全部的屬性成員變量,把它們所有增長爲CI_Loader類的屬性成員,因此,即使是在view視圖中,咱們仍然能以$this->load->的形式調用控制器類所擁有的一切成員變量,固然也包括CI_Loader類自身的成員變量和成員方法,這是個很是方便的舉措。

  805-809行:當視圖不是隻由一個簡單的腳本組成時,好比爲了擴展性,前端可能會把一個頁面分紅head(頭部)、中間實體、foot(尾部)三部分,頭部和尾部基本相同,變化的是中間實體頁面,這時就要保證在中間實體中加載的變量,在尾部中也能使用,這幾行代碼實現的就是這個效果。將咱們傳入的一些變量$_ci_vars,與Loader核心類的屬性成員_ci_cached_vars進行合併,合併後的數組使用extract方法將鍵值提取出來,做爲定義在當前腳本中的變量名和值,好比頭部中有一個$title變量,它已經假如到_ci_cached_vars屬性成員中了,當咱們再 $this->load->view('body', array(‘var’ => 'val')) 時,這個var鍵對應的值又會合併到_ci_cached_vars中,再所有定義一遍,反正重複定義沒什麼影響。這樣在中間實體部分仍然可使用那個在頭部中就加載了的$title變量,也可以使用後來新加載進來的$var變量。

  大約822行到方法結束,就是打開緩衝,加載視圖腳本,而後有條件的清空或輸出緩衝到頁面了。

  822行:打開緩存。

  828-835行:檢查服務器的配置短標記(短標記就是容許php腳本容許以<?開始,而不是最通用的<?php)是否開啓,配置文件短標記選項($config['rewrite_short_tags'])是否容許,符合條件的話,將視圖內容中的<?=(短標記時這個直接輸出)替換爲<?php echo ,正則將諸如 ;空格...空格?> 替換爲 ;空格; (只有一個空格)的形式,默認是不走if條件的,走else就加載該腳本,呈現視圖頁面了,在這兒就是真正對視圖腳本進行加載。

  840行:若是當初傳入$this->load->view方法的第三個參數爲TRUE的話,就要走這個if語句,就是將整個頁面內容返回,ob_get_contents獲取這個文本,ob_end_clean清除緩存內容。注意:上面的加載並不能當即呈現頁面,由於已經開啓了緩存,除非腳本已經結束或輸出緩衝。

  857-結束:輸出/清空緩存,展現頁面。若是由於緩衝緩衝區是能夠嵌套的,因此有個嵌套層次一說,最外面是1(未打開緩衝時),越嵌入裏面層次值越深。若是當前緩衝區層次比Loader核心類的嵌套層次加1還大,就輸出緩衝區,否寫就附加到Output核心類的一個屬性成員上(調用CI_Output類的append_output方法),這就是當有多個腳本共同組成一個頁面時,把頁面內容進行銜接,而後清除緩衝區。

  固然,若是隻是ob_start開啓緩存了,沒有手動關閉,到腳本結束時也會給你關閉掉,頁面仍是能展現出來滴。

  ~哎,就到這兒,得結合代碼看,不然就一團糟。果真圖表就簡單明瞭。

相關文章
相關標籤/搜索