6.1. Performances
性能
关于缓存的注意事项
PHP-DI 4和5非常依赖缓存。现在,PHP-DI 6的主要优化方法是将容器编译为高度优化的代码(请参见下文)。编译容器更加简单快捷。
编译容器
PHP-DI 会执行两项很繁琐的任务:
为了避免执行这两项任务,可以将容器编译为针对您的配置和类进行了优化的PHP代码。
配置
配置编译容器很简单,只需要在容器构建器上调用 enableCompilation()
方法:
$containerBuilder = new \DI\ContainerBuilder();
$containerBuilder->enableCompilation(__DIR__ . '/var/cache');
// […]
$container = $containerBuilder->build();
enableCompilation()
方法需要传入保存编译容器的目录路径。
在生产环境中部署
将容器配置为可编译时,它只会被编译一次,并且永远不会再次生成。这样可以最大限度地提高生产性能。
当您将新版本的代码部署到生产环境时, 必须删除编译生成的文件 (或者删除整个目录) 以确保重新编译容器
如果您的产品流量很大, 那么您可能还想在新版本的代码 发布之前 生成编译容器。该阶段称为“预编译”。为此,只需在部署步骤中构建容器(调用$ containerBuilder-> build()
),即可创建好编译容器。
开发环境
请勿在开发环境中配置编译容器,否则您对定义所做的所有更改(注解,配置文件等)将不会生效。你可以这样做:
$containerBuilder = new \DI\ContainerBuilder();
if (/* 如果是生产环境 */) {
$containerBuilder->enableCompilation(__DIR__ . '/var/cache');
}
优化编译
正如PHP-DI 如何工作 部分中所讲, PHP-DI 会在配置中找到并编译所有的定义。这意味着不在配置列表的自动装配类将不会被编译 ,因为PHP-DI不知道它们的存在。
如果要最大程度地优化性能,可以通过在定义文件中列出所有自动装配的类,使PHP-DI知道它们的存在:
return [
// ... (你的 definitions)
UserController::class => autowire(),
BlogController::class => autowire(),
ProductController::class => autowire(),
// ...
];
您不需要具体配置它们 (自动装配会帮你解决),但至少现在PHP-DI会知道这些类并编译它们。
当前,PHP-DI不会遍历目录以自动查找自动装配或包含注解的类。
它也不会在编译过程中解析 通配符 。即使定义仍然可以正常地工作,但使用编译容器时根本不会提高它们的性能。
另一方面,在编译容器中支持工厂定义(使用闭包或类工厂定义)。但是请注意,如果您将闭包用作工厂:
- 您不应在闭包内使用
$ this
- 您不应使用
use
关键字在闭包内部导入变量,例如在function()use($ foo){...
存在这些限制是因为每个闭包的代码都被复制到已编译的容器中。可以肯定地说,即使不编译容器,你也不应该执行这些操作。
如何工作
PHP-DI将从您的 配置中读取定义。 编译容器时,将根据这些定义生成PHP代码。
例如,我们定义创建对象的定义:
return [
'Logger' => DI\create()
->constructor('/tmp/app.log')
->method('setLevel', 'warning'),
];
该定义将被编译为类似于以下代码的PHP代码:
$object = new Logger('/tmp/app.log');
$object->setLevel('warning');
return $object;
所有已编译的定义都将存储到一个PHP类(已编译的容器)中,该类会被写入到文件(例如CompiledContainer.php
)中。
在运行时,容器构建器发现CompiledContainer.php
存在,并进行加载(而不是加载定义文件)。该PHP文件可能包含很多代码,但是PHP的opcode cache
会将此类存储在内存中(请记住在生产环境中使用opcache
)。当需要解析定义时,PHP-DI将仅执行已编译的代码并返回创建的实例。
优化延迟注入
如果您使用的是 延迟注入 功能 则应阅读它的 "优化性能"指南.
缓存
编译容器是最有效的解决方案,但是有一些限制。以下情况未优化:
- 在配置中未声明的自动装配 (或注解) 类
- 通配符定义
- 使用
Container::make()
或者Container::injectOn()
(因为它们未使用编译代码)
如果您大量使用这些功能,并且如果使应用程序变慢,则可以启用缓存系统。缓存将确保在每个请求上都不会再次读取注解或反射。
缓存直接依赖于APCu,因为它是唯一有意义的缓存系统(读写速度非常快)。其他缓存不是很好的选择,这就是为什么PHP-DI不为此缓存使用PSR-6或PSR-16的原因。
要启用缓存:
$containerBuilder = new \DI\ContainerBuilder();
if (/* 如果是生产环境 */) {
$containerBuilder->enableDefinitionCache();
}
小心:
- 不要在开发环境中使用缓存,否则您对定义(注解,配置文件等)所做的更改将不会生效
- 在生产环境的每个部署上清除APCu缓存(以避免使用陈旧的缓存)
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。