请离开include_once和require_once

2015-01-24信息快讯网

尽量使用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

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

你使用include_once,只能证明, 你对自己的代码没信心.
所以, 建议大家, 不要再使用include_once

PHP header()函数常用方法总结
PHP扩展模块Pecl、Pear以及Perl的区别
php实现httpclient类示例
php使用json_encode对变量json编码
PHP包含文件函数include、include_once、require、require_once区别总结
php警告Creating default object from empty value 问题的解决方法
php环境套包 dedeampz 伪静态设置示例
php的declare控制符和ticks教程(附示例)
php的zip解压缩类pclzip使用示例
php function用法如何递归及return和echo区别
PHP 函数call_user_func和call_user_func_array用法详解
php中require和require_once的区别说明
php中的路径问题与set_include_path使用介绍
PHP中include与require使用方法区别详解
使用PHP编写的SVN类
解析PHP中的unset究竟会不会释放内存
实测在class的function中include的文件中非php的global全局环境
浅析PHP安装扩展mcrypt以及相关依赖项(PHP安装PECL扩展的方法)
浅析PHP中的UNICODE 编码与解码
浅析HTTP消息头网页缓存控制以及header常用指令介绍
php连接函数implode与分割explode的深入解析
解析如何去掉CodeIgniter URL中的index.php
PHP Class&Object -- 解析PHP实现二叉树
PHP Class&Object -- PHP 自排序二叉树的深入解析
解析func_num_args与func_get_args函数的使用
解析php中eclipse 用空格替换 tab键
PHP中mb_convert_encoding与iconv函数的深入解析
Function eregi is deprecated (解决方法)
解析smarty 截取字符串函数 truncate的用法介绍
php include和require的区别深入解析
深入掌握include_once与require_once的区别
强烈声明: 不要使用(include/require)_once
深入理解require与require_once与include以及include_once的区别
©2014-2024 dbsqp.com