强烈声明: 不要使用(include/require)_once


Posted in PHP onJune 06, 2013

关于使用include还是include_once(以下,都包含require_once), 这个讨论很长了, 结论也一直有, 就是尽量使用include, 而不是include_once, 以前最多的理由的是, include_once需要查询一遍已加载的文件列表, 确认是否存在, 然后再加载.

诚然, 这个理由是对的, 不过, 我今天要说的, 是另外一个的原因.

我们知道, 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 b

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

你使用include_once, 只能证明, 你对自己的代码没信心.

所以, 建议大家, 不要再使用include_once

PHP 相关文章推荐
sphinx增量索引的一个问题
Jun 14 PHP
php更新mysql后获取影响的行数发生异常解决方法
Mar 28 PHP
PHP 验证码不显示只有一个小红叉的解决方法
Sep 30 PHP
phplot生成图片类用法详解
Jan 06 PHP
PHP实现对png图像进行缩放的方法(支持透明背景)
Jul 15 PHP
PHP入门教程之数组用法汇总(创建,删除,遍历,排序等)
Sep 11 PHP
php中static 静态变量和普通变量的区别
Dec 01 PHP
php学习笔记之mb_strstr的基本使用
Feb 03 PHP
PHP+redis实现的悲观锁机制示例
Jun 12 PHP
php+croppic.js实现剪切上传图片功能
Aug 14 PHP
Laravel第三方包报class not found的解决方法
Oct 13 PHP
PHP中关于php.ini参数优化详解
Feb 28 PHP
探讨PHP调用时间格式的参数详解
Jun 06 #PHP
探讨多键值cookie(php中cookie存取数组)的详解
Jun 06 #PHP
深入密码加salt原理的分析
Jun 06 #PHP
深入理解PHP几个算法:PHP冒泡、PHP二分法、PHP求素数、PHP乘法表
Jun 06 #PHP
php定时计划任务的实现方法详解
Jun 06 #PHP
PHP使用DES进行加密与解密的方法详解
Jun 06 #PHP
php xml常用函数的集合(比较详细)
Jun 06 #PHP
You might like
PHP生成和获取XML格式数据的方法
2016/03/04 PHP
Aliyun Linux 编译安装 php7.3 tengine2.3.2 mysql8.0 redis5的过程详解
2020/10/20 PHP
不用写JS也能使用EXTJS视频演示
2008/12/29 Javascript
jquery 简短几句代码实现给元素动态添加及获取提示信息
2011/09/01 Javascript
对Jquery中的ajax再封装,简化操作示例
2014/02/12 Javascript
解决ueditor jquery javascript 取值问题
2014/12/30 Javascript
jquery+php随机生成红包金额数量代码分享
2015/08/27 Javascript
基于JavaScript实现 网页切出 网站title变化代码
2016/04/03 Javascript
AngularJS 指令的交互详解及实例代码
2016/09/14 Javascript
微信小程序 小程序制作及动画(animation样式)详解
2017/01/06 Javascript
Bootstrap导航条学习使用(一)
2017/02/08 Javascript
webpack 处理CSS资源的实现
2019/09/27 Javascript
vue如何实现动态加载脚本
2020/02/05 Javascript
koa-passport实现本地验证的方法示例
2020/02/20 Javascript
在Vuex中Mutations修改状态操作
2020/07/24 Javascript
Vue+element+cookie记住密码功能的简单实现方法
2020/09/20 Javascript
Python SqlAlchemy动态添加数据表字段实例解析
2018/02/07 Python
基于python实现简单日历
2018/07/28 Python
Python使用try except处理程序异常的三种常用方法分析
2018/09/05 Python
对于Python深浅拷贝的理解
2019/07/29 Python
python实现按关键字筛选日志文件
2019/12/24 Python
Pytorch之contiguous的用法
2019/12/31 Python
Python matplotlib绘制图形实例(包括点,曲线,注释和箭头)
2020/04/17 Python
Python Web项目Cherrypy使用方法镜像
2020/11/05 Python
Python实现邮件发送的详细设置方法(遇到问题)
2021/01/18 Python
英国知名的皮手套品牌:Dents
2016/11/13 全球购物
PHP如何防止SQL注入
2014/05/03 面试题
生产部经理岗位职责
2013/12/16 职场文书
保密工作责任书
2014/04/16 职场文书
小学生安全演讲稿
2014/04/25 职场文书
法定代表人资格证明书
2014/09/11 职场文书
优秀护士事迹材料
2014/12/25 职场文书
士兵突击观后感
2015/06/16 职场文书
Python自动化测试PO模型封装过程详解
2021/06/22 Python
Mongo服务重启异常问题的处理方法
2021/07/01 MongoDB
html form表单基础入门案例讲解
2021/07/15 HTML / CSS