加载器¶
注意
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;
}
}
);
上述自动加载器缺乏任何安全性。如果代码的一部分意外地使用可能指向包含恶意代码的脚本名称调用自动加载器,则您的应用程序将被破坏。
在上述片段中,如果../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
。如果我们有一个文件:
具有命名空间:
则如上定义的加载器不需要知道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\Controllers
和MyApp\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()
返回:
你还可以访问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()
返回:
文件扩展名¶
当你使用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()
更快,但在文件从文件系统中删除时会产生问题。
不检查文件是否存在。
事件¶
[事件管理器][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',
// ];
方法¶
构造函数。如果$isDebug
是true
,则会收集调试信息。 向内部集合添加一个类以进行映射 添加一个目录供加载文件使用 添加一个扩展名供加载文件使用 添加一个文件到加载器中 向加载器添加命名空间,并将其映射到不同的目录。第三个参数允许将命名空间前置。 自动加载已注册的类 获取加载器正在检查路径的路径 返回注册在自动加载器中的类映射 返回收集的调试信息 返回注册在自动加载器中的目录 返回在加载器中注册的文件扩展名 返回注册在自动加载器中的文件 获取找到类时的路径 返回注册在自动加载器中的命名空间 检查文件是否存在,然后通过虚拟require添加文件 注册自动加载方法 注册类及其位置 注册可能找到“未找到”类的目录 设置加载器在每次尝试定位文件时必须尝试的文件扩展名数组 设置文件检查回调。 注册那些“非类”的文件,因此需要“require”。这对于包含仅具有函数的文件非常有用。 注册命名空间及其相关目录 注销自动加载方法