PHP 專案在 Windows 開發、Linux 部署時,常遇到 require 'a.php' 實際檔名為 A.php 導致「找不到檔案」的錯誤。根因在於 Windows 檔案系統不分大小寫,Linux 區分大小寫。本文提供一個輕量的跨平台檔案載入器,強制在 Windows 下也進行大小寫比對,讓開發環境盡早發現問題。
// 實際檔案:D:/web/webServer/A.php
// 程式碼寫成:
require 'D:/web/webServer/a.php'; // 小寫
// Windows:正常載入(不分大小寫)
// Linux:報錯 "failed to open stream: No such file or directory"
<?php
/**
* 跨平台安全載入檔案
* @param string $fileName 檔案路徑(相對或絕對)
* @return bool|string 成功回傳真實路徑,失敗回傳 false
*/
function safeRequire(string $fileName) {
if (!is_file($fileName)) {
return false; // 檔案不存在
}
// 只在 Windows 環境做大小寫檢查
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
// realpath() 會回傳檔案系統實際儲存的大小寫路徑
$realPath = realpath($fileName);
// 比較檔名部分(basename)是否完全一致
if (basename($realPath) !== basename($fileName)) {
trigger_error("檔案大小寫不符:實際為 {$realPath},請求為 {$fileName}", E_USER_WARNING);
return false;
}
}
require $fileName;
return true;
}
// ---------- 使用範例 ----------
safeRequire(__DIR__ . '/Config/Database.php'); // 正確大小寫
safeRequire(__DIR__ . '/config/database.php'); // Windows 會警告並拒絕載入
| 函式/常數 | 行為 | 跨平台差異 |
|---|---|---|
PHP_OS | 作業系統識別字串 | Windows 開頭為 WIN(如 WINNT),Linux 為 Linux |
realpath() | 回傳規範化絕對路徑 | Windows 會回傳實際磁碟儲存的大小寫;Linux 同參數 |
basename() | 取得路徑中的檔名部分 | 無差異 |
若專案使用 PSR-4 自動載入,問題通常不會出現,因為 composer.json 定義的命名空間對應目錄結構會強制開發者遵循正確大小寫。但若有舊代碼使用手動 require/include,可建立一個 SafeLoader 類別統一管理:
<?php
class SafeLoader {
public static function load(string $file): bool {
if (!is_file($file)) return false;
if (self::isWindows()) {
$real = realpath($file);
if (basename($real) !== basename($file)) {
// 開發環境拋出例外,正式環境記錄日誌
if (ini_get('display_errors')) {
throw new RuntimeException("檔案大小寫不符:$file (實際: $real)");
}
error_log("檔案大小寫不符:$file (實際: $real)");
return false;
}
}
require $file;
return true;
}
private static function isWindows(): bool {
return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
}
}
// 使用
SafeLoader::load(__DIR__ . '/lib/Utils.php');
PascalCase.php、設定檔 snake_case.php、視圖 kebab-case.php。safeRequire,讓大小寫錯誤在 git commit 前就被攔截。find . -name "*.php" | xargs -I{} sh -c 'test "$(basename $(realpath {}))" = "$(basename {})" || echo "MISMATCH: {}"'。require,從根源消除問題。「Windows 不分大小寫」是歷史包袱,但透過 realpath() 比對檔名,我們可以在開發階段就模擬 Linux 的嚴格行為。建立 SafeLoader 並納入團隊編碼規範,能大幅減少「本機正常、上線炸裂」的部署事故。