请离开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 相关文章推荐
文件上传类
Oct 09 PHP
做个自己站内搜索引擎
Oct 09 PHP
php读取msn上的用户信息类
Dec 05 PHP
php一句话cmdshell新型 (非一句话木马)
Apr 18 PHP
PHP程序开发范例学习之表单 获取文本框的值
Aug 08 PHP
php $_SERVER windows系统与linux系统下的区别说明
Feb 14 PHP
PHP.ini安全配置检测工具pcc简单介绍
Jul 02 PHP
YiiFramework入门知识点总结(图文教程)
Dec 28 PHP
PHP中的数组处理函数实例总结
Jan 09 PHP
简单PHP会话(session)说明介绍
Aug 21 PHP
通过php动态传数据到highcharts
Apr 05 PHP
php二维码生成以及下载实现
Sep 28 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
中东人咖啡哲学
2021/03/03 咖啡文化
php引用地址改变变量值的问题
2012/03/23 PHP
总结一些PHP中好用但又容易忽略的小知识
2017/06/02 PHP
laravel框架之数据库查出来的对象实现转化为数组
2019/10/23 PHP
javascript基础的动画教程,直观易懂
2007/01/10 Javascript
用JavaScrpt实现文件夹简单轻松加密的实现方法图文
2008/09/08 Javascript
再谈Jquery Ajax方法传递到action(补充)
2014/05/12 Javascript
jquery的ajax异步请求接收返回json数据实例
2014/06/16 Javascript
DWR中各种java方法的调用
2016/05/04 Javascript
关于Function中的bind()示例详解
2016/12/02 Javascript
JavaScript实现的XML与JSON互转功能详解
2017/02/16 Javascript
Bootstrap 表单验证formValidation 实现表单动态验证功能
2017/05/17 Javascript
用最少的JS代码写出贪吃蛇游戏
2018/01/12 Javascript
webpack4与babel配合使es6代码可运行于低版本浏览器的方法
2018/10/12 Javascript
JS散列表碰撞处理、开链法、HashTable散列示例
2019/02/08 Javascript
Vue 实例事件简单示例
2019/09/19 Javascript
jquery 键盘事件 keypress() keydown() keyup()用法总结
2019/10/23 jQuery
微信小程序实现菜单左右联动
2020/05/19 Javascript
[58:32]EG vs Liquid 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
Python中表达式x += y和x = x+y 的区别详解
2017/06/20 Python
Python在for循环中更改list值的方法【推荐】
2018/08/17 Python
Python实现批量执行同目录下的py文件方法
2019/01/11 Python
在python里协程使用同步锁Lock的实例
2019/02/19 Python
Scrapy-Redis结合POST请求获取数据的方法示例
2019/05/07 Python
python实现滑雪者小游戏
2020/02/22 Python
解决jupyter notebook import error但是命令提示符import正常的问题
2020/04/15 Python
英国在线自行车商店:Evans Cycles
2016/09/26 全球购物
元旦晚会邀请函
2014/02/01 职场文书
单位在职证明书
2014/09/11 职场文书
委托书的写法
2014/09/16 职场文书
销售人才自我评价范文
2014/09/27 职场文书
2015年元旦促销方案书
2014/12/09 职场文书
财务统计员岗位职责
2015/04/14 职场文书
JDBC连接的六步实例代码(与mysql连接)
2021/05/12 MySQL
Go语言设计模式之结构型模式
2021/06/22 Golang
详解JAVA的控制语句
2021/11/11 Java/Android