在 C/C++ 裡,可以用 #ifdef/#define/#endif 來做 include guard 避免重複引入,而在 PHP 裡則需要用 include_once()require_once()_once 系列的函式,來避免重複引入。比較麻煩的是,對於 PHP 而言,相對路徑的起點是程式碼執行起點所在的那個檔案的目錄,而不是呼叫 include_once()require_once() 的程式檔案所在的目錄。

舉個例子來說:被執行的 a.php 裡引入了 lib/b.php,然後 lib/b.php 又引入了 c.php,此時,PHP 會到 a.php 所在的目錄尋找 c.php,而非 b.php 所在的目錄尋找。

這個問題,對於 library writer 會產生相當大的困擾,除非使用者將 library 安裝至 PHP 的 library 目錄 (例如在 FreeBSD 下用 ports 安裝 PHP,其 library 目錄即為 /usr/local/lib/php),直接以該目錄為相對路徑起點,才不會有此困擾。

因此,我寫了下面這些 functions 來解決這樣的問題:

////////////////////////////////////////////////////////////////////////
// Hacks for include guards.
//

/**
 * 具備 include guard 功能的 include() 版本。
 *
 * @param caller 請固定傳入 __FILE__。
 * @param path   請傳入欲引入的檔案之相對(於呼叫此函示之檔案的)路徑。
 */
function include_file($caller, $path)
{
    include(realpath(dirname($caller) . '/' . $path));
}

/**
 * 具備 include guard 功能的 include_once() 版本。
 *
 * @param caller 請固定傳入 __FILE__。
 * @param path   請傳入欲引入的檔案之相對(於呼叫此函示之檔案的)路徑。
 */
function include_file_once($caller, $path)
{
        include_once(realpath(dirname($caller) . '/' . $path));
}
/**
 * 具備 include guard 功能的 require() 版本。
 *
 * @param caller 請固定傳入 __FILE__。
 * @param path   請傳入欲引入的檔案之相對(於呼叫此函示之檔案的)路徑。
 */
function require_file($caller, $path)
{
        require(realpath(dirname($caller) . '/' . $path));
}

/**
 * 具備 include guard 功能的 require_once() 版本。
 *
 * @param caller 請固定傳入 __FILE__。
 * @param path   請傳入欲引入的檔案之相對(於呼叫此函示之檔案的)路徑。
 */
function require_file_once($caller, $path)
{
        require_once(realpath(dirname($caller) . '/' . $path));
}