專案未使用 Composer 自動載入,需自行實作簡易模組載入器,支援:
<?php
/**
* 簡易模組自動載入器
* @param string $modulePath 點號分隔的模組路徑 (e.g. 'Core.Database.Connection')
* @param string $extension 檔案副檔名 (預設 '.class.php'),支援 'inc' 簡寫
* @return void
* @throws RuntimeException 找不到檔案
*/
function importModule(string $modulePath, string $extension = '.class.php'): void
{
// 正規化副檔名
$extMap = [
'class' => '.class.php',
'inc' => '.inc.php',
'php' => '.php',
];
$ext = $extMap[$extension] ?? $extension;
if (!str_starts_with($ext, '.')) $ext = '.' . $ext;
// 專案模組根目錄(可從環境變數或設定取得)
$baseDir = __DIR__ . '/modules/';
// 轉換路徑
$filePath = $baseDir . str_replace('.', '/', $modulePath) . $ext;
$filePath = str_replace('//', '/', $filePath); // 防雙斜線
if (!is_file($filePath)) {
throw new RuntimeException("模組檔案不存在: $filePath (來源: $modulePath)");
}
// 載入一次
require_once $filePath;
}
// 註冊為 SPL 自動載入器(可選)
spl_autoload_register(function(string $class) {
try {
// 僅處理專案命名空間
if (str_starts_with($class, 'App\\') || str_starts_with($class, 'Modules\\')) {
$path = __DIR__ . '/' . str_replace('\\', '/', $class) . '.php';
if (is_file($path)) require_once $path;
}
} catch (Throwable) {
// 忽略,讓後續載入器處理
}
});
// 使用範例
importModule('Core.Database.Connection'); // 載入 modules/Core/Database/Connection.class.php
importModule('Utils.Helpers.String', 'inc'); // 載入 modules/Utils/Helpers/String.inc.php
importModule('Services.Payment.Gateway', 'php'); // 載入 modules/Services/Payment/Gateway.php
若需生產級自動載入,建議直接使用 Composer。若不可用,可參考 Composer ClassLoader 精簡版:
class SimplePsr4Loader
{
private array $prefixes = []; // prefix => [baseDirs...]
public function addNamespace(string $prefix, string|array $baseDirs, bool $prepend = false): void
{
$prefix = rtrim($prefix, '\\') . '\\';
$dirs = (array)$baseDirs;
foreach ($dirs as &$dir) $dir = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
if ($prepend) $this->prefixes[$prefix] = array_merge($dirs, $this->prefixes[$prefix] ?? []);
else $this->prefixes[$prefix] = array_merge($this->prefixes[$prefix] ?? [], $dirs);
}
public function register(bool $prepend = false): void
{
spl_autoload_register([$this, 'loadClass'], true, $prepend);
}
public function loadClass(string $class): bool
{
foreach ($this->prefixes as $prefix => $dirs) {
if (!str_starts_with($class, $prefix)) continue;
$relative = substr($class, strlen($prefix));
$file = str_replace('\\', DIRECTORY_SEPARATOR, $relative) . '.php';
foreach ($dirs as $dir) {
$full = $dir . $file;
if (is_file($full)) {
require_once $full;
return true;
}
}
}
return false;
}
}
// 使用
$loader = new SimplePsr4Loader();
$loader->addNamespace('App\\', __DIR__ . '/src');
$loader->addNamespace('Modules\\', __DIR__ . '/modules');
$loader->register();
| 方案 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| importModule() | 舊專案、腳本、簡易模組化 | 零設定、直觀、可控 | 手動呼叫、無快取、不支援自動發現 |
| SimplePsr4Loader | 中型專案、無 Composer 環境 | 標準 PSR-4、自動載入、效能尚可 | 需手動註冊命名空間、無最佳化 |
| Composer Autoload | 所有現代專案 | Class Map 快取、最佳化、開發/生產區分、腳本鉤子 | 需安裝 Composer |