您的位置:首页 > 编程语言 > PHP开发

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

2016-07-08 10:42 766 查看
现在我们进入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()进行路由设置[u]。实际上在这里生成$RTR时已经完成路由解析。[/u]

    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 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);//默认情况下调用系统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类

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: