PHP 自動載入 (Autoloading)

自動載入讓 PHP 在需要類別時自動載入對應的檔案,不需要手動 require 每個檔案。在沒有自動載入的情況下,你必須在使用每個類別之前手動 requireinclude 它的檔案,這在大型專案中會變得非常繁瑣且容易出錯。

spl_autoload_register

spl_autoload_register() 函數讓你註冊一個自動載入函數。當 PHP 遇到一個未定義的類別時,會呼叫這個函數,並傳入類別名稱作為參數。你的函數負責找到並載入對應的檔案:

<?php
spl_autoload_register(function (string $class) {
    $file = __DIR__ . '/src/' . str_replace('\\', '/', $class) . '.php';
    
    if (file_exists($file)) {
        require $file;
    }
});

// 現在可以直接使用類別
$user = new App\Models\User("Alice");
?>

PSR-4 自動載入

PSR-4 是 PHP-FIG(PHP Framework Interoperability Group)制定的自動載入標準。它定義了命名空間與檔案路徑之間的對應規則,讓不同的套件和框架可以無縫地一起運作:

完整類別名稱命名空間前綴基礎目錄檔案路徑
App\Models\UserApp\src/src/Models/User.php
App\Controllers\HomeControllerApp\src/src/Controllers/HomeController.php

簡單的 PSR-4 載入器

<?php
class Autoloader {
    private array $prefixes = [];
    
    public function register(): void {
        spl_autoload_register([$this, 'loadClass']);
    }
    
    public function addNamespace(string $prefix, string $baseDir): void {
        $prefix = trim($prefix, '\\') . '\\';
        $baseDir = rtrim($baseDir, '/') . '/';
        $this->prefixes[$prefix] = $baseDir;
    }
    
    public function loadClass(string $class): void {
        foreach ($this->prefixes as $prefix => $baseDir) {
            if (strpos($class, $prefix) === 0) {
                $relativeClass = substr($class, strlen($prefix));
                $file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
                
                if (file_exists($file)) {
                    require $file;
                    return;
                }
            }
        }
    }
}

$loader = new Autoloader();
$loader->addNamespace('App\\', __DIR__ . '/src/');
$loader->register();
?>

使用 Composer

Composer 是 PHP 的標準套件管理工具,也提供強大的自動載入功能。在實際專案中,幾乎都會使用 Composer 來處理自動載入,而非自己實作。

composer.json

{
    "name": "myapp/project",
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

使用

# 產生自動載入檔案
composer dump-autoload
<?php
// 只需要載入一次
require __DIR__ . '/vendor/autoload.php';

// 自動載入所有類別
use App\Models\User;
use App\Services\UserService;

$user = new User("Alice");
?>

目錄結構範例

project/
├── composer.json
├── vendor/
│   └── autoload.php
├── src/
│   ├── Models/
│   │   └── User.php
│   ├── Controllers/
│   │   └── UserController.php
│   └── Services/
│       └── UserService.php
└── public/
    └── index.php

src/Models/User.php

<?php
namespace App\Models;

class User {
    public function __construct(
        public string $name,
        public string $email
    ) {}
}
?>

public/index.php

<?php
require __DIR__ . '/../vendor/autoload.php';

use App\Models\User;

$user = new User("Alice", "alice@example.com");
echo $user->name;
?>

多重命名空間

{
    "autoload": {
        "psr-4": {
            "App\\": "src/",
            "Tests\\": "tests/"
        },
        "files": [
            "src/helpers.php"
        ],
        "classmap": [
            "legacy/"
        ]
    }
}

最佳實踐

  1. 使用 Composer - 標準化的自動載入
  2. 遵循 PSR-4 - 命名空間對應目錄結構
  3. 一個檔案一個類別 - 便於自動載入
  4. 類別名稱與檔案名稱相符 - User.php 包含 User 類別