请离开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自定义函数返回多个值
Nov 26 PHP
php中使用Akismet防止垃圾评论的代码
Jun 10 PHP
php中计算未知长度的字符串哪个字符出现的次数最多的代码
Aug 14 PHP
php如何调用webservice应用介绍
Nov 24 PHP
基于xcache的配置与使用详解
Jun 18 PHP
PHP jQuery表单,带验证具体实现方法
Feb 15 PHP
php中单个数据库字段多列显示(单字段分页、横向输出)
Jul 28 PHP
PHP实现把文本中的URL转换为链接的auolink()函数分享
Jul 29 PHP
如何使用PHP Embed SAPI实现Opcodes查看器
Nov 10 PHP
网站被恶意镜像怎么办 php一段代码轻松搞定(全面版)
Oct 23 PHP
Laravel 简单实现Ajax滚动加载示例
Oct 22 PHP
php去除deprecated的实例方法
Nov 17 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
php导入大量数据到mysql性能优化技巧
2014/12/29 PHP
mysqli扩展无法在PHP7下升级问题的解决
2019/09/10 PHP
PHP中echo与print区别点整理
2021/03/09 PHP
Javascript实例教程(19) 使用HoTMetal(6)
2006/12/23 Javascript
filemanage功能中用到的common.js
2007/04/08 Javascript
Javascript中的变量使用说明
2010/05/18 Javascript
基于jquery的气泡提示效果
2010/05/31 Javascript
javascript电商网站抢购倒计时效果实现
2015/11/19 Javascript
JS+CSS3模拟溢出滚动效果
2016/08/12 Javascript
JS常见创建类的方法小结【工厂方式,构造器方式,原型方式,联合方式等】
2017/04/01 Javascript
关于定制FileField中的上传文件名称问题
2017/08/22 Javascript
阿里大于短信验证码node koa2的实现代码(最新)
2017/09/07 Javascript
Vue项目中最新用到的一些实用小技巧
2018/11/06 Javascript
详解JavaScript 新语法之Class 的私有属性与私有方法
2019/04/23 Javascript
Element 默认勾选表格 toggleRowSelection的实现
2019/09/04 Javascript
详解Vue template 如何支持多个根结点
2020/02/10 Javascript
es6函数之箭头函数用法实例详解
2020/04/25 Javascript
[00:42]《辉夜杯》—职业组预选赛12月3日15点 正式打响
2015/12/03 DOTA
详解Python中的装饰器、闭包和functools的教程
2015/04/02 Python
Python的Django框架中消息通知的计数器实现教程
2016/06/13 Python
Python多进程池 multiprocessing Pool用法示例
2018/09/07 Python
Django 路由控制的实现代码
2018/11/08 Python
Django中使用haystack+whoosh实现搜索功能
2019/10/08 Python
Python3.7安装keras和TensorFlow的教程图解
2020/06/18 Python
python requests抓取one推送文字和图片代码实例
2019/11/04 Python
Python Handler处理器和自定义Opener原理详解
2020/03/05 Python
Python字典取键、值对的方法步骤
2020/09/30 Python
全球速卖通:AliExpress(国际版淘宝)
2017/09/20 全球购物
销售人员获奖感言
2014/02/05 职场文书
党员创先争优公开承诺书
2014/03/28 职场文书
文化活动实施方案
2014/03/28 职场文书
环保小标语
2014/06/13 职场文书
债务授权委托书范本
2014/10/17 职场文书
2014年机关作风建设工作总结
2014/10/23 职场文书
2015年健康教育工作总结
2015/04/10 职场文书
CSS实现鼠标悬浮动画特效
2023/05/07 HTML / CSS