到了这里,终于进入CI框架的核心了。既然是“引导”文件,那么就是对用户的请求、参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位。例如,用户的请求url:

http://you.host.com/usr/reg

经过引导文件,实际上会交给Application中的UsrController控制器的reg方法去处理。 这之中,CodeIgniter.php做了哪些工作?我们一步步来看。

1.    导入预定义常量、框架环境初始化

  之前的一篇博客(CI框架源码阅读笔记2 一切的入口 index.php)中,我们已经看到,Index.php文件中已经对框架的ENVIRONMENT,application,system等做了定义和安全性检查.

(1).   加载预定义常量Constants.

如果定义了环境,且针对该环境的预定义常量文件存在,则优先加载环境的常量定义文件,否则加载config目录下的常量定义文件:

if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))
{require(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
}
else{require(APPPATH.'config/constants.php');
}

这么做的原因,我们之前已经介绍过了,可以快速切换环境和相应参数而不必更改应用程序核心代码。

(2).   设置自定义错误处理函数。

  这里是_exception_handler函数,该函数的定义和解释见上一篇博客( http://www.cnblogs.com/ohmygirl/p/CIRead-3.html)。再次引用手册中一句话,提醒大家注意:以下级别的错误不能由用户定义的函数来处理: E_ERRORE_PARSEE_CORE_ERRORE_CORE_WARNINGE_COMPILE_ERRORE_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT

(3).   检查核心class是否被扩展

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

其中,$assign_to_config应该是定义在入口文件Index.php中的配置数组. 通常情况下,CI的核心组件的名称均以”CI_”开头,而如果更改了或者扩展CI的核心组件,则应该使用不同的subclass_prefix前缀如MY_ ,这种情况下,应该通过$assign_to_config[‘subclass_prefix’]指定你的扩展核心的前缀名,便于CI的Loader组件加载该类,或者可能出现找不到文件的错误。另外,subclass_prefix配置项默认是位于APPPATH/Config/config.php配置文件中的,这段代码同样告诉我们,index.php文件中的subclass_prefix具有更高的优先权(也就是,如果两处都设置了subclass_prefix,index.php中的配置项会覆盖配置文件Config.php中的配置)。

到这里,CI框架的基本环境配置初始化已经算是完成了,接下来,CodeIgniter会借助一系列的组件,完成更多的需求。

2.    加载核心组件

  通常,CI框架中不同的功能均由不同的组件来完成(如Log组件主要用于记录日志,Input组件则用于处理用户的GET,POST等数据)这种模块化的方式使得各组件之间的耦合性较低,从而也便于扩展。CI中主要的核心组件如下所示:

BM
EXT
CFG
UNI
URI
RTR
OUT
SEC

其中:

  BM:   指BenchMark,是CI的基准点组件,主要用于mark各种时间点、记录内存使用等参数,便于性能测试和追踪。

  EXT:  CI的扩展组件,前面已经介绍过,用于在不改变CI核心的基础上改变或者增加系统的核心运行功能。Hook钩子允许你在系统运行的各个挂钩点(hook point)添加自定义的功能和跟踪,如pre_system,pre_controller,post_controller等预定义的挂钩点。以下所有的$EXT->_call_hook("xxx");均是call特定挂钩点的程序(如果有的话)。

  CFG:  Config配置管理组件。主要用于加载配置文件、获取和设置配置项等。

  UNI:  用于对UTF-8字符集处理的相关支持。其他组件如INPUT组件,需要改组件的支持。

  URI:  解析URI(Uniform Rescource Identifier)参数等.这个组件与RTR组件关系紧密。(似乎URI与Router走到哪里都是好基友)。

  RTR:  路由组件。通过URI组件的参数解析,决定数据流向(路由)。

  OUT: 最终的输出管理组件,掌管着CI的最终输出(海关啊)。

  SEC:  安全处理组件。毕竟安全问题永远是一个大问题。

  以BM组件为例,核心组件的加载方式是:

$BM =& load_class('Benchmark', 'core');

调用了load_class函数获取core目录下的相应组件。(load_class的实现和具体介绍见之前的博客:CI框架源码阅读笔记3 全局函数Common.php)

各组件的功能和具体实现之后会有详细的分析, 这里我们只需要知道该组件的基本功能即可。

3.    设置路由。

调用很简单,只有一句话:

$RTR->_set_routing();

调用Router组件的_set_routing()函数来设置路由,具体的实现细节,我们这里暂且不管(之后的部分会有详细介绍),我们只需要知道,通过_set_routing的处理,我们可以获得实际请求的Controller,URI的segment参数段等信息。

值得注意的是,CI允许在index.php中配置routing,且会覆盖默认的routing设置(如共享CI的安装目录的多个应用程序可能有不同的routing):

if (isset($routing))
{$RTR->_set_overrides($routing);
}

设置完路由之后,可以通过该组件的:fetch_diretory() , fetch_class(), fetch_method()等分别获取目录、类、和方法。

4.      检查缓存

到了这一步,CI会先检查是否有cache_override这个钩子(默认情况下没有配置,也就是返回FALSE),如果没有注册,则调用_display_cache方法输出缓存(这种说法并不准确,准确来说应该是,如果有相应的缓存,则输出缓存且直接退出程序,否则返回FALSE,这里我们暂时不去思考实现细节):

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

5.      实例化控制器,安全性验证、实际处理请求。

  能够走到这里,说明之前的缓存是没有命中的(实际上,任何页面都是应该先走到这一步,然后才会有设置缓存,之后的访问检查缓存才会命中)。这一步会require Controller基类和扩展的Controller类(如果有的话)及实际的应用程序控制器类:

require BASEPATH.'core/Controller.php';
if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
{require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
}
if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'))
{show_error('xxx');
}include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');

之前我们已经说过,在Router组件_set_routing之后,可以通过fetch_directory(), fetch_class(), fetch_method()等分别获取请求的文件目录、控制器和方法。

现在对请求的控制器和方法做验证,我们看一下CI的主要验证:

if ( ! class_exists($class)OR strncmp($method, '_', 1) == 0OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller')))
)

这里简单解释一下,CI认为不合法的情况有:

 (1).请求的class不存在:! class_exists($class)

 (2).请求的方法以_开头(被认为是私有的private的方法,之所以这么做事因为php并不是一开始就支持private,public的访问权限的):strncmp($method, '_', 1) == 0

 (3).基类CI_Controller中的方法不能直接被访问:in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller'))

如果请求的条件满足上面3个中的任何一个,则被认为是不合法的请求(或者是无法定位的请求),因此会被CI定向到404页面(值得注意的是,如果设置了404_override,并且404_override的class存在,并不会直接调用show_404并退出,而是会像正常的访问一样,实例化:$CI = new $class();)

走到这里,CI的Controller总算是加载完了(累趴)。不过且慢,还有不少事情要做:

(1). 检查_remap

_remap这个东西类似于CI的rewrite,可以将你的请求定位到其他的位置。这个方法是应该定义在你的应用程序控制器的:

public function _remap($method){$this->index();
}

现在,所有的请求都会被定位到改控制器的index()中去了。如果_remap不存在,则调用实际控制器的$method方法:

call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));

