电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

请离开include


发布日期:2019/8/27
 
尽量使用include 而不是include_once 理由是 include_once需要查询一遍已加载的文件列表 确认是否存在 然后再加载

诚然 这个理由是对的 不过 我今天要说的 是另外一个的原因
我们知道 PHP去判断一个文件是否被加载 是需要得到这个文件的opened_path的 意思是说 比如:

复制代码 代码如下:
<?php
set_include_path("/tmp/:/tmp/");
include_once("php");
?>


当 PHP看到include_once “php”的时候 他并不知道这个文件的实际路径是什么 也就无法从已加载的文件列表去判断是否已经加载 所以在include_once的实现中 会首先尝试解析这个文件的真实路径(对于普通文件这个解析仅仅类似是检查getcwd和文件路径 所以如果是相对路径 一般是不会成功) 如果解析成功 则查找EG(include_files) 如果存在则说明包含过了 返回 否则open这个文件 从而得到这个文件的opened_path 比如上面的例子 这个文件存在于 “/tmp/php”

然后 得到了这个opened_path以后 PHP去已加载的文件列表去查找 是否已经包含 如果没有包含 那么就直接compile 不再需要open file了

尝试解析文件的绝对路径 如果能解析成功 则检查EG(included_files) 存在则返回 不存在继续
打开文件 得到文件的打开路径(opened path)
拿opened path去EG(included_files)查找 是否存在 如果存在则返回 不存在继续
编译文件(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("bphp");
}

a();
a();
?>


然后 我们的bphp放置在”/tmp/bphp” 内容如下:

复制代码 代码如下:
<?php
class B {}
?>


那么在打开apcinclude_once_override的情况下 连续访问就会得到如下错误:
Fatal error include() : Cannot redeclare class

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

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

上一篇:如何批量替换相对地址为绝对地址

下一篇:ie与session丢失(新窗口cookie丢失)实测及解决方案