evo/autoloader

PSR4 兼容的自动加载器

2.0.0 2022-12-21 21:19 UTC

This package is not auto-updated.

Last update: 2024-09-26 05:33:07 UTC


README

PSR4 兼容的自动加载器

尽管我尝试使用 Composer 来处理所有与自动加载相关的事情。

有时您需要在 Composer 之外使用自动加载器。可能是因为一个小型项目等...

use evo\autoloader\Autoloader;

require_once 'src/evo/autoloader/Autoloader.php';

$Autoloader = Autoloader::getInstance();

默认情况下,自动加载器在其放置的位置注册一个空命名空间。

$Autoloader->registerPath('', '');

这是除 getInstance() 方法之外最重要的方法。它定义如下:

registerPath($namespace, $path, [$priority = 100])

其中 $namespace 是一个命名空间,例如 evo\\autoloader,而路径是一个路径,如 __DIR__.'/scr/evo/'。在大多数情况下,自动加载器会考虑到这些差异,例如,对于命名空间使用 \\evo\\autoloader\\,而对于路径使用 __DIR__.'/scr/evo'。您可以使用相对路径或绝对路径与自动加载器一起使用。$priority 将注册的路径按升序排序,并按命名空间分组。因此,优先级较低的路径将首先在具有多个路径的命名空间中调用。

让我们回到默认设置,这是一个没有命名空间、没有路径的设置。因此,它将基于调用其正在尝试加载的类的路径进行根目录设置。解释它如何确定路径的最简单方法是,它会在 registerPaths 中搜索 $namespace,然后为该命名空间添加 $path + class info

现在假设这个文件不是 README.md,而是 index.php。在这个相同的文件夹中,我们有一个简单的类,如下所示:

//location  /
class A
{
}

仅使用默认设置,这个类就可以被加载。自动加载器会查找命名空间,找到 '' 空值,然后添加空路径和类名 A.php,我们得到相对于 index.php 所在文件夹的相对路径,并且没有命名空间,我们只需查找文件名即可找到。

现在让我们尝试更多示例

//location  /src/evo/
namespace src\evo;

class B
{
}

像类 A 一样,这个类也可以很好地加载。让我们计算路径,记住我们在 registerPath 中为两个参数都使用了 '',这是默认注册的路径。和第一个例子一样,我们再次找到当前位置,并添加命名空间和类名,这得出 /src/evo/B.php。您可能认为自动加载器会根据命名空间搜索 registerPath,我毕竟是这样说的,但我们从未注册过 src\evo 命名空间和路径。幸运的是,自动加载器足够智能,如果在给定命名空间找不到路径,它会根据类提供的命名空间回溯。它从 registerPath 中的 src\evo 开始查找,但没有找到。然后它删除一个段并查找 src,同样没有找到。最后,它删除最后一个段并查找 '',这是我们默认注册的。然后它为该命名空间取路径,并添加类的命名空间和类名。对于这个路径,它得出 /src/evo/B.php,这是正确的。

让我们看看一个无法使用默认设置加载的类

//location  /src/evo/
namespace evo;

class C
{
}

这里我们缺少命名空间中的 src 部分,并且它没有在路径中得到考虑。在这种情况下,自动加载器将找不到要加载的文件。原因应该是显而易见的。自动加载器找不到命名空间,因此它会像上面描述的那样使用默认设置,然后添加类的信息。这得出 /evo/C.php,但我们位于 src 文件夹之上,因此该文件夹从自动加载器的计算中缺失。幸运的是,我们可以注册我们需要的任意数量的命名空间/路径对,并且注册此路径的方法很简单:

$Autoloader->registerPath('', __DIR__.'/src');

现在当我们把参数和类信息加起来时,我们得到这个 src + evo + C.phpsrc/evo/C.php,这正是我们需要的。

好的,我们几乎完全没触碰命名空间参数,就已经取得了很大的进展。上面的类也可以这样注册:

$Autoloader->registerPath('evo', __DIR__.'/src/evo');

