2.3. 最佳实践指南
最佳实践
这是一本关于如何最好地使用PHP-DI和依赖注入的权威指南。
尽管它可能无法涵盖所有情况并能让所有人满意,但它可以帮助你更好的入门依赖注入
如果您不同意该指南中的内容,没关系。 任何事情是你都应该保持自己的观点 ;)。这不会阻止你按自己的想法使用PHP-DI
使用容器和依赖项注入的规则
以下是一些基本规则:
- 永远不要直接从容器中获取条目(始终使用依赖项注入)
- 编写与容器分离的代码
- 针对接口做类型提示, 将接口的实现配置在容器配置中
编写controllers
在controllers
中使用依赖注入通常是最痛苦的地方。
如果我们以Symfony 2为例(通常适用于很多框架),则可以选择以下选项:
- 将容器注入控制器中,并调用
$ container-> get(...)
这非常糟糕,请参阅规则1。
- 在构造函数中注入依赖项 (controller as a service in Symfony)
当您有超过5个依赖项时,这是很痛苦的,您的构造函数就会像这15行样板代码
- 在属性中注入依赖项
这是我们建议的解决方案。
例:
class UserController
{
/**
* @Inject
* @var FormFactoryInterface
*/
private $formFactory;
public function createForm($type, $data, $options)
{
// $this->formFactory->...
}
}
如您所见,此解决方案只需要很少的代码,易于理解并且获得了IDE的支持(自动补全,重构等)。
属性注入通常不受欢迎,理由如下:
- 注入私有属性会破坏封装
- 它不是显式依赖:没有协议说明您的类需要将该属性才能工作
- 如果您使用PHP-DI的注解来标记要注入的依赖项,则您的类依赖于容器(违反了规则2)
但是,如果您照抄最佳实践,那您的controllers
将不包含业务逻辑(仅将调用路由到模型并将绑定的值绑定到视图)。
所以:
- 你不会对它进行单元测试 (但这并不意味着您不会在界面上编写功能测试)
- 您将不需要在其他地方重用它
- 如果您更改框架,可能得重写
controllers
(或其中的一部分)
(因为大多数依赖项(如请求,响应,模板系统等)都已更改)
该解决方案提供了许多好处,而没有重大缺点,因此我们建议在控制器中使用注解.
编写 services
鉴于service
旨在重用,测试并独立于您的框架, 因此我们不建议您使用注解注入依赖项.
相反, 我们建议使用 构造函数注入和自动装配:
class OrderService implements OrderServiceInterface
{
private $paymentService;
public function __construct(PaymentServiceInterface $paymentService)
{
$this->paymentService = $paymentService;
}
public function processOrder($order)
{
$this->paymentService->...
}
}
通过使用自动装配(默认情况下启用), 您无需在配置中绑定构造函数的参数。PHP-DI会通过检查来猜测需要注入对象的参数的类型。
在某些情况下,自动装配是不够的,因为某些参数将是标量(字符串,整数等)。此时,您将需要显式定义要在该标量参数中注入的内容,为此,您可以:
- 使用
DI\create()
定义方法或类的所有注入 (即每个参数) 。
例:
<?php
// config.php
return [
// ...
OrderService::class => DI\create()
->constructor(DI\get(SomeOtherService::class), 'a value'),
];
- 或者使用
DI\autowire()
只定义标量参数,然后PHP-DI 会自动装配剩余部分。
例:
<?php
// config.php
return [
// ...
OrderService::class => DI\autowire()
->constructorParameter('paramName', 'a value'),
];
由于避免重新定义所有内容,因此通常首选此方案。
旁注: 根据规则3,,我们建议 针对接口进行类型提示。在这种情况下,
您将需要将接口映射到容器应在配置中使用的实现:
<?php
// config.php
return [
// ...
OrderServiceInterface::class => DI\get(OrderService::class),
];
使用库
使用loggers, ORMs之类的库时,你有时需要对其进行配置。
在这种情况下,建议您在配置文件中定义这些依赖项。当配置有些复杂时,我们还建议使用匿名函数。
匿名函数允许您编写真实的PHP代码,这很棒棒,因为您
可以使用该库的文档,可以获得IDE支持,并且您是PHP开发人员,所以,您懂得该怎么使用这门语言;)。
这是一个 Monolog,做logger的例子:
<?php
// config.php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
return [
// ...
Psr\Log\LoggerInterface::class => DI\factory(function () {
$logger = new Logger('mylog');
$fileHandler = new StreamHandler('path/to/your.log', Logger::DEBUG);
$fileHandler->setFormatter(new LineFormatter());
$logger->pushHandler($fileHandler);
return $logger;
}),
];
当然了,如你所见,我们使用 PSR-3 interface 进行注入。 这样我们只要更改一下配置,就可以用任意的PSR-3 logger
来替换Monolog
。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: