跳转到内容

加载器


注意

The Phalcon\Autoload\Loader类已被重命名Phalcon\Autoload\Loader。功能保持不变。

概览

Phalcon\Autoload\Loader是一个实现了自动加载的类PSR-4。就像任何自动加载器一样,根据其设置,它将尝试根据文件、类、命名空间等找到代码所需的文件。由于此组件是用 C 语言编写的,因此在处理其设置时开销最小,从而提供了性能提升。

该组件依赖于 PHP 的自动加载类功能。如果代码中定义的类尚未包含,则特殊处理程序将尝试加载它。Phalcon\Autoload\Loader担任此操作的特殊处理程序。通过按需加载类,整体性能得到提高,因为唯一发生的文件读取是为了需要的文件。这种技术称为延迟初始化.

该组件提供了基于类、文件名、文件系统中的目录以及文件扩展名加载文件的选项。

注册

通常,我们会使用spl_autoload_register()来为我们的应用程序注册自定义自动加载器。Phalcon\Autoload\Loader隐藏了这种复杂性。在定义完所有命名空间、类、目录和文件后,您只需调用register()函数,自动加载器即可准备使用。

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setNamespaces(
    [
       'MyApp'        => 'app/library',
       'MyApp\Models' => 'app/models',
    ]
);

$loader->register();

register()使用spl_autoload_register()内部实现。因此,它也接受布尔值prepend参数。如果提供并且为true,则自动加载器将在自动加载队列中前置而不是后置(默认行为)。

您可以随时调用isRegistered()方法来检查您的自动加载器是否已注册。

注意

如果注册自动加载器时发生错误,组件将抛出异常。

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setNamespaces(
    [
       'MyApp'        => 'app/library',
       'MyApp\Models' => 'app/models',
    ]
);

$loader->register();

echo $loader->isRegistered(); // true

取消注册自动加载器同样简单。您只需调用unregister().

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setNamespaces(
    [
       'MyApp'        => 'app/library',
       'MyApp\Models' => 'app/models',
    ]
);

$loader->register();

if (true === $loader->isRegistered()) {
    $loader->unregister();
}

安全层

Phalcon\Autoload\Loader集成了安全层,默认情况下会清理类名,即去除无效字符,从而使恶意代码注入到您的应用程序变得更加困难。

考虑以下示例:

<?php

spl_autoload_register(
    function (string $className) {
        $filepath = $className . '.php';

        if (file_exists($filepath)) {
            require $filepath;
        }
    }
);

上述自动加载器缺乏任何安全性。如果代码的一部分意外地使用可能指向包含恶意代码的脚本名称调用自动加载器,则您的应用程序将被破坏。

<?php

$className = '../processes/important-process';

if (class_exists($className)) {
    // ...
}

在上述片段中,如果../processes/important-process.php是有效文件,可能是黑客上传或因不谨慎的上传过程导致的,那么外部用户可以在未经授权的情况下执行代码,并进而访问应用程序,甚至服务器。

为了避免大多数这些类型的攻击,Phalcon\Autoload\Loader从类名中移除无效字符。

命名空间

组织应用程序的一种非常流行的方式是使用目录,每个目录代表特定的命名空间。Phalcon\Autoload\Loader可以注册这些命名空间到目录的映射,并遍历这些目录以查找应用程序所需的文件。

The setNamespaces()方法接受一个数组,其中键是命名空间,值是文件系统中的实际目录。加载器尝试查找类时,命名空间分隔符将被替换为目录分隔符。

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setNamespaces(
    [
       'MyApp'             => 'app/library',
       'MyApp\Controllers' => 'app/controllers',
       'MyApp\Models'      => 'app/models',
    ]
);

$loader->register();

在上述示例中,每次我们引用控制器时,加载器都会在app/controllers和其子目录中搜索。类似地,对于模型,搜索将在app/models.

如果实际文件位于映射命名空间约定的子目录中,则无需注册子命名空间。

例如,在上述示例中,我们将MyApp命名空间指向app/library。如果我们有一个文件:

/app/library/Components/Mail.php

具有命名空间:

MyApp\Components

则如上定义的加载器不需要知道MyApp\Components命名空间位置,或者它不需要在()声明。

