- 本文地址: https://www.laruence.com/2012/09/12/2765.html
- 转载请注明出处
最近关于apc.include_once_override的去留, 我们做了几次讨论, 这个APC的配置项一直一来就没有被很好的实现过.
在这里, 我想和大家在此分享下, 这个问题的原因, 以及对我们的一些启示.
关于使用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
(后记 2012-09-15 02:07:20: 这个APC的bug我已经修复: #63070)
排除这些技术因素, 我也一直认为, 我们应该使用include, 而不是include_once, 因为我们完全能做到自己规划, 一个文件只被加载一次. 还可以借助自动加载, 来做到这一点.
你使用include_once, 只能证明, 你对自己的代码没信心.
所以, 建议大家, 不要再使用include_once
[…] 再一次, 不要使用(include/require)_once […]
[…] 再一次, 不要使用(include/require)_once […]
话说,如果是老的代码怎么办,要改成include/require吗
[…] 建议不要在代码中使用include_once()和require_once(),具体原因请参考鸟哥的文章《再一次, 不要使用(include/require)_once》。 […]
[…] 鸟哥在其博客中就多次声明,尽量不要用require_once和include_once。 […]
在include_once进程描述中:为什么我必须去include_files再次找到它?
[…] 鸟哥在其博客中就多次声明,尽量不要用require_once和include_once。 […]
[…] 鸟哥在其博客中就多次声明,尽量不要用require_once和include_once。 […]
[…] 鸟哥在其博客中就多次声明,尽量不要用require_once和include_once。 […]
[…] 鸟哥在其博客中就多次声明,尽量不要用require_once和include_once。 […]
[…] 鸟哥在其博客中就多次声明,尽量不要用require_once和include_once。 […]
[…] 1、不使用include(require)_once。原因见鸟哥博客。 2、引入没有条件限制必须的文件用require,放在程序的最前面;引入非必须的文件用include。(必须的标准就是文件存在与否决定程序启停) […]
[…] 鸟哥在其博客中就多次声明,尽量不要用require_once和include_once。 […]
[…] 鸟哥在其博客中就多次声明,尽量不要用require_once和include_once。 […]
[…] 本文地址: http://www.laruence.com/2012/09/12/2765.html […]
[…] 鸟哥在其博客中就多次声明,尽量不要用require_once和include_once。 […]
现在基本都不用这个了吧
时过境迁,现在都用 composer 自动加载
請問, 對於現在的opcache, 是否還不建議使用include/require once?
謝謝.
Construction of the trunk is very good.
Cheap Lebron XI Shoes http://www.predaptive.net/m568/
随着项目的复杂化,开发者进入了一个不断埋坑又填坑的循环。结果是能力没有提高,却花费大量的时间在查找上。看看现在网上的一些所谓开源PHP商城的代码,那个渣的程度已经不能直视了。
APC可以在PHP5.5下跑吗?
接上贴,补充几点:
1、如果能保证文件只加载一次,那绝对选择 require/inlcude;
2、如果无法保证文件只被加载一次,优先选择 (require/include)_once,因为定义语句也是要执行的,会给变量分配内存,构造数组等。
鸟哥强调的是第一点
首先,总体来说,require_once 肯定要比 require 性能好。
因为 require 某个文件等同于 “编译 + 执行” 这个文件;require_once 避免了对相同文件的重复 “编译” 和 “执行”。
即使使用APC大大降低了“编译”阶段的消耗,但是“执行”阶段依然每次都会做重复工作。
其次,鸟哥说的应该是在PHP5.2之前,require_once 的实现机制不健全,只有当参数传的是绝对路径,才会根据路径去确认该文件是否加载过,否则就会 open 这个文件,这显然很不合理。
PHP5.3之后,开始支持相对路径;
但即使是这种场景,也不是 require 绝对比 require_once 性能好,而只是对那些定义比较多的PHP文件是这样;执行代码比较多的场景就一定不是这样了。
综上所述,require_once 从设计思路上来说是要比 require 性能高的;
具体来说
PHP5.2之前:
1、如果使用的是绝对路径,使用 require_once;
2、如果是相对路径的定义文件,使用了APC,选择 require;没有使用 APC,使用 require_once;
3、如果是相对路径的偏执行文件,使用 require_once
PHP5.3之后:
1、对于定义性的文件,如果用了APC,使用 require_once 性能稍高一些;没有APC,当然是 require_once ;
2、对于有执行代码的文件,当然是 require_once;
差不多就得了,我们的生命可以支持我们做多久的程序开发?无聊不?有限的时间里多陪陪家人吧,至于代码,能跑起来就OK了,纠结一些瓜皮之事,得不偿失!
说的有道理
What’s up, this weekend is pleasant in support
of me, as this time i am reading this great informative article here at my
house.
homepage, Orlando,
[…] ,被PHP开发组的laruence建议避免使用 ————————————————————————— […]
1. 尝试解析文件的绝对路径, 如果能解析成功, 则检查EG(included_files), 存在则返回, 不存在继续
存在则返回的话,那为什么在function中调用include_once(‘/path/to/file.php’)后会导致外部引入同一个文件时失败呢?
function a() {
include_once(‘/path/to/file.php’);
var_dump($var);
}
a(); // output success
include_once(‘/path/to/file.php’);
var_dump($var); // output NULL
“存在则返回”仅仅是标记了下“引入的文件已经存在”然后就不引入了??
樓下說的用命名來載入什麼意思? 會比 include好嗎?
Great article.
有了命名空间,是不是不用考虑include ,require了,自动导入啊
我对鸟哥关于include_once调用过程描述中的第3步不理解:为什么还需要去included_files里查找一次?
还有鸟哥对于include_once过程描述中的第3步不太理解:为什么还需要再去included_files查找一次?
我的理解与之相反,如果把重复文件包含的工作交给程序员去做的话避免不了会建一个类似“已加载”的类的集合。单拿判断“已加载”这段代码来说,我觉得由底层做更合适,毕竟程序员自己写一个的话那么最终是要通过各种解析成opcode,这样的效率应该会比底层的C实现低下。而至于APC对include_once(require_once)造成的影响那就另说了,这个属于APC的实现问题,而不应该把问题归结到对include_once(require_once)的使用上。
一再强调,队友死型不改,对于有洁癖的程序员来说有时真心累 。。
也许可以给require_once加上一个参数,限制open file的频率。
怎么感觉APC用了好多HACK的手段。。。
其实有时候并不是对自己的代码没有信心,而是在接手别人的代码的时候,为了安全起见,不得已而为之
Khmer Comedy » Somnangblogs I was proposed this internet site by my own cousin. I am not sure whether this post is published by him since nobody in addition know this sort of detailed with regards to my trouble. You’re incredible! Thanks! your write-up about Khmer Humourous » Somnangblogs All the best Lisa Veronica
[…] 再一次, 不要使用(include/require)_once […]
[…] 本文地址: http://www.laruence.com/2012/09/12/2765.html […]
之所以有once的主要原因是PHP在早期的开发实践中对开发者的约束很少项目又相对简单。
当引入了面向对象后虽然有了自动加载的机制但还是依耐开发人员的的实现而且对早期函数式编程并没有改进和支持。对很多开发者来说这不能不说是个负担,实际上现在的PHP已经没有刚开始时的便捷轻便的特点开始变得复杂而有点脆弱。随着项目的复杂化对开发者的要求就进入一个陡峭提高的过程,当项目在你眼里慢慢走向靠拼凑来维持的时候,当你周围的人靠_once来保证不重复加载你又无能为力的时候不得不说对这种语言充满的是怎么样的感觉。
[…] 本文地址: http://www.laruence.com/2012/09/12/2765.html […]
[…] require和require_once的区别,require包含一次文件,如果在后面的编程中 再次包含同一个文件,PHP还是会去查找文件,解析文件,执行文件,require_once 包含一次文件,在后面编程中如果再包含已经require_once的文件则会被忽略,不会再查找文件而是直接到已包含的内存信息中读取. 个人觉得在全局性质的地方一般可以使用require_once,较个性化的地方使用require,全局性的文件在多人开发的过程中可能会被包含多次,个人个性化的地方一般使用require,自己的程序 一般人都知道自己包含了哪些文件吧.php牛人laruence 推荐在使用apc缓存的情况下使用include或require,不推荐 _once操作. http://www.laruence.com/2012/09/12/2765.html […]
新的项目都应该用自动加载了,再到处写include只能说明很久没学习了,只是老项目用APC可能要注意点儿
这不是一般人要考虑的
看到这句话比较经典: 你使用include_once, 只能证明, 你对自己的代码没信心.
顺便山寨一下经典: 你不使用绝对路径做include, 只能证明, 你对自己代码所在的环境不了解.
APC是什么 本人愚钝
require_once 还是有必要的,当绝对路径加载文件时,查询一遍已加载的文件列表时间很短,而加载PHP文件对象体积很大耗时间时候, 觉得require_once优于require。
[…] 再一次, 不要使用(include/require)_once […]
[…] 原文引用自:http://www.laruence.com/2012/09/12/2765.html 此条目发表在 PHP 分类目录,贴了 include, include_once, require, require_once 标签。将固定链接加入收藏夹。 ← CodeIgniter(CI)发邮件长标题中文乱码解决方案 […]
被震撼了,用了几年的include_once了,没出现过您说的这个问题。 存在的即是合理的吧。
技术的确很重要,但是不要把技术神化。
我也想说,看样子应该是apc的问题,不用apc就行了嘛
用 require 代替include 。。
原来只知道不推荐用once,因为要多做次判断,但看了之后了解的更多了
这个理由实在是太牵强。
第一个, include 比include_once 快,快多少?几毫秒吧。
第二个, 这应该是APC的错,关include_once什麽事。
include_once 使用绝对路径名时比include还快,
请看这两个讨论,
http://drupal.org/node/259623,
http://blog.seeit.org/2010/06/php-the-include-include_once-performance-debate/
这个是在实际应用的讨论。
ZendFrame work, drupal, Sugarcrm, WordPress,symfony framework, 这些流行的PHP 应用都采用require_once/include_once
” 排除这些技术因素, 我也一直认为, 我们应该使用include, 而不是include_once, 因为我们完全能做到自己规划, 一个文件只被加载一次. 还可以借助自动加载, 来做到这一点”
对于一个程序员来说,能偷懒,为什麽不偷懒(利用工具提高效率)?要是什麽都要自己规划,干脆自己造CPU得了。
我能说我严重同意么。
千万别用include_once、require_once
呃,刚刚的代码被过滤了。。。
function myrequire($file)
{
static $loaded = array();
if ( in_array($file, $loaded) ) return;
$loaded[] = $file;
return require($file);
}
一直使用下面的代码来替代require_once
当项目比较复杂又团队水平参差不齐时,once还是必要的吧。
[…] 原文地址: http://www.laruence.com/2012/09/12/2765.html […]
收到。以后会注意
关于 once 理论上 不会用到 实质上 存在即合理
因为有很多 作者开源代码 思维 确实磕碜
比如 phpcms 生成的时候 竟然 不断得去
foreach($a as $b){
include_once(“b.php”);
}
不得不用 include_once
[…] 本文地址: http://www.laruence.com/2012/09/12/2765.html […]
[…] 风雪之隅 » PHP应用 Posted in: php / Tagged: 不要使用include/require_once, 再一次 […]
学习啦。虽然不太懂呢。
include_once_override没有实现好,不应该成为不使用(include/require)_once, 这是include_once_override的问题呀.
博主应该只能是: 建议include_once_override和once不要同时使用.
PHP本身在模块化和命名空间方面支持就不强, 完全靠autoload基本很难解决.
如果不使用只会让PHP只适用于做一些小站了.
如果使用Zend autoload呢?
补充:
开启apc.include_once_override后,我遇到的问题是:第一次请求有输出;第二次无响应,服务器端终止服务,后台没有错误日志。
Chrome
Error 324 (net::ERR_EMPTY_RESPONSE): The server closed the connection without sending any data.
同样的代码我这里没有看到警告信息。
PHP 5.3.2-1ubuntu4.17 with Suhosin-Patch (cli) (built: Jun 19 2012 01:35:33)
APC Version: 3.1.3p1
apc.enable_cli=On
apc.include_once_override=On
这会造成性能的下降吗?还是无法说服我不用的原因。
这个有点因噎废食了
我觉得大部分使用这个include_once就是懒,懒得去判断。
其实,如果一个项目是一个人开发,一个人维护,那么确实可以完全避免使用这个,如果要是一个团多多个人交叉开发,或者一段时间之后,换其他人维护,尤其是维护者的水平高低不同的时候,这个include_once的优势就出现了,毕竟很多较大的、文件较多大一套程序,很多文件都是包来包去的,想知道一个文件是否被包过,其实是很繁琐甚至是很痛苦的。有了这个语句,可就轻松多了。
因人而异,因地制宜,根据情况酌情选择吧。
先mark下,找个机会测试下
同意 @江湖大虾仁,很多项目是多人负责,而像yii框架的import(也是数组判断)好像很慢,特别是在循环使用某个类,每次都要遍历下import数组,一般都是用include/require_once解决
哈哈 一直不用 once
good~~mark了
鸟哥转载署名了。
[…] 原文:http://www.laruence.com/2012/09/12/2765.html […]
留名。。。
受教了。。
路过。。。
学习了,很好。
我依然不是非常理解为什么不用。最好的情况当然是通过合理的架构来避免重复加载,如果只是靠自己维护一个数组来判断是否加载过的话一个没有include_once快吧。我觉得 pysche 说得对,有的时候自己没法掌控整个项目的时候只能用include_once这么写。
同意修改once的机制或apc的对once的机制.
鸟哥…第三行的第二个include拼错了….额
@zzjin 哈哈, 已经更正, 我是别字大王….:)
然而问题出在当你使用APC的时候
也就是说我的程序没有使用 apc include_once就没问题?
这个对于自己有能力管控的代码是完全没问题的,可是像某些项目,个人觉得就很难。比如Wordpress的插件之类的,各个插件由不同的人开发,水平也不尽相同
学习了,这么靠前.
受教了,鸟哥
这只能说明应该更新 require_once 的机制或者APC。
include_once 使用绝对路径,是不是就可以避免举例中那个问题了?
鸟哥,受教了!
看完了,也懂了很多,还是看鸟哥的博客学习的快
一直不适用include/require_once 看来是明智的