CodeIgniter框架源码笔记(3)——每次请求的总调度师傅:引导文件CodeIgniter.php

来源:互联网 发布:阿里云国际版 200m 编辑:程序博客网 时间:2024/05/21 07:49

现在我们进入CI框架最重要的一环,引导文件
defined('BASEPATH') OR exit('No direct script access allowed');
此行在以后的每个系统文件中都会出现,防止客户端不通过入口,直接访问这些文件

我理解的CI工作流程如下:

一、设置版本号
define('CI_VERSION', '3.0.4');

二、加载常量

if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php')){    require_once(APPPATH.'config/'.ENVIRONMENT.'/constants.php');}require_once(APPPATH.'config/constants.php');
根据定义的环境,加载对应的环境目录下的常量,如果与系统常量冲突,最终以系统常量为准,所以环境常量无法覆盖系统常量。
这样做主要为了快速设置特定环境下的特定常量。

三、加载全局函数库
require_once(BASEPATH.'core/Common.php');

四、如果低于php5.4版本,将进行全局变量安全处理
知识点:
1、PHP变量解析顺序:ini_get('variables_order'),同时也声明了接收哪种类型发送过来的变量;
当程序中使用了$_REQUEST接收变量,设置顺序EGPCS(Environment,GET,POST,Cookie,Server)就很重要,注意是从右向左覆盖。
 php配置文件给出了配置提示
//Default Value: "EGPCS";
//Development Value: "GPCS";
//Production Value: "GPCS";
2、PHP 5.4.0 废除了register_globals,magic_quotes以及安全模式。因此这一段是专门针对PHP5.4之前的版本的。
3、当开启了register_globals,这就意味着EGPCS中的变量可以直接用变量名访问,这些全局变量是存储在$GLOBALS数组中的,这是个隐患,虽然5.4及之后消除了,但考虑兼容以前,需要手工清除这些全局变量。那么挑选了最重要的需要特别保护的一些变量名,也就是$_protected数组的值。凡是EGPCS中涉及到变量名称在$_protected数组中的,一律清空。

$_protected = array('_SERVER','_GET','_POST','_FILES','_REQUEST',    '_SESSION','_ENV','_COOKIE','GLOBALS','HTTP_RAW_POST_DATA','system_path',    'application_folder','view_folder','_protected','_registered');    $_registered = ini_get('variables_order');foreach (array('E' => '_ENV', 'G' => '_GET', 'P' => '_POST', 'C' => '_COOKIE', 'S' => '_SERVER') as $key => $superglobal){    if (strpos($_registered, $key) === FALSE)    {        continue;    }    foreach (array_keys($$superglobal) as $var)    {        if (isset($GLOBALS[$var]) && ! in_array($var, $_protected, TRUE))        {            $globals[$var] = NULL;        }    }}    

五、自定义错误、异常和程序完成的函数
set_error_handler('_error_handler');set_exception_handler('_exception_handler');register_shutdown_function('_shutdown_handler');
知识点:
    1、设置错误处理:set_error_handler('_error_handler')。处理函数原型:function _error_handler($severity, $message, $filepath, $line)。程序本身原因或手工触发trigger_error("A custom error has been triggered");
    2、设置异常处理:set_exception_handler('_exception_handler')。处理函数原型:function _exception_handler($exception)。当用户抛出异常时触发throw new Exception('Exception occurred');
    3、千万不要被shutdown迷惑:register_shutdown_function('_shutdown_handler')
    可以这样理解调用条件:当页面被用户强制停止时、当程序代码运行超时时、当php代码执行完成时。


六、如果index.php有硬编码的话,重新设置子类前缀
七、加载composer(单独开篇)

八、基准时间记录
    $BM =& load_class('Benchmark', 'core');
    $BM->mark('total_execution_time_start');
    $BM->mark('loading_time:_base_classes_start');

