强烈声明: 不要使用(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 相关文章推荐
php 多线程上下文中安全写文件实现代码
Dec 28 PHP
php类常量的使用详解
Jun 08 PHP
用PHP实现弹出消息提示框的两种方法
Dec 17 PHP
PHP动态编译出现Cannot find autoconf的解决方法
Nov 05 PHP
PHP实现HTTP断点续传的方法
Jun 17 PHP
php语言的7种基本的排序方法
Dec 28 PHP
PHP计算数组中值的和与乘积的方法(array_sum与array_product函数)
Apr 01 PHP
PHP使用PHPExcel删除Excel单元格指定列的方法
Jul 06 PHP
Yii2框架数据验证操作实例详解
May 02 PHP
PHP面向对象类型约束用法分析
Jun 12 PHP
php5与php7的区别点总结
Oct 11 PHP
tp5.0框架隐藏index.php入口文件及模块和控制器的方法分析
Feb 11 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中比较时间大小实例
2014/08/21 PHP
thinkphp中memcache的用法实例
2014/11/29 PHP
Symfony2开发之控制器用法实例分析
2016/02/05 PHP
php自动载入类用法实例分析
2016/06/24 PHP
使用PHP下载CSS文件中的所有图片【几行代码即可实现】
2016/12/14 PHP
wordpress网站转移到本地运行测试的方法
2017/03/15 PHP
php实现微信原生支付(扫码支付)功能
2018/05/30 PHP
javascript入门·对象属性方法大总结
2007/10/01 Javascript
JS面向对象编程之对象使用分析
2010/08/19 Javascript
JavaScript闭包 懂不懂由你反正我是懂了
2011/10/21 Javascript
JavaScript高级程序设计(第3版)学习笔记10 再访js对象
2012/10/11 Javascript
Javascript基础教程之数据类型 (布尔型 Boolean)
2015/01/18 Javascript
使用CDN和AJAX加速WordPress中jQuery的加载
2015/12/05 Javascript
Bootstrap Table使用心得总结
2016/11/29 Javascript
jQuery设置和获取select、checkbox、radio的选中值方法
2017/01/01 Javascript
详解基于webpack和vue.js搭建开发环境
2017/04/05 Javascript
浅析vue深复制
2018/01/29 Javascript
vue项目中应用ueditor自定义上传按钮功能
2018/04/27 Javascript
解决vue 绑定对象内点击事件失效问题
2018/09/05 Javascript
JavaScript多种滤镜算法实现代码实例
2019/12/10 Javascript
[43:36]Liquid vs Mineski 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
Python中datetime模块参考手册
2017/01/13 Python
django上传图片并生成缩略图方法示例
2017/12/11 Python
Python爬虫中urllib库的进阶学习
2018/01/05 Python
Django自定义过滤器定义与用法示例
2018/03/22 Python
Python合并多个Excel数据的方法
2018/07/16 Python
python实现高斯判别分析算法的例子
2019/12/09 Python
python的数学算法函数及公式用法
2020/11/18 Python
CSS3近阶段篇之酷炫的3D旋转透视
2016/04/28 HTML / CSS
使用canvas来完成线性渐变和径向渐变的功能的方法示例
2019/07/25 HTML / CSS
天逸系统(武汉)有限公司Java笔试题
2015/12/29 面试题
品酒会策划方案
2014/05/26 职场文书
青年干部培训班学习心得体会
2016/01/06 职场文书
2016年庆祝六一儿童节活动总结
2016/04/06 职场文书
如何在Mac上通过docker配置PHP开发环境
2021/05/29 PHP
HDFS免重启挂载新磁盘
2022/04/06 Servers