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.  

 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.

 /*
* ------------------------------------------------------
* Load the global functions
* ------------------------------------------------------
*/
require_once(BASEPATH.'core/Common.php');

引用常用函数文件。这个文件中的函数都是框架中经常用到的函数,也是核心的一些函数。参考common.php学习

5.Security procedures

  对于版本小于5.4的php,将全局变量$GLOBALS数组中的一些变量置为null,说明是出于安全因素。但是我没有找到这个安全因素的起源。

6. 设置自定义错误处理程序

     set_error_handler('_error_handler');
set_exception_handler('_exception_handler');
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.调用缓存

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

  cache_override 使用你自己的方法来替代 输出类 中的 _display_cache() 方法,这让你有自己的缓存显示机制。如果不存在,则调用Output类中的_display_cache()方法,该方法的详细说明,参见CI_Output类学习

18.加载安全类Security

$SEC =& load_class('Security', 'core');

  如注释所述,该类用来为防御xss和csrf攻击提供支持。参见CI_Security类学习

18.加载输入类Input

 $IN    =& load_class('Input', 'core');

  如注释所说,加载输入类,该类用来提前处理全局变量,以保证安全。参见CI_Input类学习

18.加载语言类Lang

 /*
* ------------------------------------------------------
* Load the Language class
* ------------------------------------------------------
*/
$LANG =& load_class('Lang', 'core');

  如注释所说,该类提供相关的函数,用于检索语言文件和文本行,以便国际化。参见CI_Lang类学习

  

19.require_once()应用控制类CI_Controller

     // Load the base controller class
require_once BASEPATH.'core/Controller.php'; /**
* Reference to the CI_Controller method.
*
* Returns current CI instance object
*
* @return CI_Controller
*/
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';
}

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框架,可以有一个对比,对框架有更好的理解。

 

php优秀框架codeigniter学习系列——CodeIgniter.php概览的更多相关文章

  1. php优秀框架codeigniter学习系列——前言

    php的框架众多,笔者用过的包括thinkphp,CI,smarty,laravel,也用过一些公司自己开发的框架. thinkphp是国人自己开发的,我大概用过一段时间,基本功能都还好,应该也还比较 ...

  2. php优秀框架codeigniter学习系列——hooks

    这篇文章学习CI框架的钩子特性. hooks是CI框架提供的一种机制,允许你在程序框架运行流程的某个阶段执行你自己的一些代码.比如系统运行前,CI_Controller调用前,系统运行结束后等特定的时 ...

  3. php优秀框架codeigniter学习系列——CI_Security类学习

    这篇文章主要介绍CI核心框架工具类CI_Security. 安全类包含了一些方法,用于安全的处理输入数据,帮助你创建一个安全的应用.以下选取类中的重点方法进行说明. __construct() 在构造 ...

  4. php优秀框架codeigniter学习系列——CI_Router类学习

    这篇文章主要介绍CI核心框架工具类CI_Router. 如果说CI_URI类是用来解析URI,那么CI_Router类就应该是根据解析出来的URI来决定究竟访问哪一个文件和哪一个function. 详 ...

  5. php优秀框架codeigniter学习系列——common.php

    文件位于system/core/common.php,是框架核心文件. 该文件中定义了一系列的函数,都是框架运行中经常需要用到的.下面逐一介绍. is_php /** * Determines if ...

  6. php优秀框架codeigniter学习系列——CI_URI类学习

    这篇文章主要介绍CI核心框架工具类CI_URI. 该类主要用来解析uri和决定路由的.关于URI和URL的关系请参考这位朋友的文章.简单来说URI是唯一定位的资源,URL是唯一资源的一个网络可能访问路 ...

  7. php优秀框架codeigniter学习系列——异常和错误处理机制

    这篇介绍下CI框架的异常和错误处理机制. 在入口文件index.php中,根据设置的环境参数设置error_reporting的范围,和是否显示错误. 在CI初始化程序CodeIgniter.php中 ...

  8. php优秀框架codeigniter学习系列——index.php

    程序流程图 先来看看CI框架运行的程序流程图. 从图中我们 看到,index.php作为唯一的入口文件,会初始化CI框架运行所需的基本资源. 路由器(Routing)会根据http请求,确定如何处理: ...

  9. php优秀框架codeigniter学习系列——CI_Loader类分析

    这是一个加载视图和文件的类. __construct() 设置视图文件的路径,和获取输出缓冲级别. initialize() 该方法只会被CI_Controller调用一次,会调用 $this-> ...

随机推荐

  1. python+requests接口自动化测试框架实例详解教程

    1.首先,我们先来理一下思路. 正常的接口测试流程是什么? 脑海里的反应是不是这样的: 确定测试接口的工具 —> 配置需要的接口参数 —> 进行测试 —> 检查测试结果(有的需要数据 ...

  2. BZOJ 1833 数字计数 数位DP

    题目链接 做的第一道数位DP题,听说是最基础的模板题,但还是花了好长时间才写出来..... 想深入了解下数位DP的请点这里 先设dp数组dp[i][j][k]表示数位是i,以j开头的数k出现的次数 有 ...

  3. Spring boot @Autowired注解在非Controller中注入为null

    参考链接:https://blog.csdn.net/qq_35056292/article/details/78430777

  4. spring boot(十四)shiro登录认证与权限管理

    这篇文章我们来学习如何使用Spring Boot集成Apache Shiro.安全应该是互联网公司的一道生命线,几乎任何的公司都会涉及到这方面的需求.在Java领域一般有Spring Security ...

  5. Shiro中Realm

    6.1 Realm [2.5 Realm]及[3.5 Authorizer]部分都已经详细介绍过Realm了,接下来再来看一下一般真实环境下的Realm如何实现. 1.定义实体及关系   即用户-角色 ...

  6. Django 的逆向解析url--reverse(转)

    https://www.cnblogs.com/zhenfei/p/6368955.html Django中提供了一个关于URL的映射的解决方案,你可以做两个方向的使用: 1.有客户端的浏览器发起一个 ...

  7. consul总结

    一.介绍 内置了服务注册与发现框 架.分布一致性协议实现.健康检查.Key/Value存储.多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等)服务部署简单,只有一个可运行的二进制的包.每 ...

  8. 【oauth2.0】【2】JAVA 客户端模式

    含义:用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题 步骤: (A)客户端向认证服务器进行身份认证,并要求一个访问令牌(token). (B ...

  9. leetcode-algorithms-34 Find First and Last Position of Element in Sorted Array

    leetcode-algorithms-34 Find First and Last Position of Element in Sorted Array Given an array of int ...

  10. 牛客练习赛30-A/C

    链接:https://ac.nowcoder.com/acm/contest/216/A来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他语言65536K ...