这也将正确解析文件的路径。它是通过这种方式解析的:自动加载器查找命名空间 evo,它在 __DIR__.'/src/evo' 的路径中找到它。现在因为它找到了类命名空间中的 evo 部分,所以它不会把这个加到路径中,在这种情况下命名空间中没有剩下任何内容,所以它只加上类名,我们最终得到 __DIR__.'/src/evo/C.php。这本质上和上面的例子是一样的。

在上面的例子中,自动加载器需要做的功比上一个例子要少一点,所以你可能会通过以这种方式注册命名空间获得一点性能提升。但说到底,这种方法真正的优势在于类位于与命名空间没有逻辑关系的路径时。例如:

//location  /src/evo/
namespace foo\bar;

class D
{
}

如你所见,命名空间 foo\bar 与路径 src/evo 完全不匹配。不那么灵活的自动加载器可能会放弃这个配置,但我们可以通过使用这个命名空间/路径对来很好地处理这个问题。

$Autoloader->registerPath('foo\bar', __DIR__.'/src/evo');

和前面的例子(第二个例子中的类 C)一样,自动加载器在我们的 registerPaths 中查找命名空间,并找到上面的路径 __DIR__.'/src/evo',然后因为整个命名空间都被找到了,所以它只加上类名,得到 __DIR__.'/src/evo/D.php',这正是类所在的位置。

我们最后需要讨论的是为同一个命名空间设置多个路径,是的,这是可能的。

$Autoloader->registerPath('foo\bar', __DIR__.'/src/evo');
$Autoloader->registerPath('foo\bar', __DIR__.'/foo/bar');

现在当自动加载器找到命名空间时,它将遍历这些路径。

希望这些例子能解释清楚自动加载器的工作方式。还有一些其他的方法我想提及。首先是调试方法。这很重要,因为自动加载有时候可能会很困难。另一个挑战是自动加载器做了很多事情,所以只是打印东西出来可能会输出太多信息,变得毫无价值。让我们看看调试方法及其用法。

$Autoloader = Autoloader::getInstance();
$Autoloader->setDebug(true);
new C();
$Autoloader->setDebug(false);

由于自动加载器是单例的,我们可以调用 getInstance 并获取之前创建的相同类的实例。这无论从哪里调用都是正确的。我们不需要有多个这样的自动加载器的实例。调试方法相当简单,它接受一个布尔值 truefalsetrue 打开输出,false 关闭输出。所以在这里,我们在调用我们遇到问题的类时打开输出,然后关闭输出。这可以防止自动加载器输出过多的信息。

在这个例子中,我们将使用我们最初无法加载的类 C,调试输出看起来像这样:

============================ evo\autoloader\Autoloader::debug ============================
evo\autoloader\Autoloader::splAutoload evo\C
Checking class: C
Checking namespace: evo
checking pathname:/evo/C.php
==========================================================================================

现在我们将看看同一个类,但第二个例子中,我们通过注册命名空间与路径来加载类

============================ evo\autoloader\Autoloader::debug ============================
evo\autoloader\Autoloader::splAutoload evo\C
Checking class: C
Checking namespace: evo
checking pathname:/evo/src/C.php
Found: /evo/src/C.php
==========================================================================================

如果你比较这两个,你会注意到第一个没有 Found: 部分,这仅仅表示文件找到的位置。如果它必须遍历 registerPaths 和命名空间,就像上面描述的那样,还可能有多个 checking pathname: 条目。

大多数其他方法应该都是相当容易理解的,所以下面你可以找到所有公共方法列表。

//Get the autoloader instance
public static function getInstance($throw = false, $prepend = false)

//set debug (covered above)
public function setDebug($debug = false)

//called whenever a class is loaded, has to be public but not really intended for use
public function splAutoload($class)

//register a namespace path pair
public function getRegisteredPaths($namespace = null)

//register a namespace path pair (covered above)
public function registerPath($namespace, $path, $priority = self::DEFAULT_PRIORITY)

//remove a path that was registered, you can remove a whole namespace or a path within the namespace
public function unloadPath($namespace, $path = null)

//check if a namespace is registered
public function isRegistered($namespace, $path = null)

//get the path that a class was found at, null get all classes and their paths
public function getLoadedFile($class = null);

享受吧