隨著專案規模增長,手動 require 大量類文件不再現實。PHP 5 引入自動載入機制,經歷 __autoload() 到 spl_autoload_register() 的演進,現代框架(Composer、PSR-4)皆建立於後者之上。本文系統梳理三種載入方式的原理、優缺點與實戰代碼。
<?php
// A.php
class A { public function __construct() { echo 'Got it.'; } }
// index.php
require('A.php');
$a = new A();
痛點: 類增多時需大量 require,維護困難,易遺漏或重複載入。
當實例化未定義類時,PHP 自動調用全域函數 __autoload($class)。
<?php
function __autoload($class) {
$file = $class . '.php';
if (is_file($file)) {
require_once($file);
}
}
$a = new A(); // 自動觸發 __autoload('A') 載入 A.php
缺陷: 全域僅能定義 一個 __autoload。若引入多個第三方庫,其命名規則不同,所有邏輯須塞進同一函數,導致極其臃腫、難以維護。
註冊多個自動載入器,形成隊列,按註冊順序依次嘗試載入,完美解決多庫共存問題。
<?php
// 1. 普通函數
function loader($class) {
$file = $class . '.php';
if (is_file($file)) require_once($file);
}
spl_autoload_register('loader');
// 2. 匿名函數 (PHP 5.3+)
spl_autoload_register(function($class) {
$file = $class . '.php';
if (is_file($file)) require_once($file);
});
// 3. 類靜態方法
class Loader {
public static function loadClass($class) {
$file = $class . '.php';
if (is_file($file)) require_once($file);
}
}
spl_autoload_register(['Loader', 'loadClass']);
// 或 spl_autoload_register([__CLASS__, 'loadClass']);
// 或 spl_autoload_register([$this, 'loadClass']);
<?php
// autoload.php 入口
define('SDK_PATH', __DIR__);
require_once SDK_PATH . '/common/functions.php';
require_once SDK_PATH . '/common/config.php';
spl_autoload_register(function ($class) {
// 僅處理 YJC\Wechat 命名空間下的類
if (false !== stripos($class, 'YJC\Wechat')) {
// 將命名空間分隔符轉為目錄分隔符,去掉前綴長度
$relative = substr($class, 10); // strlen('YJC\Wechat') = 10
$file = __DIR__ . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $relative) . '.php';
if (is_file($file)) require_once($file);
}
});
關鍵點:
SDK_PATH(或 BASE_PATH),所有 require 使用絕對路徑,避免相對路徑混亂。vendor/autoload.php,遵循 PSR-4 標準,無需手寫。| 方式 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| require_once | 極小專案、單入口腳本 | 直觀、無魔術 | 維護成本高、易出錯 |
| __autoload() | 舊版單一庫專案 (PHP < 5.3) | 簡單自動化 | 只能有一個、無法組合、已移除 |
| spl_autoload_register() | 所有現代專案、框架、Composer | 隊列機制、支援多載入器、命名空間友好 | 需理解隊列順序、效能微損 |
最佳實踐: 拥抱 Composer + PSR-4,專注業務邏輯,勿重複造輪子。