请离开include_once和require_once


Posted in PHP onJuly 18, 2013

诚然, 这个理由是对的, 不过, 我今天要说的, 是另外一个的原因.
我们知道, PHP去判断一个文件是否被加载, 是需要得到这个文件的opened_path的, 意思是说, 比如:

    <?php
    set_include_path("/tmp/:/tmp2/");
    include_once("2.php");
    ?>

当PHP看到include_once “2.php”的时候, 他并不知道这个文件的实际路径是什么, 也就无法从已加载的文件列表去判断是否已经加载, 所以在include_once的实现中, 会首先尝试解析这个文件的真实路径(对于普通文件这个解析仅仅类似是检查getcwd和文件路径, 所以如果是相对路径, 一般是不会成功), 如果解析成功, 则查找EG(include_files), 如果存在则说明包含过了, 返回, 否则open这个文件, 从而得到这个文件的opened_path. 比如上面的例子, 这个文件存在于 “/tmp2/2.php”.

然后, 得到了这个opened_path以后, PHP去已加载的文件列表去查找, 是否已经包含, 如果没有包含, 那么就直接compile, 不再需要open file了.

1. 尝试解析文件的绝对路径, 如果能解析成功, 则检查EG(included_files), 存在则返回, 不存在继续
2. 打开文件, 得到文件的打开路径(opened path)
3. 拿opened path去EG(included_files)查找, 是否存在, 如果存在则返回, 不存在继续
4. 编译文件(compile_file)

这个在大多数情况下, 不是问题, 然而问题出在当你使用APC的时候…

在使用APC的时候, APC劫持了compile_file这个编译文件的指针, 从而直接从cache中得到编译结果, 避免了对实际文件的open, 避免了对open的system call.

然而, 当你在代码中使用include_once的时候, 在compile_file之前, PHP已经尝试去open file了, 然后才进入被APC劫持的compile file中, 这样一来, 就会产生一次额外的open操作. 而APC正是为了解决这个问题, 引入了include_once_override, 在include_once_override开启的情况下, APC会劫持PHP的ZEND_INCLUDE_OR_EVAL opcode handler, 通过stat来确定文件的绝对路径, 然后如果发现没有被加载, 就改写opcode为include, 做一个tricky解决方案.

但是, 很可惜, 如我所说, APC的include_once_override实现的一直不好, 会有一些未定义的问题, 比如:

    <?php
    set_include_path("/tmp");
    function a($arg = array()) {
        include_once("b.php");
    }    a();
    a();
    ?>

然后, 我们的b.php放置在”/tmp/b.php”, 内容如下:
    <?php
      class B {}
    ?>

那么在打开apc.include_once_override的情况下, 连续访问就会得到如下错误:
Fatal error - include() : Cannot redeclare class

排除这些技术因素, 我也一直认为, 我们应该使用include, 而不是include_once, 因为我们完全能做到自己规划, 一个文件只被加载一次. 还可以借助自动加载, 来做到这一点.

你使用include_once,只能证明, 你对自己的代码没信心.
所以, 建议大家, 不要再使用include_once

PHP 相关文章推荐
用PHP生成html分页列表的代码
Mar 18 PHP
关于使用key/value数据库redis和TTSERVER的心得体会
Jun 28 PHP
php实现把数组按指定的个数分隔
Feb 17 PHP
PHP中开启gzip压缩的2种方法
Jan 31 PHP
php根据日期显示所在星座的方法
Jul 13 PHP
PHP封装的MSSql操作类完整实例
May 26 PHP
php修改数组键名的方法示例
Apr 15 PHP
PHP分页显示的方法分析【附PHP通用分页类】
May 10 PHP
PHP应用跨时区功能的实现方法
Mar 21 PHP
TP3.2.3框架使用CKeditor编辑器在页面中上传图片的方法分析
Dec 31 PHP
PHP操作Redis常用命令的实例详解
Dec 23 PHP
TP - 比RBAC更好的权限认证方式(Auth类认证)
Mar 09 PHP
解析PHP中的unset究竟会不会释放内存
Jul 18 #PHP
解析php中curl_multi的应用
Jul 17 #PHP
php curl获取网页内容(IPV6下超时)的解决办法
Jul 16 #PHP
ie与session丢失(新窗口cookie丢失)实测及解决方案
Jul 15 #PHP
实测在class的function中include的文件中非php的global全局环境
Jul 15 #PHP
Php output buffering缓存及程序缓存深入解析
Jul 15 #PHP
PHP 转义使用详解
Jul 15 #PHP
You might like
玩转虚拟域名◎+ .
2006/10/09 PHP
ThinkPHP3.2.3数据库设置新特性
2015/03/05 PHP
php中array_fill函数的实例用法
2021/03/02 PHP
用js重建星际争霸
2006/12/22 Javascript
Javascript 函数对象的多重身份
2009/06/28 Javascript
在chrome中window.onload事件的一些问题
2010/03/01 Javascript
一个简单的网站访问JS计数器 刷新1次加1次访问
2012/09/20 Javascript
JS和jquery获取各种屏幕的宽度和高度的代码
2013/08/02 Javascript
JQuery打造省市下拉框联动效果
2014/05/18 Javascript
JS获得选取checkbox整行数据的方法
2015/01/28 Javascript
JavaScript中数组的合并以及排序实现示例
2015/10/24 Javascript
理解JavaScript中Promise的使用
2016/01/18 Javascript
js 基础篇必看(点击事件轮播图的简单实现)
2016/08/20 Javascript
js闭包用法实例详解
2016/12/13 Javascript
Three.js基础部分学习
2017/01/08 Javascript
详解js前端代码异常监控
2017/01/11 Javascript
[02:05]2014DOTA2西雅图邀请赛 老队长全明星大猜想谁不服就按进显示器
2014/07/08 DOTA
python概率计算器实例分析
2015/03/25 Python
Python3操作SQL Server数据库(实例讲解)
2017/10/21 Python
微信跳一跳python代码实现
2018/01/05 Python
50行Python代码实现人脸检测功能
2018/01/23 Python
pandas 使用apply同时处理两列数据的方法
2018/04/20 Python
python3使用smtplib实现发送邮件功能
2018/05/22 Python
Python pandas用法最全整理
2019/08/04 Python
Django之PopUp的具体实现方法
2019/08/31 Python
10个Python面试常问的问题(小结)
2019/11/20 Python
python list数据等间隔抽取并新建list存储的例子
2019/11/27 Python
python3.x 生成3维随机数组实例
2019/11/28 Python
Python实现投影法分割图像示例(二)
2020/01/17 Python
python3正则模块re的使用方法详解
2020/02/11 Python
印尼披萨外送专家:Domino’s Pizza印尼
2017/12/28 全球购物
世界上最大的铁人三项商店:Tri UK
2020/11/04 全球购物
教师业务培训方案
2014/05/01 职场文书
政府会议通知范文
2015/04/15 职场文书
小学运动会报道稿
2015/07/22 职场文书
2016公司中秋节寄语
2015/12/07 职场文书