CodeIgniter源码阅读笔记(2)——框架核心CodeIgniter.php

1.概述

CodeIgniter.php是整个框架的核心文件,这个文件的官方注释是

/**
 * System Initialization File
 * 系统初始化文件
 * Loads the base classes and executes the request.
 * 加载基础类并执行请求
 */

CodeIgniter除了加载系统核心类,还要将客户端的请求传递到对应的控制器中,并且要将控制器返回的内容响应给客户端。

2. 阅读源码

BASEPATH常量是在index.php中定义的,这里判断BASEPATH是否存在是防止用户绕过入口直接返回文件

defined('BASEPATH') OR exit('No direct script access allowed');

版本号

const CI_VERSION = '3.1.3';

加载框架内预定义常量

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');
}

加载公共类——公共类中封装了很多常用功能函数

require_once(BASEPATH.'core/Common.php');

进行系统安全检测

//php版本若小于5.4
if ( ! is_php('5.4'))
{
    //magic_quotes_runtime设置为关闭
    //magic_quotes_runtime含义:运行时从外部资源产生的数据是否使用自动字符串转义
    ini_set('magic_quotes_runtime', 0);
    //register_globals配置是否打开,因为register_globals存在安全隐患,如果此配置打开则将删除预定义全局变量
    if ((bool) ini_get('register_globals'))
    {
        $_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');

assign_to_config是在Index文件中定义的变量,如果这个变量不为空,则把数组内容赋值到配置参数中去,从这里也可以看得出,assign_to_config的配置优先级是高于配置文件的

    if ( ! empty($assign_to_config['subclass_prefix']))
    {
        get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
    }

加载Benchmark组件:
Benchmark可以监测和跟踪性能,total_execution_time_start,loading_time:_base_classes_start标记的是当前时间,精确到毫秒

    $BM =& load_class('Benchmark', 'core');
    $BM->mark('total_execution_time_start');
    $BM->mark('loading_time:_base_classes_start');

加载Hooks组件:
Hooks组件可以在不改变系统代码的情况下增加扩展功能,扩展方式就是在系统某个位置放置挂钩点,例如:pre_system,就是放置在系统中的挂钩点

    $EXT =& load_class('Hooks', 'core');
    $EXT->call_hook('pre_system');

加载Config组件:
Config组件是对配置参数进行管理,如果assign_to_config变量不为空,则将其值添加到配置参数中

    $CFG =& load_class('Config', 'core');
    if (isset($assign_to_config) && is_array($assign_to_config))
    {
        foreach ($assign_to_config as $key => $value)
        {
            $CFG->set_item($key, $value);
        }
    }

设置字符串编码格式

    //创建一个变量保存配置文件中声明的字符串编码格式
    $charset = strtoupper(config_item('charset'));
    //将配置的编码格式设置为默认编码格式
    ini_set('default_charset', $charset);
    //如果mbstring扩展打开(mbstring是字符串编码和字符串处理库)
    if (extension_loaded('mbstring'))
    {
        //声明一个全局常量保存mbstring扩展状态
        define('MB_ENABLED', TRUE);
        //设置编码格式为$charset
        @ini_set('mbstring.internal_encoding', $charset);
        //若输出的字符不存在编码格式中,则用none替代
        mb_substitute_character('none');
    }
    else
    {
        //声明一个全局常量保存mbstring扩展状态
        define('MB_ENABLED', FALSE);
    }

    //如果iconv扩展打开(iconv是字符集之间转换的库)
    if (extension_loaded('iconv'))
    {
        //声明一个全局常量保存mbstring扩展状态
        define('ICONV_ENABLED', TRUE);
        //设置编码格式为$charset
        @ini_set('iconv.internal_encoding', $charset);
    }
    else
    {
        //声明一个全局常量保存mbstring扩展状态
        define('ICONV_ENABLED', FALSE);
    }
    //如果php版本高于5.6
    if (is_php('5.6'))
    {
        //设置编码格式为$charset
        ini_set('php.internal_encoding', $charset);
    }

引入mbstring,hash,password,standard兼容性处理的文件

    require_once(BASEPATH.'core/compat/mbstring.php');
    require_once(BASEPATH.'core/compat/hash.php');
    require_once(BASEPATH.'core/compat/password.php');
    require_once(BASEPATH.'core/compat/standard.php');

提供Utf8支持的组件

    $UNI =& load_class('Utf8', 'core');

解析URI参数的组件

    $URI =& load_class('URI', 'core');

路由组件,通过解析出来的URI参数,决定数据路由方向

    $RTR =& load_class('Router', 'core', isset($routing) ? $routing : NULL);

输出组件,通过output输出最终内容

$OUT =& load_class('Output', 'core');

cache_override是一个挂钩点,这个挂钩点可以自定义缓存策略,如果没有扩展这个钩子,查看输出组件是否有缓存,有缓存则直接输出

    if ($EXT->call_hook('cache_override') === FALSE && $
            OUT->_display_cache($CFG, $URI) === TRUE)
    {
        exit;
    }

Security:安全组件,可以防范xss,csrf等网络攻击
Input:输入组件,可通过input读取客户端输入,表单提交等数据
Lang:加载语言类型的组件

    $SEC =& load_class('Security', 'core');
    $IN    =& load_class('Input', 'core');
    $LANG =& load_class('Lang', 'core');

加载控制器基类(缓存没有命中才会执行到这一步)

    require_once BASEPATH.'core/Controller.php';
    //get_instance指针函数返回控制器单例指针
    function &get_instance()
    {
        return CI_Controller::get_instance();
    }
    //如果有自定义的控制器基类,一并加载进来
    if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
    {
        require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
    }
    //标记时间点
    $BM->mark('loading_time:_base_classes_end');

还记得前面加载的URI和RTR组件吗?下面这段代码就是通过URI解析的参数,按照RTR的路由规则,实例化对应的控制器来处理数据请求

    //404标记变量
    $e404 = FALSE;
    //路由类名
    $class = ucfirst($RTR->class);
    //路由方法名
    $method = $RTR->method;
    //如果类名为空或者找不到路由类名的文件,404
    if (empty($class) OR
     ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
    {
        $e404 = TRUE;
    }
    else
    {
        //加载路由类名文件
        require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
        //类如果不存在,路由方法为私有函数,方法名在CI_Controller中已存在,404
        if ( ! class_exists($class, FALSE) OR 
            $method[0] === '_' OR method_exists('CI_Controller', $method))
        {
            $e404 = TRUE;
        }
        //控制器中存在_remap映射方法,将参数和方法名传给_remap映射方法中
        elseif (method_exists($class, '_remap'))
        {
            $params = array($method, array_slice($URI->rsegments, 2));
            $method = '_remap';
        }
        //方法名不存在,404
        elseif ( ! method_exists($class, $method))
        {
            $e404 = TRUE;
        }
        //当前作用域是否可以调用路由方法
        elseif ( ! is_callable(array($class, $method)))
        {
            $reflection = new ReflectionMethod($class, $method);
            //不是公开方法或是构造方法,404
            if ( ! $reflection->isPublic() OR $reflection->isConstructor())
            {
                $e404 = TRUE;
            }
        }
    }
    //路由路径不存在,展示404
    if ($e404)
    {
        //如果有配置404页面路由参数
        if ( ! empty($RTR->routes['404_override']))
        {
            //解析路由配置文件定义的404_override(config/route这个配置文件中如果配置
            //404_override参数的话,需要按照%[^/]/%s的字符串格式进行配置才能被正确解
            //析:如Err_code/code404则会找到Err_code类,code404方法)
            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);
                }
                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;
            }
        }
        //找到自定义的404页面
        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);
    }

挂钩点pre_controller

$EXT->call_hook('pre_controller');

标记当前时间,实例化路由类

    $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');
    $CI = new $class();

挂钩点post_controller_constructor

    $EXT->call_hook('post_controller_constructor');

调用路由方法

call_user_func_array(array(&$CI, $method), $params);

输出最终数据

    //标记当前时间
    $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');
    //挂钩点post_controller
    $EXT->call_hook('post_controller');
    //display_override是否有钩子,没有,输出最终内容
    if ($EXT->call_hook('display_override') === FALSE)
    {
        $OUT->_display();
    }
    //挂钩点post_system
    $EXT->call_hook('post_system');

到这里CodeIgniter.php的源码就阅读完了,回顾整个代码流程,有没有发现跟官方的流程图有点相似:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值