九、加载核心类并实例化:这些都是核心类core里的文件
    钩子类 $EXT =& load_class('Hooks', 'core');  
    配置类:$CFG =& load_class('Config', 'core');  如果在index.php有手工设置的配置选项,也加载进来
    utf8类:$UNI =& load_class('Utf8', 'core');
    URL类:$URI =& load_class('URI', 'core');
    路由类:$RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL);//$routing变量index.php可设置
                   重点说明:Router在实例化时,构造函数调用$this->_set_routing()进行路由设置实际上在这里生成$RTR时已经完成路由解析。
    OUTPUT类:$OUT =& load_class('Output', 'core');
    安全类:$SEC =& load_class('Security', 'core');
    输入及过滤类:$IN    =& load_class('Input', 'core');
    语言类:$LANG =& load_class('Lang', 'core');

八、多字节字符支持常量设置
    根据php扩展启用情况设置了MB_ENABLED,ICONV_ENABLED两个常量
    多字节支持也是一个重要的话题,详见[KTOPIC]

九、重写系统组件的一些方法,包括: mbstring/ hash/password/ standard

十、缓存调用:
$EXT->call_hook('cache_override') === FALSE && $OUT->_display_cache($CFG, $URI) === TRUE
正常没有写cache_override这个构子方法,所以会去执行$OUT->_display_cache($CFG, $URI)。如果缓存命中则输出,并结束整个CI的单次生命周期。如果没有命中缓存,或没有启用缓存,那么将继续向下执行。
$OUT类是一个重要的核心类,负责了整个系统向浏览器终端呈现的内容输出,包括缓存的创建和过期删除
详细解析:[KTOPIC]

十一、加载Controller类:

    require_once BASEPATH.'core/Controller.php';
    function &get_instance();//创建实例化controller函数
    require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'; //引入自定义扩展controller

十二、路由判断

$e404 = FALSE;$class = ucfirst($RTR->class);$method = $RTR->method;
CI认为下面这几种情况认为是404,如果找不到就调用show_404()函数:
1) 请求的class不存在:! class_exists($class)
2) 请求私有方法:!$method[0] === '_'
3) 请求基类方法:method_exists('CI_Controller', $method)
4)请求的方法不存在:! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE)

如果你的控制器中包含一个名为 _remap() 的方法,那么不管你的 URI 中包含什么,它总会被忽略掉。这个方法会废除掉由 URI片段来决定哪个方法被调用的规则,允许你重新定义调用方法的规则(方法的路由规则)。这个会有什么用处呢?其实用处有两个:
1,改变URL,隐藏方法,比如你的应用中,原来的URL方法是:
http://xxx.com/mall/display_successful_message
现在想改变显示的方法名为:
http://xxx.com/mall/successful
但显示虽然是successful,但实际上是调用存在的display_successful_message
方法,这就要用到_remap方法了。
2 还可以借这个函数做简单的函数方法控制,比如:
public function _remap($method, $params = array()){   $user_type = $_SESSION['user_type'];   $access_control = $this->validate_access($user_type,$method);   if ($access_control){      $this->$method();   }   else{      $this->show_message();   }}

    
十三、404处理
这段主要看代码了:
if ($e404){if ( ! empty($RTR->routes['404_override']))//如果在application/config/routes.php配置文件中设置了$route['404_override'],就按设置加载404页面{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 overrideelseif ( ! 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 1if ( ! $e404){$class = $error_class;$method = $error_method;$URI->rsegments = array(1 => $class,2 => $method);}else{show_404($RTR->directory.$class.'/'.$method);//默认情况下调用系统show_404函数进行显示}}

十四、解析请求的类,并调用请求的方法

$CI = new $class();call_user_func_array(array(&$CI, $method), $params);
call_user_func_array 调用回调函数,并把一个数组参数作为回调函数的参数,call_user_func_array 函数和 call_user_func 很相似,只是 使 用了数组 的传递参数形式,让参数的结构更清晰。
十五、输出
if ($EXT->call_hook('display_override') === FALSE)
{
    $OUT->_display();
}

至此,终于可以松一口气了。接下来放一幅粒度较小的流程图,并在随后开始分析这个负责呈现输出的Output类



0 0
原创粉丝点击