中定义。MyApp\Components\Mail, it will assume that it is a subdirectory of the root namespace. However, since we specified a different location for the MyApp\ControllersMyApp\Models namespaces, the loader will look for those namespaces in the specified directories.

The setNamespaces()方法还接受第二个参数merge默认情况下,它是false但是你可以将其设置为true在进行多次调用时setNamespaces()时,您可以将其设置为

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setNamespaces(
    [
       'MyApp'             => 'app/library',
    ]
);

$loader->setNamespaces(
    [
       'MyApp\Controllers' => 'app/controllers',
       'MyApp\Models'      => 'app/models',
    ],
    true
);

$loader->register();

上面的例子将第二个声明与之前的声明合并setNamespaces() with the previous one.

如果您需要检查自动加载器中注册了哪些命名空间,可以使用getNamespaces()获取器,它返回已注册命名空间的数组。对于上面的示例,getNamespaces()返回:

[
   'MyApp'             => 'app/library',
   'MyApp\Controllers' => 'app/controllers',
   'MyApp\Models'      => 'app/models',
]

另一种让Phalcon\Autoload\Loader知道您的组件/类所在位置,以便自动加载器能够正确加载它们的另一种方法是使用setClasses().

该方法接受一个数组,其中键是命名空间类,值是包含该类的文件的位置。正如所料,这是自动加载类的最快方式,因为自动加载器不需要进行文件扫描或统计来查找文件引用。

然而,使用这种方法可能会妨碍应用程序的维护。您的应用程序越大,添加的文件越多,维护setClasses()

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setClasses(
    [
        'MyApp\Components\Mail'             => 'app/library/Components/Mail.php',
        'MyApp\Controllers\IndexController' => 'app/controllers/IndexController.php',
        'MyApp\Controllers\AdminController' => 'app/controllers/AdminController.php',
        'MyApp\Models\Invoices'             => 'app/models/Invoices.php',
        'MyApp\Models\Users'                => 'app/models/Users.php',
    ]
);

$loader->register();

中使用的文件列表时就越容易出错。

The setClasses()方法还接受第二个参数merge默认情况下,它是false但是你可以将其设置为true在进行多次调用时setClasses()以便类定义被合并

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setClasses(
    [
        'MyApp\Components\Mail'             => 'app/library/Components/Mail.php',
        'MyApp\Controllers\IndexController' => 'app/controllers/IndexController.php',
        'MyApp\Controllers\AdminController' => 'app/controllers/AdminController.php',
    ]
);

$loader->setClasses(
    [
        'MyApp\Models\Invoices'             => 'app/models/Invoices.php',
        'MyApp\Models\Users'                => 'app/models/Users.php',
    ],
    true
);

$loader->register();

上面的例子将第二个声明与之前的声明合并setClasses() with the previous one.

如果你需要检查自动加载器中注册了哪些类,可以使用getClasses()获取器,它返回已注册类的数组。对于上面的例子,getClasses()返回:

[
    'MyApp\Components\Mail'             => 'app/library/Components/Mail.php',
    'MyApp\Controllers\IndexController' => 'app/controllers/IndexController.php',
    'MyApp\Controllers\AdminController' => 'app/controllers/AdminController.php',
    'MyApp\Models\Invoices'             => 'app/models/Invoices.php',
    'MyApp\Models\Users'                => 'app/models/Users.php',
]

文件

有时候你可能需要引入一个包含没有命名空间的类的特定文件,或者是一个包含你需要的一些代码的文件。例如,一个包含方便调试函数的文件。

Phalcon\Autoload\Loader提供了setFiles()它用于引入这样的文件。它接受一个数组,包含每个文件的名称和位置。

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setFiles(
    [
        'functions.php',
        'arrayFunctions.php',
    ]
);

$loader->register();

register()方法被调用时,这些文件会自动加载。

The setFiles()方法还接受第二个参数merge默认情况下,它是false但是你可以将其设置为true在进行多次调用时setFiles()以便文件定义被合并

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setFiles(
    [
        'app/functions/functions.php',
    ]
);

$loader->setFiles(
    [
        'app/functions/debug.php',
    ],
    true
);

$loader->register();

上面的例子将第二个声明与之前的声明合并setFiles() with the previous one.

如果你需要检查自动加载器中注册了哪些文件,可以使用getFiles()获取器,它返回已注册文件的数组。对于上面的例子,getFiles()返回:

