盡量使用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("b
php");
}
a();
a();
?>
然後 我們的bphp放置在”/tmp/bphp” 內容如下:
復制代碼 代碼如下:
<?php
class B {}
?>
那麼在打開apcinclude_once_override的情況下 連續訪問就會得到如下錯誤:
Fatal error include() : Cannot redeclare class
排除這些技術因素 我也一直認為 我們應該使用include 而不是include_once 因為我們完全能做到自己規劃 一個文件只被加載一次 還可以借助自動加載 來做到這一點
你使用include_once只能證明 你對自己的代碼沒信心
所以 建議大家 不要再使用include_once
From:http://tw.wingwit.com/Article/program/PHP/201311/20878.html