[注:本文是 2012.07.14 CodeIgniter 中国第一次线下活动 PPT 分享《学习 CodeIgniter》演讲内容记录。更多的现场活动细节请查看这里,PPT 附件也在刚才的帖子里,本贴是配合 PPT 使用的,所以配合着一起看的话不会让本文显得太凌乱。]
学习 CodeIgniter BruceWolf 2012.07.14
我是07 年圣诞加入的 CI 中国社区,社区 ID 是 BruceWolf,Email 地址也是这个,BruceWolf(a)CodeIgniter.org.cn。早期在社区里翻译过一些资料,现在在社区里主要负责打杂。
标题有点大,大家别紧张,后来想把标题改了,想想公告已经发出去了,就没改。其实今天是纯唠嗑,说说 CodeIgniter 那些事。
关于 CodeIgniter
一个叫 Ellis 的前摇滚乐手写的框架
没什么好说的,一个前摇滚乐手写的框架,满世界被使用,如果自己还想给自己找借口学不会编程,我只能说想想 Ellis 同志吧。
管理员里面也有一个从搞艺术转行到搞技术没几年的,现在也混的很好。
学习 CodeIgniter
“不会 PHP 可以先学 CI 找份工作之后再找机会学习 PHP”
这句话是真的,我们社区翻译的那本书确实有这句话。前几天有个小伙把这给写签名上了(这位小伙居然在场,在上之前 Hex 演讲结束的时候还特地问了这个问题),这里我要向这位同学道个歉,不好意思误导你了。
这是对 CI 的赞扬,但不是程序员该有的心态。
CI易学易用,只学会用CI能让你做出一些程序,但只会用 CI 不能让你掌握编程思想。对你学习 PHP 无铺垫作用,不会节省你学 PHP的时间。但是反过来,你熟练掌握 PHP 会节省你大量的学习 CI 的时间,同时会节省你学习其它语言的成本。
但是如果真没有一点程序理念和技术基础,学习程序开发都是一个曲线很大的过程,和是不是学 CI 没关系。所以最好能从下往上的学习编程。
how?
怎么学?
How can I?
学我如何能怎么怎么?这是大家都关心的,
How does it?
还是学这东西是如何实现的?对很多人来说,寡然无味,不过这是我们今天主要讲的。如果这个 how 太长了的话,一会儿我们就简单说一下 why。
how does it
造轮子说
大家都知道吧,不要造轮子,被用来 bs 一切重复的劳动。黑盒,API,透明等等等等,这些词经常听到,某某框架好,易学,易上手。
造轮子可耻!
世界上为什么需要这么多程序员?90% 的业务需求是个性需求,通用软件只能解决 10% 的问题,剩下的呢?自己造吧。
你不能去造现有的轮子,但是要知道怎么造轮子。
学习造轮子
造轮子和学习造轮子是两码事,如果你没学过造轮子,面对新需求时如何开发出合格的产品?人总得从零开始突破,得学着造几个轮子。
另一方面,用现成的轮子会不会出错?如果出错,能查出原因来吗?如果没有去了解过轮子是怎么转起来的,解决问题不会太容易吧。
CI 运行也会有问题,如果大家都不去了解内部逻辑,有问题都去找作者来帮忙?现在很多人找 Hex 帮忙,如果按照前面说的,Hex 也没去了解底层的逻辑,找谁?找 CI 作者 Ellis?他说英语,大家会吗?
所以即使不造轮子也要学会怎么造轮子,偷懒总是以知道如何偷懒为前提的,要知道如何偷懒,得花点苦功夫。
要去学习多大的轮子?
这个要具体分析,将来你面对的问题不知道有多大。
就 CI 而言,手册通读,核心代码过一遍,能看懂整个代码逻辑。
php 而言,手册所有的部分,除了函数索引部分,都应完整的看一遍。
CI 的特性
免费、快速、轻量、功能强大
这里哪个更重要?先不说其他的,就轻量二字就应该把 CI 当作代码学习的案例。前提是我已经知道很多人在使用 CI,也知道同样很多人在用 cakephp 或者 symfony。
当然,只是想简单地用用 CI 就不能把“轻量”放首位了。我最开始学 PHP 的时候是想做一个应用,没转过弯,没想过可以用现成的,只是到处搜轻量级框架,最后终于让我找到 CI 了。
对 PHP 有概念是从 CI 开始的、从抄代码、读代码开始。
文档丰富、社区活跃
中文文档比较及时、社区也是相当活跃的,我就故意不说了。Hex 是个好同志,但是这里人多,不能太表扬 Hex。
技术特性
一会儿我们讲一下架构特性和程序开发思想,顺便关联的介绍一下 CI 的 core/ 目录里的核心类库,library 下的类库属于功能性的组建,这里就不细说了。主要说一下 CI 核心本身的一些特性。
架构特性
MVC
CI 之根本
用 CI 的人应该都知道它是 MVC 框架吧?在正常情况下请求发送给相应的 Controller,也就是控制器,控制器根据传入参数让 Model 取出相关数据,然后控制器对数据进行逻辑处理,处理好的数据发给 View 生成页面。以上说的是一般情况的一般情况,还有很多一般情况,比如 Model 还需要负责数据写入。
在我的理解里,Model 就是把 PHP 进程以外的数据转化成 PHP 能操作的数据,以及逆向操作。比如读写 Database、File、Memcache。其实这是一个概念,名字可以随意定,可能在另外一些架构里面它的名字不一样,像前些天看到的 MOVE。但是有这么一个模块来处理这件事情,外部数据源的数据读写模块,不参与任何业务逻辑。
MVC 是理念约束
理念约束不是强制性的。
[互动]你们见过往 view 里面写 function 的吗?(没人举手,其实会后交流的时候有人也做过这个)没见过是吧,我也没见过。但是以前在一个公司,一个同事维护他刚离职的组长的代码的时候发现了这个奇观,然后在 QQ 上把签名给改了,于是我也有机会知道这么个传奇故事。
MVC 是基础架构
MVC 只是让形成了基础的架构,纯 MVC 基本功能也没人直接拿来使用。为了便于使用,CI 提供了其他辅助文件,放在 libraries 或者 helper 下面,但那些只是功能性的代码,并不影响 CI 的构架。这个有点像linux 的 kernel 和 linux 发行版的关系,相信这里没人直接用 linux kernel 吧?除非你是搞嵌入式的。
超级对象
这个有意思了,CI 留给程序员的业务逻辑空间大部分都是在超级对象之下,建立一个控制器类,继承 CI_Controller 或者 MY_Controller,所使用的 Controller、Model、Library 等等都是 $this 的一个 property,Controller、Model、View 里面的 $this 都能直接使用这个超级对象的资源,感觉上是同一个东西。它,无处不在。
连 helper 这种定义为一个函数完成一个功能的角色,它的载入都是靠超级对象的 load 属性来完成的,这也给人造成一定的错觉。
Hooks
刚才说的一切都在超级对象的管辖之下,具体说是除了 hooks。hooks 可以负责向超级对象以外的业务空间添加逻辑,业务的维护和管理不在超级对象内完成,使用单独的 hook 文件和配置。稍后我们会具体讲 hooks 机制。
无处不在
一种幻觉,CI 原来是只有面向对象,所有的业务逻辑都是在超级对象对内部完成。实际上超级对象并不是一个对象,只是提供一个具有相同的数据调用方式而已。通过 $this 来访问数据资源,但是 $this 并不是同一个对象。以下是 MVC 三个模块实现对同一资源引用的方式。
Controller
控制器没什么好说的,继承自 CI_Controller,$this 就是超级对象
Model
通过魔术方法 __get($key),只要是自己没设置的属性都尝试去超级对象去取
PHP复制代码
function __get($key)
{
$CI =& get_instance();
return $CI->$key;
}
复制代码 view
View 中实现超级对象则是通过foreach 遍历,把超级对象的所有属性都在 Loader 对象下建立一个同名属性通过引用关联起来。$this 实际是 Loader 实例化出来的对象。
最终结果就是让开发者以为超级对象无处不在,在提供给开发者的业务空间里,会让你飘飘然,原来这就是面向对象啊?其实这个实现我是真心纠结,不过这纠结的目的就是让开发者方便。
超级对象的幕后英雄是一个叫做 $CI 的变量,它一直是通过引用的方式使用超级对象的资源,当超级对象的资源不能以 $this 的形式引用时,就通过 $CI =& get_instance(); 来使用。
另外有一个类似的角色,$conf,config_get()、Config 类都是引用同一个数组值。
按需加载
Load 是一个理念
load 并不仅仅是指 core/ 下面的 Loader 类。load 的使用也并不仅仅局限在 model、view、library、helper 这些上面,Lang、Config、load_class()、hook 也是这个理念的一个具体表象,都是按需加载,资源一般为引用。
Load 是一个系统
由多个实现统同一理念的类和函数组成
core/Loader.php
Loader,可能是开发者调用最多的类了,在 CI 里面算是核心的核心,load 系统的重要组成部分。
30K,1248 行
看代码量就知道,30K,1248 行, CI core/ 目录下最硕大的文件,目测就是最核心的组件。如前面所介绍的,在留给开发者的业务空间里面,超级对象下几乎所有的属性都是 loader 载入进来的,除了 loader 本身,是在控制器实例化的时候在继承的构造函数里或者显性调用 parent::__construct() 来引入到超级对象里。
load_class()、config_get()、Lang、Config
在超级对象实例化之前,load_class() 就是加载类以及将其实例化的唯一功臣,CI 自带的核心类里面也是大量使用,位置在 core/common.php 文件里。
其他的都差不多理念,只是加载的资源/数据不一样。load 系统所体现的理念是什么呢?
按需加载,资源复用
load 时资源不存在,生成一份,如果存在,直接复用。
hook 是个例外,只有按需载入,没有资源复用。因为它是纯面向过程的调用,hook 可能会让很多人费解,我们稍后会具体讲到。
按需
php 是翻译型语言,就本身而言每次执行前都需要翻译,为减少开销,不能因为可能使用就一次性预加载相关的脚本,只有确定需要使用时才载入。
复用
PHP复制代码
public function sample()
{
$this->load->library('lib1');
$this->lib1->doSomething();
}
复制代码 在一个方法里加载了一个资源,在另外一个方法可能还要加载,在不确定已经加载某个资源的情况下,每次都要重新调用加载。虽然都在超级对象之下,几乎所有的资源都用 $this 来引用,但是不能确定之前那个加载过某资源的方法已经执行了,除非是在控制器的 __construct() 方法下加载的或者有明确的先后调用关系。比如一个 model A 的方法调用了另外一个 model B,如果这个 model A 的另外一个方法也用到这个 model B,那就要重新加载一次。加载只是建立变量名和引用,所引用的资源并不重新生成。
复用,会节省一定的资源,以及使用资源最新状态的数据
比如一个控制器方法内给超级对象加了一个属性,在 Model 内可以直接使用,
config 数组修改了之后就不能直接去 config.php 取数据了,那会造成数据不一致,其实加载过的config不会重新从配置文件读取。
纠结
按需加载也带来一些纠结,资源的优化和代码的维护怎么平衡。
一个控制器 7 个业务方法,4 个需要使用某一个 model,是在这 4 个方法里单独载入还是在 __construct() 里直接载入?__constrcut() 来预加载可能需要的对象,对剩余 3 个业务方法就不是按需加载,但是每次执行的都只有一个业务方法。如果单独写的话,四个方法就要多四条 load,这是简单举例,复杂一点的业务逻辑可能不是 4 乘 1 的概念了。这个要自己把握。
自身可扩展
计划总是赶不上变化,谁也不知道下一个业务需求是什么样子。作为通用底层程序,自身可扩展这个特性非常重要,CI 给开发者留下了非常宽敞的操作空间。除了 system/core/CodeIgniter.php 这个文件不能被换,其它的都能换掉,如果这个文件换掉了,那就不是 CodeIgniter 框架了。
load 系统的支持
这个特性的实现很大程度也是基于这个 loader 系统。按什么顺序、按什么规则加载所需要的资源,loader 并不是根据传入的关键字,检查一个文件,而是按照优先级检查多个文件是否存在。比如 CI_Contorller,就是 load_class() 函数先去检查 application/core 目录下是否有相应文件,然后才去检查 system/core 下的 CI_Controller。
Hooks 机制的支持
钩子机制也是重要组成部分,hook 机制让开发者能够操作超级对象以外的业务空间。前面讲到 hooks 超然于和超级对象之外,尤其是在超级对象启动之前的一些业务处理。稍后讲 CI 的生命周期时会详细介绍 hooks 的几个挂载点。看手册着实不好理解,总觉得很玄乎,尤其是那名字不好懂。其实看下 core/CodeIgniter.php 的代码,你会发现它很好理解,最好自己都看一遍。
所谓的挂载点,其实是在面向对象的编程过程中,在从上至下的流程中的几个位置判断要不要执行其他逻辑代码。这里的逻辑代码指不在 MVC 框架下的代码。
功能原型
hook 的钩子通过在代码流中插入一个语句,调用 Hooks::_call_hook() 来实现的
原型相当于这个
PHP复制代码 // 插入点:
hook ('pre_system');
// 某文件中,相当于 Hooks 类:
function hook ($hook) {
if(defined($hook)) {
include($hook.'.php');
}
}
// 某功能文件:
pre_system .php
function sayHi ()
{
echo 'Hi~';
}
sayHi (); 复制代码
在某个位置触发 hooks 的检查,如果有相应的功能文件,载入并执行。
hooks 机制的优点就是实现了两个分离:逻辑与配置参数的分离,逻辑与功能代码的分离。hooks 开启后会载入 hooks 配置文件 config/hooks.php,然后去判断是否有功能代码要执行,有的话载入并执行。
挂载点的名字
为了方便人理解各个判断点,在这些逻辑代码的插入点设置了名字,由于是插入点都是英文,所以我们总觉得别扭,什么叫 post_controller, 什么叫 post_system?post 不是投递吗?还是 http 传参方式?post 有“之后”的意思,最有名的 postfix,取名的目的就是给 sendmail 殿后,在“之后” “修复”。pre 就是“之前”,现在好理解了吧。具体 hooks 后面会详细介绍。
目录结构设计
system 和 application 的子目录结构有很多是相同的。这么处理的目的,两个
1、便于文件管理
便于开发者管理自己的代码文件,尤其是对原有框架进行扩展的时候
2、便于 Loader 的实现
在现有 load 系统下对CI的扩展不需要修改 system 下的文件,同样 system 升级的时候也不怎么需要修改自己的代码。如果要实现更动态设置,性能影响是一方面,那 load 代码得多纠结?我这自定义的 model 文件所在的目录不叫 models,虽然也放在 application/ 下面,我喜欢单数,叫 model。想找到 model/ 目录先去找配置文件,配置这个选项的文件在哪哪哪,这样的 loader 开发下来差不多也要疯了。
规则,就是为了高效。
优雅的 CodeIginiter
超级对象
按需加载
自身可扩展
正是超级对象、按需加载、自身可扩展,这三个特性让 CodeIgniter 变得优雅。
程序开发思想
刚才讲的是 CI 的架构特性,现在说一下程序开发的一般思想。这里不是程序开发思想的系统讨论,只是说从核心代码表现出来的一些思想在这里讨论一下。首先是安全。
安全
Security, Input
CI core/ 目录下除了刚才的 Loader 类所在的文件体积最大,紧接着就是 Security 和 Input 类了。可以想象安全在 CI 中的地位了吧,启动时后台已经对 $_GET、$_POST、$_COOKIE 做了预处理,去除不可见字符等等,以及其他一些可选操作,比如 xss 过滤。
安装指导
另外一个体现 CI 重视安全的地方是手册上的安装指导:设置目录在 web 目录之外、.htaccess、密钥、php 运行错误提示等等,出现最多的词就是“安全”,基本上就是就是安全指导。
性能
性能一直是 CI 的看家手艺,CI 一直想挖掘性能潜力。前面已经讲了按需载入,另外一个 MVC 中的 view 没有模板。
模板引擎
PHP 本身就是一个模板语言,最早是嵌入在 HTML 来生成动态内容的,当年就是 html 注释符号中间灌上变量和简单的数据处理、条件判断等等
1994 年的 PHP [摘自 Rasmus Lerdorf 的 PPT] HTML复制代码 <!--getenv HTTP_USER_AGENT-->
<!--ifsubstr $exec_result Mozilla-->
Hey, you are using Netscape! <p>
<!--endif-->
<!--sql database select * from table where user='$username'-->
<!--ifless $numentries 1-->
Sorry, that record does not exist <p>
<!--endif exit-->
Welcome <!--$user-->! <p>
You have <!--$index:0--> credits left in your account. <p>
<!--include /text/footer.html--> 复制代码
1995 年的 PHP [摘自 Rasmus Lerdorf 的 PPT] PHP复制代码 <?
$name = "bob";
$db = "db";
$result = msql($db,"select * from table where firstname='$name'");
$num = msql_numrows($result);
echo "$num records found!<p>";
$i=0;
while($i<$num);
echo msql_result($result,$i,"fullname");
echo "<br>";
echo msql_result($result,$i,"address");
echo "<br>";
$i++;
endwhile;
> 复制代码
这个已经和现在的 PHP 有点像了,注意看它的闭合,还是 HTML 标签,没有斜线,当年还没 xhtml。对于 php 来说,模板是多余的。如果是跨语言呢?模板就有其价值了,mustache,各种语言都有实现它的模板引擎,比如需要异步渲染 HTML 页面的时候,第一次生成页面时 PHP 使用的模板和后来 Javascript 使用的模板可以是同一个。扯远了,回头说 CodeIgniter 的模板引擎:
include
CI 的模板渲染处理机制非常简单,简单到你不敢想象,默认是没有模板引擎的。直接 include,模板解析交由 PHP 来处理,因为前面已经说了 PHP 天生就是模板引擎。
eval
<?= 变成 <?php echo
eval 的触发需要两个条件,会把 view 文件中的字符串提取替换掉短标签 <?= 变成 <?php echo 后 eval
1. PHP 没有开启短标签
很多年来 PHP 都不开启短标签了,相当于这个条件没什么用
2. 设置了 CI 的 rewrite_short_tags 为 true
之前为了向 XHTML 看齐,取消掉了短标签,从 5.4.0 <?= 又开始一直有效,这个替换其实就是没意义,为了兼容性,支持一下历史版本吧。
Benchmark
为什么把 Benchmark 放这里?
我觉得这个类其实可有可无,在自己的 controller 文件里写 microtime() 比调用 $this->benchmark->mark() 还省键盘敲击次数。而且这玩意绝对是对性能的负提升,和业务一点关系都没有,要这干嘛?CI 把它放 core/ 下面,就是想突出一下自己性能而已,是吧?大家不要上当,当然,有特殊爱好的可以多使使。
这个是开玩笑,待会儿讲 CI 的生命周期的时候会提到,Benchmark 其实在是 CI 实例化的第一个对象,比前超级对象实例化早了好几代,主要用于找出性能瓶颈。另一方面也是提醒开发者注意性能问题。
多环境支持
作为在整个程序的入口文件 index.php 第一行的 php 语句,能不重要吗?是很重要,这个是 web 开发的基本理念,开发、测试、线上三个环境分离,为了这个多环境配置,CI 核心改了不少代码以配合开发环境的使用,主要是在 load 系统上做的修改。
用户体验
伪静态
有效优化搜索引擎检索以及用户体验,CI 忽悠初级用户的最大卖点,伪静态,你以前见过没?知道怎么实现的吗?不知道,那更好,直接用我的吧。以前手册上技术部分的第一个页面,这就是身价。现在改新闻教程了。
伪静态主要是由 URI 和 Router 两个协作调完成,不过技术上确实没什么好说的,字符串和数组操作。对它的实现有兴趣的同学回头可以自己看看。
整体上说,CI 自身用好可用的,能不用就不用。当年就力挺 PHP4,用足了 PHP4 的特性。现在对 php 的新特性也支持不是很好,这个还是有点遗憾,对新人挺好,PHP 的新特性比较纠结。
其它类库就不作具体讨论了, upload 类体现了什么开发思想?这个说不开,就一功能类。
CodeIgniter 的生命周期
index.php system/core/CodeIgniter.php
CI 的生命周期,两个文件就表达清楚了,php 的执行始于 index.php,终于 core/CodeIgniter.php。前面提到的超级对象,只是 core/CodeIgniter.php 的一个环节生成、使用的。
index.php
各种配置、参数定义
没有什么业务逻辑,主要是初始化
设置运行环境 product、development 或者 development
为了安全,可以将 system 和 application 目录转移到其他位置
被注释的一个变量 $routing,设置默认控制器
网站后台大改或者需要离线的时候可以用上
用于覆盖 config/config.php 设置的数组 $assign_to_config
基本上用不到,多个应用共用 config/config.php 才用得上
其他目录参数的提取,这里就不是设置了
include system/core/CodeIgniter.php
末了 include core/CodeIgniter.php
index.php 的代码执行完了
system/core/CodeIgniter.php
一开始依然是各中参数定义,引入常数、配置
include('core/commons.php')
引入常用函数
commons.php 最重要的函数就是 load_class() 了
还有两个重要的是 config 相关的 get_config() 和 config_item()
没这三个 CI 起不来。
config/constants.php
引入 application 下某个位置的 config/constants.php,主要是文件/文件夹操作相关的常数设置。象征意义大于实际意义,对英语国家的初级开发者是个便利,但是文件操作的那几个标识符已经是各语言通用,记下才是正常的。
其他配置
错误处理函数、运行时间限制等等
load_class(),后面是一路的 load_class()
Load benchmark、hooks
第一个被加载的核心类是这个,Benchmark
hooks,耳熟了吧,CI 运行过程中的里程碑可以说是由 Hooks 来标记的。后面的 hook 都由它来打理
hook: pre_system
这里对 CI 来说其实什么业务都还开始代码执行到这里,你想做点什么就去配置对应的 config/hooks.php,你可以去弄一个 exit("byebye, world")。pre_system 指的是加载完 benchmark 和 hooks 之后的一个逻辑代码插入点。
Load Config、Utf8、URI、Router、Output
pre_system 之后还是接着刚才的 load_class() 一路狂飙,一口气加载 Config、Utf8、URI、Router、Output 五个类并实例化,根据构造函数还执行了一些业务逻辑:
Config 是接着前面 config_get() 的劳动成果,初始化一些配置,并设置参数 base_url 的值
utf8 设置了一些常数开关
URI、Router、Output 这时候可以说什么也没干
hook: cache_override
于是到了钩子 cache_override
这里和 手册上的顺序稍微有点不一样,手册上第二个是 pre_controller。
撇开钩子不说,在业务逻辑上,执行到这里,程序已经有足够的工具资源来查找缓存文件了,虽然超级对象还没被实例化,但是 Output 对象已经有了,所需要的 URI、Config 对象也已经有了。
如果没有钩子,返回缓存内容,直接 exit()。所以这里来个钩子,就是插入处理读取缓存以及其他相关操作的业务代码,从而覆盖 CI 默认的 output::_display_cache(),返回完缓存内容记得 exit()。在这里自定义方法可以往缓存头部追加一段广告代码,过期即时下线,而不用重新生成缓存文件。
读取缓存成功才能 exit,如果失败?继续执行 load_class()。
Load Security、Input、Lang
Security 没做什么太多,生成一个 CSQF 待使用。Input 就勤劳了,首先用 php 的方式把 register_globals 执行了一遍,这个其实也经没多大意义了,4.2.0 开始默认设置为 off,5.3.0 开始会报不建议使用的错误,5.4.0 开始就彻底抛弃了。然后,把 $_GET、$_POST、$_COOKIE 的值都处理了一遍。劳苦功高不得不说。相比之下,Lang 什么也没干,需要 debug 时写一条 log,不然就真的什么也不干。
下面就是体力活了,一些逻辑代码的处理。载入 core/Controller.php,以及扩展文件,如果有的话。通过路由查找对应的控制器,确认类存在,确认所需的方法是否合法,不能下划线开始,不能是 CI_Controller 的方法。一切顺利就 include 控制器所在的文件,否则 404。这一段算是 core/CodeIgniter.php 最长的逻辑代码了,不过这里好像也没说多少。
hook: pre_controller
走到这个点,就所有准备工作都做得差不多,万事具备只欠东风。可是设置影响控制器实例化的参数,主要是通过变量、参数修改来影响它的构造函数执行。
$CI = new $class();
实例化控制器 $CI = new $class(),在 CI_Controller 的构造函数里,Loader 类被加载了。
仅仅这一行代码之后,钩子 post_controller_constructor 出现。
hook: post_controller_constructor
两个 hook 之间就这么一个语句。
这时候还没开始处理业务请求,只是让你开始开始准备处理业务请求,给你一个钩子,你想干啥就干啥吧,主要是通过变量、参数修改来影响具体控制器方法的执行。在这个环节之后,你写的控制器方法才被启用,首先是找一下有没有 _remap,有的话重定向到相应的方法上,如果最后调用控制器的方法失败就是 404 了。调用控制器合适方法是 core/CodeIgniter.php 第二长的逻辑代码了,说起来又简单了一点。
hook: post_controller
hook: display_override
hook: post_system
这三个没什么好说的,挨在一起,如果不是 hook dispaly_override 的存在的话,这三个可以合一块儿了。
display_override 如果没有逻辑代码要执行的话,就使用 Output::_display() 输出 view。这里的 hook 就是替代 Output::_display()处理要输出的字符串,和前面的 output::_display_cache() 差不多,只不过前面是 PHP 处理从缓存文件来的数据,这里是处理 PHP 临时生成的字符串的数据。
就这么个 if,把一个 hook 变三个。
清理数据库连接
最后,清理一下 DB 资源了。
差不多就这些吧。
感谢大家以巨大的忍耐力听我在这里扯了这么久,谢谢大家!
|