PHP-DI是用PHP编写的、强大的和实用的、框架无关的依赖注入容器。这是一个关于如何使用PHP-DI和依赖注入的最佳实践指南。php
文章来源于PHP-DI,做者:Matthieu Napoli和贡献者。PHP-DI是用PHP编写的、强大的和实用的、框架无关的依赖注入容器。html
原文地址(英文):http://php-di.org/doc/best-pr...框架
PHP-DI在【本站实用开源目录】连接:http://www.worldlink.com.cn/o...
这是一个关于如何使用PHP-DI和依赖注入的最佳实践指南。函数
虽然它可能不包括每一个案例,并知足每一个人,但它可做为抛砖引玉,以帮助您开始依赖注入。单元测试
若是你不一样意该指南中解释的任何内容,那不要紧。你应该对这些问题提出本身的看法:)。它不会阻止你以你想要的方式使用PHP-DI。测试
如下是一些基本规则:this
不要直接从容器中获取一个条目(老是使用依赖注入)
更广泛的是,编写代码解耦的容器
针对接口的类型约束,要在容器的配置中配置使用哪一种实现
#编写控制器
在控制器中使用依赖注入一般是最痛苦的。日志
若是咱们以Symfony 2为例(但这一般适用于每一个框架),这里有你的选择:code
在容器中注入控制器,并调用 $container->get(...)
这是很差的,见规则n°1。orm
在构造函数中注入依赖性(在Symfony中做为服务的控制器)
这是痛苦的,当你有5个以上的依赖项,你的构造函数是15行样板代码
在属性中注入依赖性
这是咱们建议的解决方案。
例如:
class UserController { /** * @Inject * @var FormFactoryInterface */ private $formFactory; public function createForm($type, $data, $options) { // $this->formFactory->... } }
如你所见,这个解决方案须要不多的代码,很容易理解和利于IDE支持(自动完成,重构,...)。
属性注入一般让人皱眉头,这是有必定的缘由的:
注入私有属性破坏了封装
它不是一个显式的依赖:没有约定,说你的类须要设置属性以工做
若是使用PHP-DI的注解来标记要注入的依赖关系,那么您的类依赖于容器(请参见上面的第2条规则)
但
若是您遵循关于如何编写应用程序的通常最佳实践,您的控制器将不包含业务逻辑(仅对模型进行路由调用并绑定返回的值以查看)。
所以:
你不会对它进行单元测试(这并不意味着你不会在接口上写功能测试)
你不须要在别处重用它
若是你改变框架,你可能不得不重写它(或其部分)(由于大多数依赖,如请求、响应、模板系统等将改变)
此解决方案提供了许多优势,没有主要的缺点,所以咱们建议在控制器中使用注解。
给定一个服务旨在被重用、测试和独立于你的框架,咱们不建议使用注解注入依赖。 相反,咱们建议使用构造函数注入和自动装配:
class OrderService implements OrderServiceInterface { private $paymentService; public function __construct(PaymentServiceInterface $paymentService) { $this->paymentService = $paymentService; } public function processOrder($order) { $this->paymentService->... } }
经过使用自动装配(默认状况下启用),您本身保存绑定配置中的构造函数的每一个参数。 PHP-DI将经过检查您的参数的类型来猜想它须要注入哪一个对象。
在某些状况下,使用自动装配还不够,由于一些参数将是一个标量(string,int,...)。 此时,您须要明肯定义要在该标量参数中注入的内容,为此,您能够:
定义方法/类的所有注入(即,每一个参数)。
例如:
<?php // config.php return [
// ... OrderService::class => DI\object() ->constructor(DI\get(SomeOtherService::class), 'a value'),
];
或者只定义标量参数,让PHP-DI使用自动装配。
例如:
<?php // config.php return [ // ... OrderService::class => DI\object() ->constructorParameter('paramName', 'a value'), ];
这个解决方案一般是优选的,避免从新定义一切。
旁注:如规则n°3中所述,咱们建议对接口进行类型约束。 在这种状况下,您将须要将接口映射到容器在配置中应该使用的实现:
当使用库,如日志记录器,ORM,...有时须要配置它们。
在这种状况下,咱们建议您在配置文件中定义这些依赖关系。 咱们还建议在配置有点复杂时使用匿名函数。
匿名函数容许你编写真正的PHP代码,这是伟大的,由于你可使用库的文档,获得IDE支持,做为一个PHP开发人员,你懂的:)。
这里是一个例子,Monolog,一个PHP记录器:
<?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接口进行注入。 这样,咱们能够随时用任何PSR-3记录器替换Monolog,只需更改此配置便可。