許多開發者在 require() 或 include() 時遇到「找不到檔案」錯誤,根因在於 PHP 的包含路徑是相對於「最初執行的腳本(入口檔案)」而非「目前被包含的檔案」。本文透過具體範例徹底釐清此機制。
網站根目錄/
├── a.php
├── b/
│ └── b.php
├── c/
│ └── c.php
└── d/
└── d.php
包含關係: a.php、b/b.php 皆包含 c/c.php → c/c.php 再包含 d/d.php
<?php
$file_name = 'a.php';
echo "this is a.php";
echo "<hr>";
require('c/c.php');
?>
<?php
$c_file_name = 'c.php';
echo 'this is c.php, is required by ' . $file_name;
echo "<hr>";
require('../d/d.php'); // 錯誤寫法!
?>
直覺上 c.php 在 c/ 目錄,上一層是根目錄,再進 d/ 應寫 ../d/d.php。但執行 a.php 時會報錯 「failed to open stream: No such file or directory」。
被包含檔案中的相對路徑,基準點永遠是「最外層執行腳本(入口檔)所在目錄」。
可將 require('c/c.php') 視為將 c.php 程式碼直接「內嵌」到 a.php 該行位置。因此 c.php 內的 require('../d/d.php') 實際解析為:
<?php
$file_name = 'a.php';
echo "this is a.php";
echo "<hr>";
// require('c/c.php'); <-- 視為展開
$c_file_name = 'c.php';
echo 'this is c.php, is required by ' . $file_name;
echo "<hr>";
require('../d/d.php'); // 相對於 a.php 所在目錄(根目錄)
?>
根目錄下並無 ../d/d.php(上一層不存在),故失敗。
// c/c.php
require(dirname(__FILE__) . '/../d/d.php');
// 或 PHP 5.3+
require(__DIR__ . '/../d/d.php');
// 入口檔 a.php
define('APP_ROOT', __DIR__);
// c/c.php
require(APP_ROOT . '/d/d.php');
// 入口檔
set_include_path(get_include_path() . PATH_SEPARATOR . __DIR__);
// 之後所有包含皆可直接用檔名
require('d/d.php');
__DIR__ 或專案常數建構絕對路徑,消除相對路徑歧義。BASE_PATH、APP_PATH 等常數,全專案統一引用。require_once、include_once 同樣遵循此規則。autoload 機制(PSR-4)透過命名空間對應目錄,從根本避免路徑問題。