強(qiáng)烈聲明: 不要使用(include/require)_once
關(guān)于使用include還是include_once(以下,都包含require_once), 這個(gè)討論很長(zhǎng)了, 結(jié)論也一直有, 就是盡量使用include, 而不是include_once, 以前最多的理由的是, include_once需要查詢一遍已加載的文件列表, 確認(rèn)是否存在, 然后再加載.
誠(chéng)然, 這個(gè)理由是對(duì)的, 不過(guò), 我今天要說(shuō)的, 是另外一個(gè)的原因.
我們知道, PHP去判斷一個(gè)文件是否被加載, 是需要得到這個(gè)文件的opened_path的, 意思是說(shuō), 比如:
<?php
set_include_path("/tmp/:/tmp2/");
include_once("2.php");
?>
當(dāng)PHP看到include_once “2.php”的時(shí)候, 他并不知道這個(gè)文件的實(shí)際路徑是什么, 也就無(wú)法從已加載的文件列表去判斷是否已經(jīng)加載, 所以在include_once的實(shí)現(xiàn)中, 會(huì)首先嘗試解析這個(gè)文件的真實(shí)路徑(對(duì)于普通文件這個(gè)解析僅僅類似是檢查getcwd和文件路徑, 所以如果是相對(duì)路徑, 一般是不會(huì)成功), 如果解析成功, 則查找EG(include_files), 如果存在則說(shuō)明包含過(guò)了, 返回, 否則open這個(gè)文件, 從而得到這個(gè)文件的opened_path. 比如上面的例子, 這個(gè)文件存在于 “/tmp2/2.php”.
然后, 得到了這個(gè)opened_path以后, PHP去已加載的文件列表去查找, 是否已經(jīng)包含, 如果沒(méi)有包含, 那么就直接compile, 不再需要open file了.
1. 嘗試解析文件的絕對(duì)路徑, 如果能解析成功, 則檢查EG(included_files), 存在則返回, 不存在繼續(xù)
2. 打開文件, 得到文件的打開路徑(opened path)
3. 拿opened path去EG(included_files)查找, 是否存在, 如果存在則返回, 不存在繼續(xù)
4. 編譯文件(compile_file)
這個(gè)在大多數(shù)情況下, 不是問(wèn)題, 然而問(wèn)題出在當(dāng)你使用APC的時(shí)候…
在使用APC的時(shí)候, APC劫持了compile_file這個(gè)編譯文件的指針, 從而直接從cache中得到編譯結(jié)果, 避免了對(duì)實(shí)際文件的open, 避免了對(duì)open的system call.
然而, 當(dāng)你在代碼中使用include_once的時(shí)候, 在compile_file之前, PHP已經(jīng)嘗試去open file了, 然后才進(jìn)入被APC劫持的compile file中, 這樣一來(lái), 就會(huì)產(chǎn)生一次額外的open操作. 而APC正是為了解決這個(gè)問(wèn)題, 引入了include_once_override, 在include_once_override開啟的情況下, APC會(huì)劫持PHP的ZEND_INCLUDE_OR_EVAL opcode handler, 通過(guò)stat來(lái)確定文件的絕對(duì)路徑, 然后如果發(fā)現(xiàn)沒(méi)有被加載, 就改寫opcode為include, 做一個(gè)tricky解決方案.
但是, 很可惜, 如我所說(shuō), APC的include_once_override實(shí)現(xiàn)的一直不好, 會(huì)有一些未定義的問(wèn)題, 比如:
<?php
set_include_path("/tmp");
function a($arg = array()) {
include_once("b.php");
}
a();
a();
?>
然后, 我們的b.php放置在”/tmp/b.php”, 內(nèi)容如下:
<?php
class B {}
?>
那么在打開apc.include_once_override的情況下, 連續(xù)訪問(wèn)就會(huì)得到如下錯(cuò)誤:
Fatal error - include() : Cannot redeclare class b
排除這些技術(shù)因素, 我也一直認(rèn)為, 我們應(yīng)該使用include, 而不是include_once, 因?yàn)槲覀兺耆茏龅阶约阂?guī)劃, 一個(gè)文件只被加載一次. 還可以借助自動(dòng)加載, 來(lái)做到這一點(diǎn).
你使用include_once, 只能證明, 你對(duì)自己的代碼沒(méi)信心.
所以, 建議大家, 不要再使用include_once
相關(guān)文章
php實(shí)現(xiàn)快速對(duì)二維數(shù)組某一列進(jìn)行組裝的方法小結(jié)
這篇文章主要介紹了php實(shí)現(xiàn)快速對(duì)二維數(shù)組某一列進(jìn)行組裝的方法,涉及PHP數(shù)組遍歷、轉(zhuǎn)換、拆分等相關(guān)操作技巧,需要的朋友可以參考下2019-12-12PHP實(shí)現(xiàn)操作redis的封裝類完整實(shí)例
這篇文章主要介紹了PHP實(shí)現(xiàn)操作redis的封裝類,以完整實(shí)例形式較為詳細(xì)的分析了PHP操作redis的自定義類及其相關(guān)使用方法,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11使用php來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)服務(wù)
在調(diào)用網(wǎng)絡(luò)服務(wù)的過(guò)程中,需要兩個(gè)消息,發(fā)送的消息和接受的消息,又來(lái)有往方能來(lái)往不是。2009-09-09php中array_column函數(shù)簡(jiǎn)單實(shí)現(xiàn)方法
這篇文章主要介紹了php中array_column函數(shù)簡(jiǎn)單實(shí)現(xiàn)方法,結(jié)合實(shí)例形式簡(jiǎn)單分析了array_column函數(shù)的功能,并針對(duì)低版本的情況給出了array_column函數(shù)的實(shí)現(xiàn)代碼,需要的朋友可以參考下2016-07-07PHP 字符串正則替換函數(shù)preg_replace使用說(shuō)明
PHP 字符串正則替換函數(shù)preg_replace使用說(shuō)明,需要的朋友可以參考下。2011-07-07php讀取富文本的時(shí)p標(biāo)簽會(huì)出現(xiàn)紅線是怎么回事
這篇文章主要介紹了php讀取富文本的時(shí)p標(biāo)簽會(huì)出現(xiàn)紅線是怎么回事,需要的朋友可以參考下2014-05-05