请离开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 相关文章推荐
在IIS上安装PHP4.0正式版
Oct 09 PHP
PHP无限分类代码,支持数组格式化、直接输出菜单两种方式
May 18 PHP
php截取后台登陆密码的代码
May 05 PHP
php对二维数组进行相关操作(排序、转换、去空白等)
Nov 04 PHP
浅谈PHP检查数组中是否存在某个值 in_array 函数
Jun 13 PHP
thinkPHP基于ajax实现的菜单与分页示例
Jul 12 PHP
PHP中SQL查询语句的id=%d解释(推荐)
Dec 10 PHP
php分页查询mysql结果的base64处理方法示例
May 18 PHP
PHP流Streams、包装器wrapper概念与用法实例详解
Nov 17 PHP
ThinkPHP框架结合Ajax实现用户名校验功能示例
Jul 03 PHP
php7 图形用户界面GUI 开发示例
Feb 22 PHP
TP5框架实现一次选择多张图片并预览的方法示例
Apr 04 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会员权限控制实现原理分析
2011/05/29 PHP
php学习之 循环结构实现代码
2011/06/09 PHP
使用gd库实现php服务端图片裁剪和生成缩略图功能分享
2013/12/25 PHP
js本身的局限性 别让javascript做太多事
2010/03/23 Javascript
js判断浏览器是否支持html5
2014/08/17 Javascript
jQuery实现在下拉列表选择时获取json数据的方法
2015/04/16 Javascript
js实现图片轮播效果
2015/12/19 Javascript
JQuery用户名校验的具体实现
2016/03/18 Javascript
AngularJS 模型详细介绍及实例代码
2016/07/27 Javascript
jQuery实现点击后高亮背景固定显示的菜单效果【附demo源码下载】
2016/09/21 Javascript
详解Vue整合axios的实例代码
2017/06/21 Javascript
JS实现的加减乘除四则运算计算器示例
2017/08/09 Javascript
vue移动UI框架滑动加载数据的方法
2018/03/12 Javascript
微信小程序中使用ECharts 异步加载数据实现图表功能
2018/07/13 Javascript
js中apply()和call()的区别与用法实例分析
2018/08/14 Javascript
让 babel webpack vue 配置文件支持智能提示的方法
2019/06/22 Javascript
微信公众号平台接口开发 菜单管理的实现
2019/08/14 Javascript
nodejs和react实现即时通讯简易聊天室功能
2019/08/21 NodeJs
JavaScript实现简单动态表格
2020/12/02 Javascript
[02:40]DOTA2英雄基础教程 先知
2013/11/29 DOTA
Python 专题五 列表基础知识(二维list排序、获取下标和处理txt文本实例)
2017/03/20 Python
python出现&quot;IndentationError: unexpected indent&quot;错误解决办法
2017/10/15 Python
python 实现调用子文件下的模块方法
2018/12/07 Python
详解Numpy中的数组拼接、合并操作(concatenate, append, stack, hstack, vstack, r_, c_等)
2019/05/27 Python
Python企业编码生成系统之系统主要函数设计详解
2019/07/26 Python
通过字符串导入 Python 模块的方法详解
2019/10/27 Python
使用python 将图片复制到系统剪贴中
2019/12/13 Python
python实现滑雪游戏
2020/02/22 Python
详解使用python爬取抖音app视频(appium可以操控手机)
2021/01/26 Python
毕业求职自荐信格式是什么
2013/11/19 职场文书
售后专员岗位职责
2013/12/08 职场文书
教室布置标语
2014/06/26 职场文书
房贷工资证明范本
2015/06/12 职场文书
幼儿园奖惩制度范本
2015/08/05 职场文书
Android开发实现极为简单的QQ登录页面
2022/04/24 Java/Android
全网非常详细的pytest配置文件
2022/07/15 Python