(2).最终输出

  $this->load->view()之后,并不会直接输出,而是放在了缓存区。$Out->_display之后,才会设置缓存,并最终输出(详细参考Output.php和Loader.php)

(3)若有使用了数据库,还要关闭数据库连接:

if (class_exists('CI_DB') AND isset($CI->db))
{$CI->db->close();
}

注意,如果在Config/database.php中设置了开启pconnect,则建立的连接是长连接,这种长连接是不会被close关闭的。所以,请谨慎使用pconnect.

到现在,CI的核心流程总算是走完了(虽然还有很多细节的问题,但不管怎么说,大树的枝干已经有了,树叶的细节,可以慢慢添加)。在结束本文之前,我们来梳理一下CI的核心执行流程:

 回顾之前我们引用的官方给出的流程图,是不是基本一致的:

最后,贴上整个文件的源码:

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');/*** CodeIgniter Version*/define('CI_VERSION', '2.1.4');/*** CodeIgniter Branch (Core = TRUE, Reactor = FALSE)*/define('CI_CORE', FALSE);require(BASEPATH.'core/Common.php');/* 如有有相应的环境的配置文件,优先加载配置下的常量定义文件,否则加载config目录下的常量定义文件 */if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php')){require(APPPATH.'config/'.ENVIRONMENT.'/constants.php');}else{require(APPPATH.'config/constants.php');}set_error_handler('_exception_handler');if ( ! is_php('5.3')){@set_magic_quotes_runtime(0); // Kill magic quotes
    }/**  Set the subclass_prefix*/if (isset($assign_to_config['subclass_prefix']) AND $assign_to_config['subclass_prefix'] != ''){get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));}/**  Set a liberal script execution time limit*///if(function_exists("set_time_limit") && @!ini_get("safe_mode"))if (function_exists("set_time_limit") == TRUE AND @ini_get("safe_mode") == 0){@set_time_limit(300);}$BM =& load_class('Benchmark', 'core');$BM->mark('total_execution_time_start');$BM->mark('loading_time:_base_classes_start');/**  Instantiate the hooks class*/$EXT =& load_class('Hooks', 'core');/**  Is there a "pre_system" hook?*/$EXT->_call_hook('pre_system');/**  Instantiate the config class*/$CFG =& load_class('Config', 'core');// Do we have any manually set config items in the index.php file?if (isset($assign_to_config)){$CFG->_assign_to_config($assign_to_config);}/**  Instantiate the UTF-8 class*/$UNI =& load_class('Utf8', 'core');/** ------------------------------------------------------*  Instantiate the URI class* ------------------------------------------------------*/$URI =& load_class('URI', 'core');/**  Instantiate the routing class and set the routing*/$RTR =& load_class('Router', 'core');$RTR->_set_routing();// Set any routing overrides that may exist in the main index fileif (isset($routing)){$RTR->_set_overrides($routing);}/**  Instantiate the output class*/$OUT =& load_class('Output', 'core');/**    Is there a valid cache file?  If so, we're done...*/if ($EXT->_call_hook('cache_override') === FALSE){if ($OUT->_display_cache($CFG, $URI) == TRUE){exit;}}/** Load the security class for xss and csrf support*/$SEC =& load_class('Security', 'core');/**  Load the Input class and sanitize globals*/$IN    =& load_class('Input', 'core');/**  Load the Language class*/$LANG =& load_class('Lang', 'core');/**  Load the app controller and local controller*/// Load the base controller classrequire BASEPATH.'core/Controller.php';function &get_instance(){return CI_Controller::get_instance();}if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php')){require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';}if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php')){show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.');}include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');$BM->mark('loading_time:_base_classes_end');/**  Security check*/$class  = $RTR->fetch_class();$method = $RTR->fetch_method();if ( ! class_exists($class)OR strncmp($method, '_', 1) == 0OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller')))){if ( ! empty($RTR->routes['404_override'])){$x = explode('/', $RTR->routes['404_override']);$class = $x[0];$method = (isset($x[1]) ? $x[1] : 'index');if ( ! class_exists($class)){if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')){show_404("{$class}/{$method}");}include_once(APPPATH.'controllers/'.$class.'.php');}}else{show_404("{$class}/{$method}");}}/**  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();/**  Is there a "post_controller_constructor" hook?*/$EXT->_call_hook('post_controller_constructor');/**  Call the requested method*/// Is there a "remap" function? If so, we call it insteadif (method_exists($CI, '_remap')){$CI->_remap($method, array_slice($URI->rsegments, 2));}else{if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI)))){if ( ! empty($RTR->routes['404_override'])){$x = explode('/', $RTR->routes['404_override']);$class = $x[0];$method = (isset($x[1]) ? $x[1] : 'index');if ( ! class_exists($class)){if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')){show_404("{$class}/{$method}");}include_once(APPPATH.'controllers/'.$class.'.php');unset($CI);$CI = new $class();}}else{show_404("{$class}/{$method}");}}call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));}// Mark a benchmark end point$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');/**  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');/**  Close the DB connection if one exists*/if (class_exists('CI_DB') AND isset($CI->db)){$CI->db->close();}/* End of file CodeIgniter.php */

