• CI加载流程小结


      无聊,决定水一把。

      CI(CodeIgniter)是我最早接触的一个框架,到现在也只是用了其中一点零碎的方法。一直想对其流程做个小结,却总是因各种各样的“理由”挨着。看见别人图表齐上阵,没那耐心,就从代码说起吧,权当做个笔记,纪念一下。

      看在线的用户手册,也知道,将CI下载下来(最新版本2.2.1),解压到机子上,比如www目录,可改个根目录名(原名CodeIgniter-2.2-stable太长),初步目录文件如下,当然这在是windows下面。

         

          访问下,如localhost/ci/index.php,就进入CI默认的Welcome页面

      

      如何一步步加载这个页面的?首先访问的是index.php脚本

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

      21行:首先定义一个ENVIRONMENT常量为development,即开发环境。

      31-47行:switch语句,由于当前环境是development,所以是设置报告所有级别的错误。

      49-59行:$system_path变量定义CI的默认的系统脚本目录是 system,61-75行定义当前默认的供我们主要开发用的目录为 application。

      77-105行:全部注释掉了,这里是我们可以强制设置系统加载时默认的目录名($routing['directory'])、控制器名($routing['directory'])和方法名($routing['directory']),虽然一般这些是设置在applicationconfig outes.php中(下图),访问的Welcome页面也是通过这个默认控制器Welcome类进行的,这里只是作为一个选择性的方式,其实没必要弄

         

      108-129行:全部注释掉,用于自定义配置变量(CUSTOM CONFIG VALUES),前一篇说过,任何后端project中,总有些配置信息,只是各个项目或框架加载方式不同,这个$assign_to_config数组就存放我们的自定义配置信息,如$assign_to_config['home'] = 'localhost'; ,之所以注释掉,又是因为这只是一个可选的操作,CI的用户自定义配置信息,一般放在applicationconfig目录下边,以自动加载信息(autoload.php),普通配置信息(config.php)、常量(constants.php)、数据库(database.php)等分开文件存储,所以一般不会在这里的去配置一个要用到的变量,$assign_to_config默认是没有定义的。

         

       从131行到index.php文件末尾主要是对一些路径变量的定义。

      137-141行:是为CLI(Command-Interface Line)的调用方式准备的,是直接在Mac/Linux系统上通过终端命令运行脚本,这个在CI中文官网(http://codeigniter.org.cn/user_guide/general/cli.html)也有介绍,如果定义了名为STDIN的常量,则将执行目录改为当前文件所在目录,当然前面没有出现过STDIN这个常量的定义,这里就不会执行了。

         

      143-155行:确定框架存放系统脚本的目录变量$system_path,也就是前面图中的system目录,这里会检测它的有效性,无效的话程序就挂在这里了。

      157-192行:定义若干主要目录常量,分别是SELF:当前脚本的文件名、EXT:脚本扩展名、BASEPATH:system目录的路径、FCPATH:当前脚本所在的目录、SYSDIR:system目录的目录名,不改动的话就是system。

      179-194行:定义APPPATH常量,确定application所在的目录,就是以后我们主要开发的地方,使用is_dir检测,稍微注意的是is_dir可以检测相对目录,所以实际运行的是if里边的代码,APPPATH得到的是相对路径。

      最后打印看看这些变(常)量的值都是啥,有的与存放目录相关:

      

      202行:加载BASEPATH.'core/CodeIgniter.php'脚本,就是system目录下的核心类文件目录下的文件,进入到CI的核心类目录下的文件了。

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

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

      在CodeIgniter中,可以看到开头的英文描述,该脚本时系统初始化文件,主要作用是装载基类和执行请求。

      31-45行:定义了CI_VERSION常量,描述当前框架版本,CI_CORE常量,目前我也不清楚没探究过,注释是CI的分支,啥意思?

      52行:加载系统核心目录下的Common.php文件,Load the global functions,记得前一篇中说到,一般一个项目会将很多公共方法放在一个脚本中加载进来,通常取名Utilities.php,也可是Common.php,这里的Common.php也是这个意思,如它的解释是“加载全局函数”,即这里的函数都是后边直接拿来用的。在这个脚本中有两个重要的方法(目前来说)一个是get_config,单独拿出来如下

     1 <?php
     2 /**
     3 * Loads the main config.php file
     4 *
     5 * This function lets us grab the config file even if the Config class
     6 * hasn't been instantiated yet
     7 *
     8 * @access    private
     9 * @return    array
    10 */
    11 if ( ! function_exists('get_config'))
    12 {
    13     function &get_config($replace = array())
    14     {
    15         static $_config;
    16 
    17         if (isset($_config))
    18         {
    19             return $_config[0];
    20         }
    21 
    22         // Is the config file in the environment folder?
    23         if ( ! defined('ENVIRONMENT') OR ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
    24         {
    25             $file_path = APPPATH.'config/config.php';
    26         }
    27 
    28         // Fetch the config file
    29         if ( ! file_exists($file_path))
    30         {
    31             exit('The configuration file does not exist.');
    32         }
    33 
    34         require($file_path);
    35 
    36         // Does the $config array exist in the file?
    37         if ( ! isset($config) OR ! is_array($config))
    38         {
    39             exit('Your config file does not appear to be formatted correctly.');
    40         }
    41 
    42         // Are any values being dynamically replaced?
    43         if (count($replace) > 0)
    44         {
    45             foreach ($replace as $key => $val)
    46             {
    47                 if (isset($config[$key]))
    48                 {
    49                     $config[$key] = $val;
    50                 }
    51             }
    52         }
    53 
    54         $_config[0] =& $config;
    55         return $_config[0];
    56     }
    57 }
    View Code

      注释说它加载主要的config.php文件,它使得我们能抓取到配置文件,即便配置类还未被实例化。在CI中,有专门的核心配置类CI_Config来加载配置信息,而这里的get_config方法也能获得主要配置信息,注意是主要配置信息,在application/config目录下有很多其他的配置信息文件(前面在自定义配置变量时也说过CI将配置信息分为了很多文件),其中有一个config.php文件就是get_config能获取到的,这个文件存放的就是基本信息,如果你还想获取其他的配置信息,貌似就要用配置类了。所以如果想添加节本配置信息就在这个里边。

      如果是第一次调用get_config方法,先声明静态变量$_config,如果已定义则直接返回它的索引为0的子数组。然后查看APPPATH/config/ENVIRONMENT/config.php文件是否存在(前面打印已知ENVIRONMENT常量值,未改动就是development,原始的框架中没有这个目录,所以这里加载的是application/config/config.php(只加载了这一个,其他的配置文件没有),可以打开看看config.php中定义了一个$config数组,一些基本定义如基础链接、链接后缀、编码、语言、缓存、日志、钩子等等。如果传入一个关联数组,它会将键-值(临时)加入$_config中。总之,get_config方法主要得到的是config.php中定义的数组变量。

      与get_config相关的config_item方法则是得到这个数组变量中的某一项。

      另一个比较重要的方法是load_class:

     1 <?php
     2 /**
     3 * Class registry
     4 *
     5 * This function acts as a singleton.  If the requested class does not
     6 * exist it is instantiated and set to a static variable.  If it has
     7 * previously been instantiated the variable is returned.
     8 *
     9 * @access    public
    10 * @param    string    the class name being requested
    11 * @param    string    the directory where the class should be found
    12 * @param    string    the class name prefix
    13 * @return    object
    14 */
    15 if ( ! function_exists('load_class'))
    16 {
    17     function &load_class($class, $directory = 'libraries', $prefix = 'CI_')
    18     {
    19         static $_classes = array();
    20 
    21         // Does the class exist?  If so, we're done...
    22         if (isset($_classes[$class]))
    23         {
    24             return $_classes[$class];
    25         }
    26 
    27         $name = FALSE;
    28 
    29         // Look for the class first in the local application/libraries folder
    30         // then in the native system/libraries folder
    31         foreach (array(APPPATH, BASEPATH) as $path)
    32         {
    33             if (file_exists($path.$directory.'/'.$class.'.php'))
    34             {
    35                 $name = $prefix.$class;
    36 
    37                 if (class_exists($name) === FALSE)
    38                 {
    39                     require($path.$directory.'/'.$class.'.php');
    40                 }
    41 
    42                 break;
    43             }
    44         }
    45 
    46         // Is the request a class extension?  If so we load it too
    47         if (file_exists(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'))
    48         {
    49             $name = config_item('subclass_prefix').$class;
    50 
    51             if (class_exists($name) === FALSE)
    52             {
    53                 require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php');
    54             }
    55         }
    56 
    57         // Did we find the class?
    58         if ($name === FALSE)
    59         {
    60             // Note: We use exit() rather then show_error() in order to avoid a
    61             // self-referencing loop with the Excptions class
    62             exit('Unable to locate the specified class: '.$class.'.php');
    63         }
    64 
    65         // Keep track of what we just loaded
    66         is_loaded($class);
    67 
    68         $_classes[$class] = new $name();
    69         return $_classes[$class];
    70     }
    71 }
    View Code

      先看它的注释:这个方法作为一个单例,如果被请求的类没有出现过,则该类会被实例化为一个static variable,如果先前被实例化过则直接返回它。它的三个参数分别是请求的类名、所在目录,类名前缀。可以看到,目录默认是libraries,在application和system中均有它,它就是存放我们自定义的类库或者CI自带的类库的地方,就是自定义工具和CI提供的工具,如日历类、加密类、Ftp类、日志类、Session会话类、Email邮件收发类、JavaScript类、ZIP压缩类等等。或许你已经注意到这里返回的是引用而非值,就像它将加载的类作为静态变量一样,这些细节地方最终提高了整个系统的访问速度。

      大致流程:先定义一个静态数组,若数组中已有该类直接返回。先后扫描APPPATH和BASEPATH(前面已知这俩常量值)文件夹下的$directory(默认值是libraries)目录下的$class.php文件是否存在,存在则加上CI的标准类前缀CI_(第三个参数的默认值),在检查类存在与否,存在则require该文件(class_exists()先判断类是否存,存在时就不加载该类文件),一旦文件出现则加载它,并break跳出。注意扫描顺序,先APPPATH后BASEPATH,假如只传第一个参数类名,则优先在我们自己开发的application目录libraries中寻找,然后才去system目录的libraries下边。

      由于我们可以对CI的核心类进行扩展(继承它们),所以在扫描完APPPATH和BASEPATH的核心类(名称以CI_为前缀)目录后,还要扫描APPPATH的libraries下边是否有自定义的扩展类(默认以MY_为前缀),有的话也要加载它们,然后实例化一个对应对象(有扩展类是扩展类)存入$_classes静态数组并返回该对象。

      对Common.php有大致了解后回到CodeIgniter.php脚本。

      54-66行:加载APPPATH.'config/constants.php'脚本,constants.php如同名字一样放的是framework constants,集中定义了一些常量,所以我们在添加常量时就可以放到这里边来定义。

        

      68-78行:首先定义了一个自定义错误处理方法_exception_handler。判断php版本,非5.3关闭magic_quotes引用,这个配置在5.3版本已弃用,提高安全性。

      80-99行:这里就是将前面说过的$assign_to_config自定义配置信息数组临时加到$_config数组中,通过get_config方法实现,前面说过$assign_to_config默认是没有定义的,这里的if语句也不会运行。

      101-109行:设置自定义脚本最大执行时间为300秒(略长,跑日志的话得更长)

      111-118行:加载核心类Benchmark,设置两个标记点。Benchmark基准测试类,就是测试某个开始标记到结束标记之间占用的内存大小、执行时间等信息,测试嘛,当然它要结合CI中一个叫分析器的东西使用。

      120-132行:加载核心类Hooks,钩子,设置了一个系统开始执行的钩子(实际未执行,因为application/config/config.php关于它的配置信息默认设置为false,即不启用钩子)。它就就相当于一个触发器,在某个东西要执行前开始执行某些代码,比如控制器加载前、加载后等,一旦控制器加载就运行指定的代码。在这里,它尝试调用一个pre_system(系统执行前)的扩展,默认不执行。

      134-145行:加载核心类Config,配置类,它用来加载其他需需要的配置信息,并且它再次加载$assign_to_config数组中配置信息如果该数组定义了的话。

      147-159行:加载核心类Utf8,编码类。

      161-166行:加载核心类URI,路由。

      168-180行:加载核心类Router,路径处理类,_set_routing方法设置好访问路径。如果路径配置数组$routing(前面提到默认是注释掉的)定义了的话,将覆盖默认的路由配置。如果你输入了不存在的脚本路径,在这一步就停住,开始报404了,当然还得Router里边的方法处理。

      Router类里面,URI作为它的一个成员存在,实际处理方法在URI类中,熟悉点的都知道CI的访问方式默认是段(segment)的形式,据说更有利于搜索引擎。一个简单的访问方式是这样的localhost/ci/index.php/Controller/Function/Arguments,它们将访问的形式解析为需要的控制器,调用的方法,以及提供的参数列表,当然也可启用传统的查询字符串形式。具体方法略复杂。

      187行:加载核心类Output。

      189-200行:通过Hooks类和Output类检测有无缓存,有的话直接输出缓存页面,跳出脚本了。这也是在CI的介绍中应用程序流程图部分,当路径处理完后,若有缓存直接输出的原因。

       

      207行:加载核心类Security。

      214行:加载核心类Input。

      221行:加载核心类Lang,语言处理。

      229-235行:加载核心类Controller,它是所有控制器的基类,而get_instance全局方法也能得到它的实例,Controller的牛逼之处在于,它将前面所有通过load_calss载入的libraries(默认)目录(APPPATH和BASEPATH)中的工具库全部实例化为对象,并作为它的属性成员。所以这里的get_instance方法得到的实例也被CI称为超级对象(super object),因为通过这个对象就可以获取所有通过前面加载的对象实例。

      238-242行:加载自定义的,对上一步的核心类CI_Controller的扩展类的文件,默认就是MY_Controller,当然前提是如果你扩展了的的话。

      243-251行:通过核心类Router的实例,提取当前访问的控制器所在的目录和类名,不存在则报错,存在则加载它,这里就加载了默认的welcome控制器文件。当然如果你自己定义了控制器类文件并访问,也是在这里被include进来的(通过Router类提取子目录$RTR->fetch_directory(),若存在,提取类名$RTR->fetch_class()来找),大概在246行的if语句块,就是检查这个类文件是否存在。

      252行:设置一个基准测试结束标记,标记加载基本核心类结束(这些测试默认不会执行)。

      256-292行:安全检查。先通过Router类取得类名和要执行的方法名,if条件检查3项内容。1. 上面的243-251行是找到了控制器对应的脚本,并且加载了它,但是假如这只是一个名字匹配的空脚本呢?里边什么都没写就不行了,于是要检查类的定义是否存在(class_exists),2. 以下划线_开头的方法名不能执行,直接报错,当然这是CI自己的的规则,也就是说无论你的类定义的以_开头的方法即使是公有访问属性也不行(除了一个_remap),3. 当类中的方法根控制器核心类中的方法同名时也不行。定义方法名时有个印象就行了。进入if中就很可能会404了。

      298行:Hooks类尝试调用一个pre_controller(控制器执行前)的扩展,默认没有。

      301-309行:基准测试类设置一个起点标记,目的在于测试控制器执行的时长(默认不显示测试信息),并且实例化前面加载的控制器类,默认的就是Welcome。

      315行:Hooks尝试执行post_controller_constructor(所调用的控制器类构造完成后)的扩展,默认没有。

      317-364行:开始调用指定的控制器类的指定方法(当然这里是默认控制器Welcome的默认方法index)。看看这个流程,首先一个if判断,如果你的控制器类中有方法_remap,只调用它了,所以前面说以下划线开头的方法除了_remap,这也是CI的一个类的方法的规则,有了这个重映射方法,只调它。默认的Welcome控制器中没有_remap方法,进入else,else中还有个if,再次判断,我们调用的方法是否在这个控制器类中,如果不在的话注定要404了,只是404的调用脚本稍有不同。假如我们得application/config/routes.php文件中的routes['404_override']选项不为空(它就是我们自定义的404错误页脚本路径),将去解析它指定的目录中类名与方法名,如果类定义不存在且文件(自定义404文件)也不存在,就直接调调show_404展示CI默认的404页,只是类定义不存在的话就加载该文件,删除原对象,再new一个新的404对象(348行),当然因类定义不存在,这里理论上是要报错的;假如routes['404_override']选项为空,那么直接启用show_404方法。这个show_404是公用方法,自然是在system/core目录下的Common.php脚本里定义的。

      如果我们调用的方法在这个控制器定义中,就要运行这行了:call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));,调用$CI实例的$method方法,参数就是后边的数组(URI核心类对象的成员rsegments,它被重新索引,从下标2开始是解析的所调用方法的各个参数),$CI就是我们得控制器类实例,$method是对应调用方法。至此,才真正的调用了一个控制器的方法(默认Welcome的index方法),而这还是最简单的情况>3<

      它然后就是进入Welcome控制器类调用index方法加载一个默认的页面了,就是开头的欢迎页。在index加载欢迎页($this->load->view(...))又加载了核心类

      CodeIgniter.php后面剩下的几行

      364行:设置一个基准测试标记点,控制器执行结束标记。

      378-381行:如果调用Hooks钩子在输出覆盖(display_override)的扩展失败的话,做最后到浏览器的信息输出(这个输出主要做一些写入缓存,整个方法执行、页面加载等的时间、内存等的统计,头信息的设置、日志的记录等等...)。调用默认方法的话实际上从这开始到CodeIgniter.php结束没执行。

      388行:尝试调用Hooks钩子扩展的,在系统执行结束时。

      390-398行:如果还有数据库类实例的,关闭掉它的连接。

      CodeIgniter.php结束。

      OK,来看看调用一个默认的Welcome控制器默认方法都间接加载了哪些文件

       

      可以看到有CI介绍的系统类清单全在里边。

      但是最后的Loader类好像没有在CodeIgniter中明确加载,确实,它是在实例化Welcome类时,调它的父类CI_Controller的构造函数里边通过load_class加载的。

      如果输入一个错误的链接访问如localhost/ci/index.php/welcome/func,404是当然的

      

      多加载了一个Exception类,这个小细节就体现了CI的原则,“组件的导入和函数的执行只有在被要求的时候才执行,而不是在全局范围”。所以CI还是很不错的一个框架。

      时间又被拉长了。。。

      日后再补下其他的,主要是数据库和缓存文件的加载麻烦点,其他的还行。  

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

      补充下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函数的定义。第一个参数是待加载的库的名称,第二、三个参数默认为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开启缓存了,没有手动关闭,到脚本结束时也会给你关闭掉,页面还是能展示出来滴。

      ~哎,就到这儿,得结合代码看,否则就一团糟。果然图表就简单明了。

  • 相关阅读:
    迪杰斯特拉算法简单分析
    华科机考:二叉排序树(改)
    华科机考:八进制
    华科机考:阶乘
    华科机考:找位置
    华科机考:回文字符串
    华科机考:a+b
    华科机考:N阶楼梯上楼
    华科机考:大整数排序
    iOS 适配iOS9
  • 原文地址:https://www.cnblogs.com/lazycat-cz/p/4315453.html
Copyright © 2020-2023  润新知