PHP 的 include 暗藏了一些潛規則,如果沒搞懂的話,遇到問題會以為見鬼了。所以這篇主要就在研究 PHP 的 include 運作方式。
首先,我們從官方文件可以看到 include 會區分為兩種模式,一種是 include 檔案,另一種是 include 路徑。只要是絕對路徑,或是相對路徑,就是以 include 路徑來運作。這裡比較容易搞混的是相對路徑的部份,因為 PHP 只把 “.” 或 “..” 開頭的作為相對路徑看待。
因此 include “foo/bar.php” 是屬於 include 檔案,而 include “./foo/bar.php” 則是 include 路徑。
在 include 檔案時,會先從 include_path 開始搜尋,再來是搜尋此 PHP 程式所在的資料夾(the calling script’s own directory),最後是搜尋 current working directory(CWD)。
而在 include 路徑時,則只會從 CWD 去搜尋。
為了驗證,我設置了這樣的環境來作測試:
1 2 3 4 5 6 7 |
/calling/include_priority.php /calling/library/libxx.php /calling/library/libyy.php /cwd/library/libxx.php /cwd/library/libzz.php /cwd/library/libyy.php /inc_path/library/libxx.php |
其中 lib{xx,yy,zz}.php 的內容都是一樣的:
1 2 |
<?php echo "included " , __FILE__, "\n"; |
include_priority.php 是將要執行的主程式,分別將 include_path 以及 CWD 設置到 inc_path 與 cwd 資料夾,這樣就能分辨優先順序,其內容是:
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php set_include_path(__DIR__ . "/../inc_path"); chdir("../cwd"); include "library/libxx.php"; include "library/libyy.php"; include "library/libzz.php"; echo "------\n"; include "./library/libxx.php"; include "./library/libyy.php"; include "./library/libzz.php"; |
最終結果是這樣子:
1 2 3 4 5 6 7 |
/inc_path/library/libxx.php /calling/library/libyy.php /cwd/library/libzz.php ------ /cwd/library/libxx.php /cwd/library/libyy.php /cwd/library/libzz.php |
由結果來看,符合文件中所描述的運作方式。
接著來驗證第二種情況,如果 a.php include b.php 且 b.php 又 include c.php 的話,那麼在排除 include_path 與 CWD 的情況下,會如何運作?
環境設置:
1 2 3 4 |
/library/libxx.php /root.php /sub/sub.php /sub/library/libxx.php |
root.php 是主程式,內容為:
1 2 3 |
<?php set_include_path("/no/such/dir"); include "sub/sub.php"; |
sub.php 的內容:
1 2 |
<?php include "library/libxx.php"; |
libxx.php 的內容與前一個實驗相同。
最後的輸出結果是:
1 |
included /sub/library/libxx.php |
由此可見程式所在的資料夾(非CWD)是會隨著 include 所在的檔案而變動的。換句話說,就是以這一行 include 語法所在的檔案的位置為主。
綜合以上的內容,就可以解釋下面這個看到鬼的例子了:
1 2 3 4 |
/root.php /sub/sub.php /sub/library/libxx.php /tools.php |
在 root.php 裡面 include 了 sub.php 檔案:
1 2 |
<?php include "sub/sub.php"; |
然後在 sub.php 裡面又分別 include libxx.php 與 tools.php 兩個檔案:
1 2 3 |
<?php include "library/libxx.php"; include "../tools.php"; |
因為 include “library/libxx.php” 成功了,於是以為相對路徑是由 sub.php 所在位置來計算,因此又下了 include “../tools.php” 這樣的指令,結果死的不明不白。
其實 libxx.php 是以檔案的方式去 include 的,所以會搜尋三個地方:include_path,sub.php 所在的位置,以及 CWD。而 sub.php 所在的位置剛好就可以找到 library/libxx.php 這檔案,所以 include 成功。
在 include tools.php 時則是因為它以 “..” 開頭,所以只會從 CWD 來計算相對路徑找檔案。而此時的 CWD 是在 root.php 那邊,自然就找不到東西了。