zoukankan      html  css  js  c++  java
  • php优秀框架codeigniter学习系列——CodeIgniter.php概览

      CodeIgniter.php是CI框架的核心文件。它在前端控制器index.php之后运行,加载各类基础组件,执行请求。文件执行完成后,这次请求也就结束了。所以,该文只能对CodeIgniter.php做一个大致的讲解,中间如果遇到重要部分,会新写一篇日志单独详细讲解。

      CI框架的注释非常的详细和规范。官方对这个文件的解释就是 System Initialization File(系统初始化文件),加载基础类库和执行请求。它不同于index.php只是设置环境和定义重要路径,而是要深入框架的核心了。

      让我们一起来学习这个文件吧。

    1.

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

    第一行代码,如果没有定义‘BASEPATH’就退出,而该变量是在index.php文件中定义的。所以用意很明显,防止该文件被直接访问。CI框架的很多文件中都可以看到这行代码。

    2.  

    1 const CI_VERSION = '3.1.7';

    定义CI框架的版本,而且是全局变量,每个框架都有类似的定义吧,为什么不使用define定义而是使用静态变量呢,不太清楚

    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学习

    这里有一个小技巧,很多框架在定义一个常量前会用defined去检测是否已定义,对于引用文件则会用file_exists,对于类是否定义则会用class_exists。防止重复定义。

    4.

    1 /*
    2  * ------------------------------------------------------
    3  *  Load the global functions
    4  * ------------------------------------------------------
    5  */
    6     require_once(BASEPATH.'core/Common.php');
    View Code

    引用常用函数文件。这个文件中的函数都是框架中经常用到的函数,也是核心的一些函数。参考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');
    View Code

      自定义错误处理程序,异常处理程序,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);
        }
    View Code

     这一段代码就是判断究竟是否输出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();
    View Code

    接下来,就是实例化应用控制类啦。 实例化前还有个 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');
        }
    }
    View Code

     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');
    View Code

    接下来,是调用应用控制器类的方法了。也就是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');
    View Code

    post_controller 是一个hook,可以定义在整个控制器执行完后的附加处理函数。
    display_override 是一个hook,可以设置自定义的输出处理函数,如果没有设置,则执行默认的输出函数。

     在退出整个执行程序之前,也会执行 hook, post_system 。

    结尾 

    CodeIgniter.php 文件的结束,即标志着整个 php 程序执行的结束。回顾整个过程,我们可以清晰的看到作者在框架中处理整个请求的思路,如下:

    从图中可以看出,写一个php框架,需要考虑到非常多的方面。 更重要的是,要扩展不少的 方法类库, 以方便开发者使用和进行各种扩展开发。有时间,我再分析一下炙手可热的Laravel框架,可以有一个对比,对框架有更好的理解。

     

  • 相关阅读:
    正则表达式详解
    js前端性能优化之函数节流和函数防抖
    Vue 给axios做个靠谱的封装(报错,鉴权,跳转,拦截,提示)
    你所误解的微信公众号开发、以及微信公众号网页授权、接收url跳转参数等问题
    JavaScript 复杂判断的更优雅写法
    秒懂 this(带你撸平this)
    Vue.js 3.0 新特性预览
    完美平滑实现一个“回到顶部”
    从插入排序到希尔排序
    LWIP协议中tcp_seg结构相关指针的个人理解
  • 原文地址:https://www.cnblogs.com/Andres/p/8988900.html
Copyright © 2011-2022 走看看