無聊,決定水一把。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 */
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 */
在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 }
註釋說它加載主要的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 }
先看它的註釋:這個方法做爲一個單例,若是被請求的類沒有出現過,則該類會被實例化爲一個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 */
看它的構造函數,先循環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 */
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 */
大約跟着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開啓緩存了,沒有手動關閉,到腳本結束時也會給你關閉掉,頁面仍是能展現出來滴。
~哎,就到這兒,得結合代碼看,不然就一團糟。果真圖表就簡單明瞭。