__autoload() 魔術函數,僅能定義一個,無法鏈結多個載入器。spl_autoload_register(),支援多載入器堆疊、優先序控制、例外處理。__autoload 正式棄用(PHP 7.2 棄用警告,8.0 移除)。<?php
// 設定搜尋路徑
set_include_path('aa' . PATH_SEPARATOR . get_include_path());
function __autoload($className) {
$file = $className . '.php';
// 問題:file_exists() 檢查的是相對當前執行腳本的路徑,
// 但 include 會搜尋 include_path,兩者可能不一致導致誤判
if (file_exists($file)) {
include_once $file;
} else {
exit('no file: ' . $file);
}
}
$a = new Acls(); // 嘗試載入 Acls.php
?>
缺點:只能有一個、無法協同第三方庫、路徑解析邏輯寫死在函數裡、難以測試。
<?php
set_include_path('aa' . PATH_SEPARATOR . get_include_path());
// 使用內建 spl_autoload(小寫檔名 .php/.inc)
spl_autoload_register();
// 或註冊自定義載入器(支援多個,按註冊順序嘗試)
spl_autoload_register(function ($class) {
$file = __DIR__ . '/lib/' . $class . '.php';
if (file_exists($file)) include $file;
});
spl_autoload_register(function ($class) {
$file = __DIR__ . '/vendor/' . $class . '.php';
if (file_exists($file)) include $file;
});
$a = new Acls(); // 依序嘗試上述載入器
?>
spl_autoload_extensions('.php,.inc'):設定內建載入器掃描的副檔名。spl_autoload_unregister($func):移除已註冊載入器。spl_autoload_functions():取得目前載入器堆疊列表。project/
├── application/
│ ├── controllers/
│ ├── models/
│ └── library/
├── public/
│ └── index.php
└── vendor/ (Composer)
<?php
// 1. 定義專案根路徑
define('ROOT', __DIR__ . '/..');
// 2. 設定 include_path(非 Composer 專案常用)
$paths = [
ROOT . '/application/controllers',
ROOT . '/application/models',
ROOT . '/application/library',
];
set_include_path(implode(PATH_SEPARATOR, $paths) . PATH_SEPARATOR . get_include_path());
// 3. 註冊 PSR-4 風格載入器
spl_autoload_register(function ($class) {
// 命名空間分隔符 \ -> 目錄分隔符 /
// 類名尾綴 .php
$file = str_replace('\\', '/', $class) . '.php';
// 依序搜尋 include_path
$paths = explode(PATH_SEPARATOR, get_include_path());
foreach ($paths as $path) {
$full = $path . '/' . $file;
if (file_exists($full)) {
include $full;
return; // 找到即停止
}
}
});
// 4. 路由解析並執行
$router = new Router();
$router->dispatch();
?>
<?php
class Router {
public function dispatch() {
// 解析 URL 取得 controller、action
$controllerName = $this->getController(); // 如 'App\Controllers\UserController'
$actionName = $this->getAction(); // 如 'index'
if (!class_exists($controllerName)) {
throw new Exception('Controller not found: ' . $controllerName);
}
$rc = new ReflectionClass($controllerName);
if (!$rc->hasMethod($actionName)) {
throw new Exception('Action not found: ' . $actionName);
}
$controller = $rc->newInstance();
$method = $rc->getMethod($actionName);
$method->invoke($controller);
}
// ... getController, getAction 解析邏輯
}
?>
手寫載入器適合學習或極簡專案,正式專案請直接使用 Composer:
# composer.json
{
"autoload": {
"psr-4": {
"App\\": "application/",
"Core\\": "core/"
}
}
}
執行 composer dump-autoload 產生 vendor/autoload.php,入口只需 require 'vendor/autoload.php'; 即可。
composer dump-autoload -o 產生類別映射陣列,直接查表不掃描檔案系統。| 現象 | 可能原因 | 排查方法 |
|---|---|---|
| Class 'X' not found | 命名空間/目錄結構不符 PSR-4 | echo 實際搜尋路徑、檢查大小寫(Linux 區分大小寫) |
| 載入順序錯誤 | 多載入器優先序問題 | 調整 spl_autoload_register 順序、或用 prepend 參數 |
| 重複定義錯誤 | 同一類別被多次 include | 改用 include_once、或檢查 class_exists 前置 |
| 效能差 | 大量 file_exists 系統呼叫 | 開啟 OPcache、Composer -o、減少 include_path 深度 |