CodeIgniter.php是CI框架的核心文件。它在前端控制器index.php以后运行,加载各种基础组件,执行请求。文件执行完成后,此次请求也就结束了。因此,该文只能对CodeIgniter.php作一个大体的讲解,中间若是遇到重要部分,会新写一篇日志单独详细讲解。php
CI框架的注释很是的详细和规范。官方对这个文件的解释就是 System Initialization File(系统初始化文件),加载基础类库和执行请求。它不一样于index.php只是设置环境和定义重要路径,而是要深刻框架的核心了。html
让咱们一块儿来学习这个文件吧。前端
1.数组
defined('BASEPATH') OR exit('No direct script access allowed');
第一行代码,若是没有定义‘BASEPATH’就退出,而该变量是在index.php文件中定义的。因此用意很明显,防止该文件被直接访问。CI框架的不少文件中均可以看到这行代码。浏览器
2. 缓存
1 const CI_VERSION = '3.1.7';
定义CI框架的版本,并且是全局变量,每一个框架都有相似的定义吧,为何不使用define定义而是使用静态变量呢,不太清楚。php框架
3.安全
/* * ------------------------------------------------------ * Load the framework constants * ------------------------------------------------------ */ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php')) { require_once(APPPATH.'config/'.ENVIRONMENT.'/constants.php'); } if (file_exists(APPPATH.'config/constants.php')) { require_once(APPPATH.'config/constants.php'); }
加载框架的常量。若是环境变量(product,development,test)文件夹存在,且文件夹中有constants.php文件,先加载。并且优先级也比后加载的高。constants.php文件定义的是一些不会去作改变的(用define定义)系统级的常量,好比文件可读写模式,退出状态码等。具体能够参考constants.php学习。app
这里有一个小技巧,不少框架在定义一个常量前会用defined去检测是否已定义,对于引用文件则会用file_exists,对于类是否认义则会用class_exists。防止重复定义。composer
4.
1 /* 2 * ------------------------------------------------------ 3 * Load the global functions 4 * ------------------------------------------------------ 5 */ 6 require_once(BASEPATH.'core/Common.php');
引用经常使用函数文件。这个文件中的函数都是框架中常常用到的函数,也是核心的一些函数。参考common.php学习。
5.Security procedures
对于版本小于5.4的php,将全局变量$GLOBALS数组中的一些变量置为null,说明是出于安全因素。可是我没有找到这个安全因素的起源。
6. 设置自定义错误处理程序
1 set_error_handler('_error_handler'); 2 set_exception_handler('_exception_handler'); 3 register_shutdown_function('_shutdown_handler');
自定义错误处理程序,异常处理程序,php停止执行处理程序。关于CI的错误和异常处理机制,请看。
7.加载config.php
config.php在application/config/文件夹中定义。定义了一些系统和应用程序的设置,包括使用何种编码,是否采用csrf,自定义的类的类名前缀等等。在这里还能够用在index.php中定义的$assign_to_config数组去覆盖这些定义的参数。
8.use a Composer autoloader
若是定义了composer_autoload参数,加载这个文件。
9.timer
加载一个计时器。
10.hooks
hooks容许你在框架运行的特定节点,好比系统运行前,CI_Controller调用前,系统运行结束后等特定的时间节点,执行自定义的函数。官方说法:钩子特性提供了一种方法来修改框架的内部运做流程,而无需修改 核心文件。我写了一篇学习文章。
11.Important charset-related stuff
读取cofig.php中设置的字符集。判断是否加载了多字节字符串处理的扩展mbstring和字符集转换的扩展iconv,而且设置这两个扩展的默认字符集。最后设置php的默认字符集。
12.加载兼容性的函数
因为php版本的缘由或者没有开启某个模块,一些函数不能使用。在这里做者对于不能使用的函数,本身进行了补充,放在system/core/compat/文件夹中。在这里统一加载进框架中。
13.加载CI_Utf8类
这个类主要对Utf8编码环境提供支持。看来做者是提倡在CI中使用utf8编码的。参见CI_Utf8类学习。
14.加载URI类
这个类主要用来解析uri和决定路由的。关于URI和URL的关系请参考这位朋友的文章。简单来讲URI是惟必定位的资源,URL是惟一资源的一个可能访问路径。更多该类的分析,参见CI_URI类学习。
15.加载Router类
这个类用来经过URI解析出来 的URI找到访问的文件。参见CI_Router类学习。
16.加载Output类
这个类用主要用来生成返回的页面给浏览器。参见CI_Output类学习。
17.调用缓存
1 if ($EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE) 2 { 3 exit; 4 }
cache_override 使用你本身的方法来替代 输出类 中的 _display_cache() 方法,这让你有本身的缓存显示机制。若是不存在,则调用Output类中的_display_cache()方法,该方法的详细说明,参见CI_Output类学习。
18.加载安全类Security
$SEC =& load_class('Security', 'core');
如注释所述,该类用来为防护xss和csrf攻击提供支持。参见CI_Security类学习。
18.加载输入类Input
1 $IN =& load_class('Input', 'core');
如注释所说,加载输入类,该类用来提早处理全局变量,以保证安全。参见CI_Input类学习。
18.加载语言类Lang
1 /* 2 * ------------------------------------------------------ 3 * Load the Language class 4 * ------------------------------------------------------ 5 */ 6 $LANG =& load_class('Lang', 'core');
如注释所说,该类提供相关的函数,用于检索语言文件和文本行,以便国际化。参见CI_Lang类学习。
19.require_once()应用控制类CI_Controller
1 // Load the base controller class 2 require_once BASEPATH.'core/Controller.php'; 3 4 /** 5 * Reference to the CI_Controller method. 6 * 7 * Returns current CI instance object 8 * 9 * @return CI_Controller 10 */ 11 function &get_instance() 12 { 13 return CI_Controller::get_instance(); 14 } 15 16 if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php')) 17 { 18 require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'; 19 }
CI_Controller是全部应用控制器的父类,因此在这里须要先导入,以便在下面的路由导入应用控制器的时候,不会由于找不到文件,而报错。对该文件的具体分析见:CI_Controller分析。
20. 合理性检查
/* * ------------------------------------------------------ * Sanity checks * ------------------------------------------------------ * * The Router class has already validated the request, * leaving us with 3 options here: * * 1) an empty class name, if we reached the default * controller, but it didn't exist; * 2) a query string which doesn't go through a * file_exists() check * 3) a regular request for a non-existing page * * We handle all of these as a 404 error. * * Furthermore, none of the methods in the app controller * or the loader class can be called via the URI, nor can * controller methods that begin with an underscore. */ $e404 = FALSE; $class = ucfirst($RTR->class); $method = $RTR->method; if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php')) { $e404 = TRUE; } else { require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php'); if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method)) { $e404 = TRUE; } elseif (method_exists($class, '_remap')) { $params = array($method, array_slice($URI->rsegments, 2)); $method = '_remap'; } elseif ( ! method_exists($class, $method)) { $e404 = TRUE; } /** * DO NOT CHANGE THIS, NOTHING ELSE WORKS! * * - method_exists() returns true for non-public methods, which passes the previous elseif//对于私有方法同样会返回true * - is_callable() returns false for PHP 4-style constructors, even if there's a __construct() * - method_exists($class, '__construct') won't work because CI_Controller::__construct() is inherited * - People will only complain if this doesn't work, even though it is documented that it shouldn't. * * ReflectionMethod::isConstructor() is the ONLY reliable check, * knowing which method will be executed as a constructor. */ elseif ( ! is_callable(array($class, $method))) { $reflection = new ReflectionMethod($class, $method); if ( ! $reflection->isPublic() OR $reflection->isConstructor()) { $e404 = TRUE; } } } if ($e404) { if ( ! empty($RTR->routes['404_override'])) { if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2) { $error_method = 'index'; } $error_class = ucfirst($error_class); if ( ! class_exists($error_class, FALSE)) { if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php')) { require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'); $e404 = ! class_exists($error_class, FALSE); } // Were we in a directory? If so, check for a global override elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php')) { require_once(APPPATH.'controllers/'.$error_class.'.php'); if (($e404 = ! class_exists($error_class, FALSE)) === FALSE) { $RTR->directory = ''; } } } else { $e404 = FALSE; } } // Did we reset the $e404 flag? If so, set the rsegments, starting from index 1 if ( ! $e404) { $class = $error_class; $method = $error_method; $URI->rsegments = array( 1 => $class, 2 => $method ); } else { show_404($RTR->directory.$class.'/'.$method); } } if ($method !== '_remap') { $params = array_slice($URI->rsegments, 2); }
这一段代码就是判断到底是否输出404页面。固然啦,是否输出有不少缘由,也就是说,在这里有不少判断。
经过前面加载CI_Router类,已经把要访问访问类和方法名都提取出来了。若是访问的文件找不到,或者类不存在,或者方法不存在,都会设置 $e404 = TRUE 。若是访问的方法不存在,且存在‘_remap’方法,会将要访问的方法改为‘——remap’。
在这里,还会经过反射的机制,检测下方法是不是公开可访问的,不能到时候是私有方法,就尴尬了。反射 ReflectionMethod 的用法可参考[网文]。
- 须要输出404。若是是自定义了404页面,会require自定义的404页面的controller。若是没有自定义404页面,或者自定义的404页面控制器找不到。就调用 show_404 进行默认的404处理。整个请求就完成了。
- 页面找到了,不须要输出404页面。则下一步操做。
20. 实例化应用控制类
/* * ------------------------------------------------------ * 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();
接下来,就是实例化应用控制类啦。 实例化前还有个 hook ,看看你还有什么提早的操做。 在这一步,会执行控制类中的构造函数__construct(不必定有)。一个典型的应用控制类应该是长这个样子:
<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Welcome extends CI_Controller { /** * Index Page for this controller. * * Maps to the following URL * http://example.com/index.php/welcome * - or - * http://example.com/index.php/welcome/index * - or - * Since this controller is set as the default controller in * config/routes.php, it's displayed at http://example.com/ * * So any other public methods not prefixed with an underscore will * map to /index.php/welcome/<method_name> * @see https://codeigniter.com/user_guide/general/urls.html */ public function index() { $this->load->view('welcome_message'); } }
21. 调用应用控制类中的方法
/* * ------------------------------------------------------ * Is there a "post_controller_constructor" hook? * ------------------------------------------------------ */ $EXT->call_hook('post_controller_constructor'); /* * ------------------------------------------------------ * Call the requested method * ------------------------------------------------------ */ call_user_func_array(array(&$CI, $method), $params); // Mark a benchmark end point $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
接下来,是调用应用控制器类的方法了。也就是URI中请求执行的方法。在这以前,会有一个hook, 用户能够设置在执行了应用控制器构造函数以后,进行一些操做。
22. 收尾
/* * ------------------------------------------------------ * 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');
post_controller 是一个hook,能够定义在整个控制器执行完后的附加处理函数。
display_override 是一个hook,能够设置自定义的输出处理函数,若是没有设置,则执行默认的输出函数。
在退出整个执行程序以前,也会执行 hook, post_system 。
CodeIgniter.php 文件的结束,即标志着整个 php 程序执行的结束。回顾整个过程,咱们能够清晰的看到做者在框架中处理整个请求的思路,以下:
从图中能够看出,写一个php框架,须要考虑到很是多的方面。 更重要的是,要扩展很多的 方法类库, 以方便开发者使用和进行各类扩展开发。有时间,我再分析一下煊赫一时的Laravel框架,能够有一个对比,对框架有更好的理解。