命名空间和自动加载

从技术上讲,自动加载通过在需要 PHP 类但未找到时执行回调来工作。这种回调通常会尝试加载这些类。

通常,自动加载可以理解为在需要类时根据类的完全限定名称(FQN)从适当的路径加载 PHP 文件(特别是 PHP 类文件,其中 PHP 源文件专用于特定类)的尝试。

假设我们有这些类:

application\controllers\Base 的类文件:

<?php
namespace application\controllers { class Base {...} }

application\controllers\Control 的类文件:

<?php
namespace application\controllers { class Control {...} }

application\models\Page 的类文件:

<?php
namespace application\models { class Page {...} }

在源文件夹下,这些类应分别作为其 FQN 放在路径中:

  • 源文件夹
    • applications
      • controllers
        • Base.php
        • Control.php
      • models
        • Page.php

使用此函数,此方法可以根据 FQN 以编程方式解析类文件路径:

function getClassPath(string $sourceFolder, string $className, string $extension = ".php") {
    return $sourceFolder . "/" . str_replace("\\", "/", $className) . $extension; // note that "/" works as a directory separator even on Windows
}

spl_autoload_register 函数允许我们在需要时使用用户定义的函数加载类:

const SOURCE_FOLDER = __DIR__ . "/src";
spl_autoload_register(function (string $className) {
    $file = getClassPath(SOURCE_FOLDER, $className);
    if (is_readable($file)) require_once $file;
});

此函数可以进一步扩展为使用后台加载方法:

const SOURCE_FOLDERS = [__DIR__ . "/src", "/root/src"]);
spl_autoload_register(function (string $className) {
    foreach(SOURCE_FOLDERS as $folder) {
        $extensions = [
            // do we have src/Foo/Bar.php5_int64?
            ".php" . PHP_MAJOR_VERSION . "_int" . (PHP_INT_SIZE * 8),
            // do we have src/Foo/Bar.php7?
            ".php" . PHP_MAJOR_VERSION,
            // do we have src/Foo/Bar.php_int64?
            ".php" . "_int" . (PHP_INT_SIZE * 8),
            // do we have src/Foo/Bar.phps?
            ".phps"
            // do we have src/Foo/Bar.php?
            ".php"
        ];
        foreach($extensions as $ext) {
            $path = getClassPath($folder, $className, $extension);
            if(is_readable($path)) return $path;
        }
    }
});

请注意,只要加载使用此类的文件,PHP 就不会尝试加载类。它可以在脚本中间加载,甚至可以在关闭函数中加载。这是开发人员(尤其是使用自动加载的开发人员)应该避免在运行时替换执行源文件的原因之一,尤其是在 phar 文件中。