[
    'app/functions/functions.php',
    'app/functions/debug.php',
]

你还可以访问loadFiles()方法,它将遍历所有已注册的文件,如果它们存在,则会require加载它们。此方法在你调用时会自动执行register().

目录

另一种让Phalcon\Autoload\Loader知道你的应用程序文件所在位置的方法是注册目录。当应用程序需要引入一个文件时,自动加载器将扫描已注册的目录以找到引用的文件并引入它。

The setDirectories()方法接受一个数组,其中每个元素都是文件系统中的一个目录,该目录包含应用程序将要引入的文件。

这种类型的注册不推荐用于性能考虑。此外,声明目录的顺序很重要,因为自动加载器通过按顺序搜索目录来定位文件。因此,应首先声明包含最常引用文件的目录,等等。

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setDirectories(
    [
        'app/functions',
        'app/controllers',
        'app/models',
    ]
);

$loader->register();

The setDirectories()方法还接受第二个参数merge默认情况下,它是false但是你可以将其设置为true在进行多次调用时setDirectories()以便类定义被合并

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setDirectories(
    [
        'app/functions',
    ]
);

$loader->setDirectories(
    [
        'app/controllers',
        'app/models',
    ],
    true
);

$loader->register();

上面的例子将第二个声明与之前的声明合并setDirectories() with the previous one.

如果你需要检查自动加载器中注册了哪些目录,可以使用getDirs()获取器,它返回已注册目录的数组。对于上面的例子,getDirs()返回:

[
    'app/functions',
    'app/controllers',
    'app/models',
]

文件扩展名

当你使用setNamespaces()setDirectories(), Phalcon\Autoload\Loader它会自动假设你的文件具有.php扩展名。你可以通过使用setExtensions()方法更改这种行为。该方法接受一个数组,其中每个元素是要检查的扩展名(不带.):

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setExtensions(
    [
        'php',
        'inc',
        'phb',
    ]
);

$loader->setDirectories(
    [
        'app/functions',
    ]
);

在上面的例子中,当引用文件Mail时,自动加载器将在app/functions中查找以下文件:

  • Mail.php
  • Mail.inc
  • Mail.phb

文件按照每个扩展名定义的顺序进行检查。

文件检查回调

你可以通过使用setFileCheckingCallback()方法设置容器。

设置不同的文件检查回调方法来加速加载器。默认行为使用is_file然而,你也可以使用null它不会在加载文件之前检查文件是否存在,或者你可以使用stream_resolve_include_path它比is_file更快,但如果目标文件从文件系统中删除,它会引起问题。

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setFileCheckingCallback("is_file");

默认行为

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setFileCheckingCallback("stream_resolve_include_path");

is_file()更快,但在文件从文件系统中删除时会产生问题。

<?php

use Phalcon\Autoload\Loader;

$loader = new Loader();

$loader->setFileCheckingCallback(null);

不检查文件是否存在。

事件

[事件管理器][events] 组件提供了可以实现的钩子,以观察或扩展加载器的功能。该Phalcon\Autoload\Loader实现了Phalcon\Events\EventsAwareInterface因此getEventsManager()setEventsManager()方法可用。

以下是可用的事件:

事件 描述 能否停止操作?
afterCheckClass 在自动加载过程结束且未找到类时触发。
beforeCheckClass 在自动加载过程开始、检查类之前触发。
beforeCheckPath 在检查目录中的类文件之前触发。
pathFound 在加载器找到类文件或注册目录中的文件时触发。

在下面的例子中,EventsManager正在与类加载器协作,提供有关操作流程的额外信息:

<?php

use Phalcon\Events\Event;
use Phalcon\Events\Manager;
use Phalcon\Autoload\Loader;

$eventsManager = new Manager();
$loader        = new Loader();

$loader->setNamespaces(
    [
       'MyApp'        => 'app/library',
       'MyApp\Models' => 'app/models',
    ]
);

$eventsManager->attach(
    'loader:beforeCheckPath',
    function (
        Event $event, 
        Loader $loader
    ) {
        echo $loader->getCheckedPath();
    }
);

$loader->setEventsManager($eventsManager);

$loader->register();

在上面的例子中,我们创建了一个新的 Events Manager 对象,将一个方法附加到loader:beforeCheckPath事件,然后在我们的自动加载器中设置它。每次加载器循环并在特定路径中查找特定文件时,路径都会显示在屏幕上。