View Code

由于写作仓促,难免会有错误。欢迎随时指出,欢迎沟通交流。

参考文献:

  1. Calix的CI源码分析,写的很不错。本文的很多东西都是借鉴该文:http://blog.163.com/wu_guoqing/blog/static/196537018201281671321308/
  2. CI的论坛。http://codeigniter.org.cn/forums/thread-2992-1-1.html
  3. php官方Manual  http://cn2.php.net/manual/zh/index.php

转载于:https://www.cnblogs.com/ohmygirl/p/CIRead-4.html

CI框架源码阅读笔记4 引导文件CodeIgniter.php相关推荐

  1. CI框架源码阅读笔记8 控制器Controller.php

    最近时间有些紧,源码阅读系列更新有些慢.鉴于Controller中代码比较少,本次Blog先更新该文件的源码分析. 在经过路由分发之后,实际的应用Controller接管用户的所有请求,并负责与用户数 ...

  2. ****CI框架源码阅读笔记7 配置管理组件 Config.php

    http://blog.csdn.net/ohmygirl/article/details/41041597 一个灵活可控的应用程序中,必然会存在大量的可控参数(我们称为配置),例如在CI的主配置文件 ...

  3. CI框架源码阅读笔记7 配置管理组件 Config.php

    一个灵活可控的应用程序中,必然会存在大量的可控参数(我们称为配置),例如在CI的主配置文件中(这里指Application/Config/Config.php文件),有如下多项配置: $config[ ...

  4. CI框架源码学习笔记7——Utf8.php

    愉快的清明节假期结束了,继续回到CI框架学习.这一节我们来看看Utf8.php文件,它主要是用来做utf8编码,废话不多说,上代码. class CI_Utf8 {/*** Class constru ...

  5. 【Flink】Flink 源码阅读笔记(15)- Flink SQL 整体执行框架

    1.概述 转载:Flink 源码阅读笔记(15)- Flink SQL 整体执行框架 在数据处理领域,无论是实时数据处理还是离线数据处理,使用 SQL 简化开发将会是未来的整体发展趋势.尽管 SQL ...

  6. Yii源码阅读笔记 - 日志组件

    2015-03-09 一 By youngsterxyf 使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category); Yii: ...

  7. HashMap源码阅读笔记

    HashMap是Java编程中常用的集合框架之一. 利用idea得到的类的继承关系图可以发现,HashMap继承了抽象类AbstractMap,并实现了Map接口(对于Serializable和Clo ...

  8. Transformers包tokenizer.encode()方法源码阅读笔记

    Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode

  9. 源码阅读笔记 BiLSTM+CRF做NER任务 流程图

    源码阅读笔记 BiLSTM+CRF做NER任务(二) 源码地址:https://github.com/ZhixiuYe/NER-pytorch 本篇正式进入源码的阅读,按照流程顺序,一一解剖. 一.流 ...

最新文章

  1. Python 爬取北京二手房数据,分析北漂族买得起房吗?(附完整源码)
  2. 【C++】log(n)斐波那契数列计算
  3. CSP认证201409-1 相邻数对[C++题解]:排序
  4. 【AI白身境】深度学习中的数据可视化
  5. 市面上主流的TCC框架介绍
  6. TCP/IP详解学习笔记(8)-DNS域名系统
  7. Tengine开源新特性:如何让HTTPS处理能力轻松翻倍?
  8. 批量找注入 python3+sqlmap结合
  9. 特斯拉Model X成NASA官方宇航员转运车辆,已打上NASA标志
  10. 宕昌一中2021高考成绩查询,2014年陇南市普通高考分数成绩排名
  11. 高级字符驱动程序操作之休眠(理论篇)
  12. PyQt5 与PyQt4的区别
  13. php网页拍照并上传,HTML中网页拍照并上传照片的实现方法
  14. 面向对象编程三大特性------封装、继承、多态
  15. mac mini u盘安装系统_U盘重装Mac全新操作系统教程
  16. python取字母以及数字随机数
  17. mac和windows共享键盘鼠标方案
  18. 移动信息中心号码,移动短信中心号码速查
  19. 为方便旅客,某航空公司拟开发一个机票预定系统。旅行社把预定机票的旅客信息......
  20. 下血本买的!Android开发者出路在哪?先收藏了

热门文章

  1. 升级jquery-easyui1.4.5后treegrid无法显示
  2. struts2中访问servlet API
  3. Kenshin Cui's Blog
  4. 程序员面试不完全指南
  5. C++ STL中哈希表 hash_map介绍
  6. C# Windows Phone 8 WP8 开发,取得手机萤幕大小两种方法。
  7. NOI2019省选模拟赛 第五场
  8. react中webpack.config.js配置lessless-loader less
  9. Python基础——细琐知识点
  10. Linux 下监控系统几个重要组件