At this point, we finally enter the core of the CI framework. Since it is a "guidance" file, it guides user requests, parameters, etc. so that user requests and data flows follow the correct route. For example, the user's request url:
http://you.host.com/usr/reg
After the boot file, it will actually be handed over to the reg method of the UsrController controller in Application for processing. What work does CodeIgniter.php do? Let's look at it step by step.
In a previous blog (CI Framework Source Code Reading Notes 2, All Entrance Index.php), we have seen that the Index.php file has already defined the framework’s ENVIRONMENT, application, system, etc. and security Check.
(1). Load predefined constants.
If an environment is defined and a predefined constant file for the environment exists, the environment's constant definition file will be loaded first, otherwise the constant definition file in the config directory will be loaded:
<span>if</span> (<span>defined</span>('ENVIRONMENT') AND <span>file_exists</span>(APPPATH.'config/'.ENVIRONMENT.'/constants.php'<span>)) { </span><span>require</span>(APPPATH.'config/'.ENVIRONMENT.'/constants.php'<span>); } </span><span>else</span><span>{ </span><span>require</span>(APPPATH.'config/constants.php'<span>); }</span>
The reason for this, which we have introduced before, is that you can quickly switch environments and corresponding parameters without changing the core code of the application.
(2). Set a custom error handling function.
Here is the _exception_handler function. For the definition and explanation of this function, see the previous blog (http://www.cnblogs.com/ohmygirl/p/CIRead-3.html). Quoting a sentence from the manual again, I would like to remind everyone that the following levels of errors cannot be handled by user-defined functions: <code><strong>E_ERROR</strong>
E_ERROR<strong>E_PARSE</strong>
, <code><strong>E_CORE_ERROR</strong>
E_PARSE<strong>E_CORE_WARNING</strong>
< /code>, <code><strong>E_COMPILE_ERROR</strong>
E_CORE_ERROR<strong>E_COMPILE_WARNING</strong>
, <strong>E_CORE_WARNING</strong>
, <code><strong>E_STRICT</strong>
E_COMPILE_ERROR
E_COMPILE_WARNING, and most <span>E_STRICT</span>
generated in the file where the set_error_handler()
<span>if</span> (<span>isset</span>(<span>$assign_to_config</span>['subclass_prefix']) AND <span>$assign_to_config</span>['subclass_prefix'] != ''<span>) { get_config(</span><span>array</span>('subclass_prefix' => <span>$assign_to_config</span>['subclass_prefix'<span>])); }</span>
(3). Check whether the core class is extended
Among them, $assign_to_config should be the configuration array defined in the entry file Index.php. Normally, the names of the core components of CI start with "CI_", and if the core components of CI are changed or expanded, then You should use a different subclass_prefix prefix such as MY_. In this case, you should specify the prefix name of your extension core through $assign_to_config['subclass_prefix'] to facilitate the Loader component of CI to load the class, or an error that the file cannot be found may occur. . In addition, the subclass_prefix configuration item is located in the APPPATH/Config/config.php configuration file by default. This code also tells us that the subclass_prefix in the index.php file has higher priority (that is, if both are set subclass_prefix, the configuration items in index.php will overwrite the configuration in the configuration file Config.php). <🎜> <🎜>At this point, the basic environment configuration initialization of the CI framework has been completed. Next, CodeIgniter will use a series of components to complete more requirements. <🎜> <🎜>2. Load core components<🎜> <🎜> Usually, different functions in the CI framework are completed by different components (for example, the Log component is mainly used to record logs, and the Input component is used to process user GET, POST and other data). This modular approach makes each The coupling between components is low, which also facilitates expansion. The main core components in CI are as follows: <🎜> BM EXT CFG UNI URI RTR OUT SEC
Among them:
BM: refers to BenchMark, which is the benchmark component of CI. It is mainly used to mark various time points and record memory usage and other parameters to facilitate performance testing and tracking.
EXT: The extension component of CI, which has been introduced before, is used to change or increase the core operating functions of the system without changing the CI core. Hook hooks allow you to add custom functions and tracking at various hook points (hook points) running in the system, such as pre_system, pre_controller, post_controller and other predefined hook points. All the following $EXT->_call_hook("xxx"); are programs that call specific hook points (if any).
CFG: Config configuration management component. Mainly used to load configuration files, obtain and set configuration items, etc.
UNI: is used to support UTF-8 character set processing. Other components, such as the INPUT component, need to be modified to support the component.
URI: Parse URI (Uniform Rescource Identifier) parameters, etc. This component is closely related to the RTR component. (It seems that URI and Router are good friends wherever they go).
RTR: routing component. Determine the data flow direction (routing) through parameter parsing of the URI component.
OUT: The final output management component is in charge of the final output of CI (customs).
SEC: 安全处理组件。毕竟安全问题永远是一个大问题。
以BM组件为例,核心组件的加载方式是:
<span>$BM</span> =& load_class('Benchmark', 'core');
调用了load_class函数获取core目录下的相应组件。(load_class的实现和具体介绍见之前的博客:CI框架源码阅读笔记3 全局函数Common.php)
各组件的功能和具体实现之后会有详细的分析, 这里我们只需要知道该组件的基本功能即可。
调用很简单,只有一句话:
<span>$RTR</span>->_set_routing();
调用Router组件的_set_routing()函数来设置路由,具体的实现细节,我们这里暂且不管(之后的部分会有详细介绍),我们只需要知道,通过_set_routing的处理,我们可以获得实际请求的Controller,URI的segment参数段等信息。
值得注意的是,CI允许在index.php中配置routing,且会覆盖默认的routing设置(如共享CI的安装目录的多个应用程序可能有不同的routing):
<span>if</span> (<span>isset</span>(<span>$routing</span><span>)) { </span><span>$RTR</span>->_set_overrides(<span>$routing</span><span>); }</span>
设置完路由之后,可以通过该组件的:fetch_diretory() , fetch_class(), fetch_method()等分别获取目录、类、和方法。
到了这一步,CI会先检查是否有cache_override这个钩子(默认情况下没有配置,也就是返回FALSE),如果没有注册,则调用_display_cache方法输出缓存(这种说法并不准确,准确来说应该是,如果有相应的缓存,则输出缓存且直接退出程序,否则返回FALSE,这里我们暂时不去思考实现细节):
<span>if</span> (<span>$EXT</span>->_call_hook('cache_override') === <span>FALSE</span><span>) { </span><span>if</span> (<span>$OUT</span>->_display_cache(<span>$CFG</span>, <span>$URI</span>) == <span>TRUE</span><span>) { </span><span>exit</span><span>; } }</span>
能够走到这里,说明之前的缓存是没有命中的(实际上,任何页面都是应该先走到这一步,然后才会有设置缓存,之后的访问检查缓存才会命中)。这一步会require Controller基类和扩展的Controller类(如果有的话)及实际的应用程序控制器类:
<span>require</span> BASEPATH.'core/Controller.php'<span>; </span><span>if</span> (<span>file_exists</span>(APPPATH.'core/'.<span>$CFG</span>->config['subclass_prefix'].'Controller.php'<span>)) { </span><span>require</span> APPPATH.'core/'.<span>$CFG</span>->config['subclass_prefix'].'Controller.php'<span>; } </span><span>if</span> ( ! <span>file_exists</span>(APPPATH.'controllers/'.<span>$RTR</span>->fetch_directory().<span>$RTR</span>->fetch_class().'.php'<span>)) { show_error(</span>'xxx'<span>); } </span><span>include</span>(APPPATH.'controllers/'.<span>$RTR</span>->fetch_directory().<span>$RTR</span>->fetch_class().'.php');
之前我们已经说过,在Router组件_set_routing之后,可以通过fetch_directory(), fetch_class(), fetch_method()等分别获取请求的文件目录、控制器和方法。
现在对请求的控制器和方法做验证,我们看一下CI的主要验证:
<span>if</span> ( ! <span>class_exists</span>(<span>$class</span><span>) OR </span><span>strncmp</span>(<span>$method</span>, '_', 1) == 0<span> OR </span><span>in_array</span>(<span>strtolower</span>(<span>$method</span>), <span>array_map</span>('strtolower', <span>get_class_methods</span>('CI_Controller'<span>))) )</span>
这里简单解释一下,CI认为不合法的情况有:
(1).请求的class不存在:! class_exists($class)
(2).请求的方法以_开头(被认为是私有的private的方法,之所以这么做事因为php并不是一开始就支持private,public的访问权限的):strncmp($method, '_', 1) == 0
(3).基类CI_Controller中的方法不能直接被访问:in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller'))
如果请求的条件满足上面3个中的任何一个,则被认为是不合法的请求(或者是无法定位的请求),因此会被CI定向到404页面(值得注意的是,如果设置了404_override,并且404_override的class存在,并不会直接调用show_404并退出,而是会像正常的访问一样,实例化:$CI = new $class();)
走到这里,CI的Controller总算是加载完了(累趴)。不过且慢,还有不少事情要做:
(1). 检查_remap。
_remap这个东西类似于CI的rewrite,可以将你的请求定位到其他的位置。这个方法是应该定义在你的应用程序控制器的:
<span>public</span> <span>function</span> _remap(<span>$method</span><span>){ </span><span>$this</span>-><span>index(); }</span>
现在,所有的请求都会被定位到改控制器的index()中去了。如果_remap不存在,则调用实际控制器的$method方法:
<span>call_user_func_array</span>(<span>array</span>(&<span>$CI</span>, <span>$method</span>), <span>array_slice</span>(<span>$URI</span>->rsegments, 2));
(2).最终输出
$this->load->view()之后,并不会直接输出,而是放在了缓存区。$Out->_display之后,才会设置缓存,并最终输出(详细参考Output.php和Loader.php)
(3)若有使用了数据库,还要关闭数据库连接:
<span>if</span> (<span>class_exists</span>('CI_DB') AND <span>isset</span>(<span>$CI</span>-><span>db)) { </span><span>$CI</span>->db-><span>close(); }</span>
注意,如果在Config/database.php中设置了开启pconnect,则建立的连接是长连接,这种长连接是不会被close关闭的。所以,请谨慎使用pconnect.
到现在,CI的核心流程总算是走完了(虽然还有很多细节的问题,但不管怎么说,大树的枝干已经有了,树叶的细节,可以慢慢添加)。在结束本文之前,我们来梳理一下CI的核心执行流程:
回顾之前我们引用的官方给出的流程图,是不是基本一致的:
Finally, paste the source code of the entire file:
if ( ! defined('BASEPATH')) exit('No direct script access allowed' ); /** * CodeIgniter Version */ define('CI_VERSION', '2.1.4'); /** * CodeIgniter Branch (Core = TRUE, Reactor = FALSE) */ define('CI_CORE', FALSE); require(BASEPATH.'core/Common.php'); /* If there is a corresponding environment configuration file, the constant definition file under the configuration is loaded first, otherwise the constant definition file under the config directory is loaded */ if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php')) { require(APPPATH.'config/'.ENVIRONMENT.'/constants.php'); } else { require(APPPATH.'config/constants.php'); } set_error_handler('_exception_handler'); if ( ! is_php('5.3')) { @set_magic_quotes_runtime(0); // Kill magic quotes } /* * Set the subclass_prefix */ if (isset($assign_to_config['subclass_prefix']) AND $assign_to_config['subclass_prefix'] != '') { get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix'])); } /* * Set a liberal script execution time limit */ //if(function_exists("set_time_limit") && @!ini_get("safe_mode")) if (function_exists("set_time_limit") == TRUE AND @ini_get("safe_mode") == 0) { @set_time_limit(300); } $BM =& load_class('Benchmark', 'core'); $BM->mark('total_execution_time_start'); $BM->mark('loading_time:_base_classes_start'); /* * Instantiate the hooks class */ $EXT =& load_class('Hooks', 'core'); /* * Is there a "pre_system" hook? */ $EXT->_call_hook('pre_system'); /* * Instantiate the config class */ $CFG =& load_class('Config', 'core'); // Do we have any manually set config items in the index.php file? if (isset($assign_to_config)) { $CFG->_assign_to_config($assign_to_config); } /* * Instantiate the UTF-8 class */ $UNI =& load_class('Utf8', 'core'); /* *------------------------------------------------ ----- * Instantiate the URI class *------------------------------------------------ ----- */ $URI =& load_class('URI', 'core'); /* * Instantiate the routing class and set the routing */ $RTR =& load_class('Router', 'core'); $RTR->_set_routing(); // Set any routing overrides that may exist in the main index file if (isset($routing)) { $RTR->_set_overrides($routing); } /* * Instantiate the output class */ $OUT =& load_class('Output', 'core'); /* * Is there a valid cache file? If so, we're done... */ if ($EXT->_call_hook('cache_override') === FALSE) { if ($OUT->_display_cache($CFG, $URI) == TRUE) { exit; } }/* * Load the security class for xss and csrf support */ $SEC =& load_class('Security', 'core'); /* * Load the Input class and sanitize globals */ $IN =& load_class('Input', 'core'); /* * Load the Language class */ $LANG =& load_class('Lang', 'core'); /* * Load the app controller and local controller */ // Load the base controller class require BASEPATH.'core/Controller.php'; function &get_instance() { return CI_Controller::get_instance(); } if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php')) { require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'; } if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php')) { show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.'); } include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'); $BM->mark('loading_time:_base_classes_end'); /* * Security check */ $class = $RTR->fetch_class(); $method = $RTR->fetch_method(); if ( ! class_exists($class) OR strncmp($method, '_', 1) == 0 OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller'))) ) { if ( ! empty($RTR->routes['404_override'])) { $x = explode('/', $RTR->routes['404_override']); $class = $x[0]; $method = (isset($x[1]) ? $x[1] : 'index'); if ( ! class_exists($class)) { if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) { show_404("{$class}/{$method}"); } include_once(APPPATH.'controllers/'.$class.'.php'); } } else { show_404("{$class}/{$method}"); } } /* * Is there a "pre_controller" hook? */ $EXT->_call_hook('pre_controller'); /* * Instantiate the requested controller */ // Mark a start point so we can benchmark the controller $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start'); $CI = new $class(); /* * Is there a "post_controller_constructor" hook? */ $EXT->_call_hook('post_controller_constructor'); /* * Call the requested method */ // Is there a "remap" function? If so, we call it instead if (method_exists($CI, '_remap')) { $CI->_remap($method, array_slice($URI->rsegments, 2)); }else { if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI)))) { if ( ! empty($RTR->routes['404_override'])) { $x = explode('/', $RTR->routes['404_override']); $class = $x[0]; $method = (isset($x[1]) ? $x[1] : 'index'); if ( ! class_exists($class)) { if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) { show_404("{$class}/{$method}"); } include_once(APPPATH.'controllers/'.$class.'.php'); unset($CI); $CI = new $class(); } } else { show_404("{$class}/{$method}"); } } call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2)); } // Mark a benchmark end point $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end'); /* * Is there a "post_controller" hook? */ $EXT->_call_hook('post_controller'); /* * Send the final rendered output to the browser */ if ($EXT->_call_hook('display_override') === FALSE) { $OUT->_display(); } /* * Is there a "post_system" hook? */ $EXT->_call_hook('post_system'); /* * Close the DB connection if one exists */ if (class_exists('CI_DB') AND isset($CI->db)) { $CI->db->close(); } /* End of file CodeIgniter.php */ View Code由于写作仓促,难免会有错误。欢迎随时指出,欢迎沟通交流。