The getCheckedPath()包含在内部循环每次迭代期间扫描的路径。还可以使用getfoundPath()方法,在内部循环期间保存找到的文件路径。

对于可以停止操作的事件,你只需在附加到特定事件的方法中返回false即可:

<?php

use Phalcon\Events\Event;
use Phalcon\Events\Manager;
use Phalcon\Autoload\Loader;

$eventsManager = new Manager();
$loader        = new Loader();

$loader->setNamespaces(
    [
       'MyApp'        => 'app/library',
       'MyApp\Models' => 'app/models',
    ]
);

$eventsManager->attach(
    'loader:beforeCheckPath',
    function (
        Event $event, 
        Loader $loader
    ) {
        if ('app/models' === $loader->getCheckedPath()) {
            return false;
        }
    }
);

$loader->setEventsManager($eventsManager);

$loader->register();

在上面的例子中,当自动加载器开始扫描app/models文件夹以查找MyApp\Models命名空间时,它将停止操作。

排查问题

使用自动加载器时需要注意的一些事项:

  • 自动加载过程是区分大小写的
  • 基于命名空间/前缀的策略比目录策略更快
  • 如果安装了字节码缓存,例如APCu将用于获取请求的文件(隐式地对文件进行了缓存)。

调试

The Phalcon\Autoload\Loader可以通过传递参数true到构造函数中,以便启用调试模式。在调试模式下,加载器将收集有关查找和定位所请求文件的数据。然后您可以使用getDebug()方法输出调试信息,以诊断问题。

<?php

use Phalcon\Autoload\Loader;

$loader    = new Loader(true);
$directory = dataDir('some/directory/');
$loader->addDirectory($directory);

$loader->autoload('Simple');

var_dump($loader->getDebug());

// [
//     'Loading: Simple',
//     'Class: 404: Simple',
//     'Namespace: 404: Simple',
//     'Require: some/directory/Simple.php',
//     'Directories: some/directory/Simple.php',
// ];

方法

public function __construct(bool $isDebug = false)
构造函数。如果$isDebugtrue,则会收集调试信息。

public function addClass(string $name, string $file): Loader
向内部集合添加一个类以进行映射

public function addDirectory(string $directory): Loader
添加一个目录供加载文件使用

public function addExtension(string $extension): Loader
添加一个扩展名供加载文件使用

public function addFile(string $file): Loader
添加一个文件到加载器中

public function addNamespace(
  string $name,
  mixed $directories,
  bool $prepend = false
): Loade
向加载器添加命名空间,并将其映射到不同的目录。第三个参数允许将命名空间前置。

public function autoload(string $className): bool
自动加载已注册的类

public function getCheckedPath(): string | null
获取加载器正在检查路径的路径

public function getClasses(): array
返回注册在自动加载器中的类映射

public function getDebug(): array
返回收集的调试信息

public function getDirectories(): array
返回注册在自动加载器中的目录

public function getExtensions(): array
返回在加载器中注册的文件扩展名

public function getFiles(): array
返回注册在自动加载器中的文件

public function getFoundPath(): string | null
获取找到类时的路径

public function getNamespaces(): array
返回注册在自动加载器中的命名空间

public function loadFiles(): void
检查文件是否存在,然后通过虚拟require添加文件

public function register(bool $prepend = false): Loader
注册自动加载方法

public function setClasses(
    array $classes, 
    bool $merge = false
): Loader
注册类及其位置

public function setDirectories(
    array $directories, 
    bool $merge = false
): Loader
注册可能找到“未找到”类的目录

public function setExtensions(
    array $extensions, 
    bool $merge = false
): Loader
设置加载器在每次尝试定位文件时必须尝试的文件扩展名数组

public function setFileCheckingCallback(
    mixed $method = null
): Loader
设置文件检查回调。

public function setFiles(
    array $files, 
    bool $merge = false
): Loader
注册那些“非类”的文件,因此需要“require”。这对于包含仅具有函数的文件非常有用。

public function setNamespaces(
    array namespaces, 
    bool merge = false
): Loader
注册命名空间及其相关目录

public function unregister(): Loader
注销自动加载方法

无噪 Logo
无噪文档
25 年 6 月翻译
文档源↗