miladrahimi / phpcontainer
PHP项目的依赖注入(IoC)容器
Requires
- php: >=7.4
- psr/container: ^2.0
Requires (Dev)
- phpunit/phpunit: ^7|^8|^9
README
PhpContainer
使用PHP编程语言编写的依赖注入(控制反转)容器,符合PSR-11标准。
特性
- 单例、短暂和闭包绑定
- 显式和隐式绑定
- 类型和命名绑定
- 自动注入
概述
依赖反转 是面向对象设计中的一个基本概念。
它引出了诸如 依赖注入、控制反转 以及创建 IoC容器 的重要思想。
对于PHP项目,有PhpContainer这个方便的工具,它提供了一个符合 PSR-11标准 的依赖注入容器(IoC容器)。
安装
要将PhpContainer集成到项目中,请使用以下Composer命令
composer require miladrahimi/phpcontainer:5.*
文档
显式绑定
显式绑定涉及直接将一个抽象与一个具体实现相连接。这种绑定可以通过使用 singleton()
、transient()
和 closure()
方法实现。
use MiladRahimi\PhpContainer\Container; $container = new Container(); $container->singleton(DatabaseInterface::class, MySQL::class); $container->transient(MailerInterface::class, MailTrap::class); $container->closure('sum', function($a, $b) { return $a + $b; }); $database = $container->get(DatabaseInterface::class); // An instance of MySQL $mailer = $container->get(MailerInterface::class); // An instance of MailTrap $sum = $container->get('sum'); // A closure: $sum(6, 7) => 13
绑定方法
- 单例绑定:容器只创建一次具体实现,并在需要时返回它。
- 短暂绑定:每次需要时,容器都会克隆或创建全新的具体实现。
- 闭包绑定:仅适用于闭包。它防止容器调用闭包(默认行为)。
以下示例演示了单例绑定和短暂绑定的区别。
use MiladRahimi\PhpContainer\Container; $container = new Container(); $container->transient(InterfaceA::class, ClassA::class); $container->singleton(InterfaceB::class, ClassB::class); $a1 = $container->get(InterfaceA::class); $a1->name = 'Something'; $a2 = $container->get(InterfaceA::class); echo $a2->name; // NULL $b1 = $container->get(InterfaceB::class); $b1->name = 'Something'; $b2 = $container->get(InterfaceB::class); echo $b2->name; // 'Something'
隐式绑定
当容器需要没有特定绑定的类时,它会尝试创建一个实例。在下面的示例中,它实例化了提供的代码中的 MySQL
类。但如果它遇到无法直接实例化的抽象类或接口,将发生错误。
use MiladRahimi\PhpContainer\Container; $container = new Container(); // No (explicit) binding here! $database = $container->get(MySQL::class);
绑定到对象
您可以将摘要连接到特定对象。使用单例绑定可以在需要时获取原始对象,而瞬态绑定每次请求时都提供对象的新副本。
use MiladRahimi\PhpContainer\Container; $user = new User(); $user->name = 'Milad'; $container = new Container(); $container->singleton('user', $user); // OR $container->transient('user', $user);
构造函数自动注入
具体类可能包含具有默认值或可以被容器解析的构造函数参数。
use MiladRahimi\PhpContainer\Container; class Notifier implements NotifierInterface { public MailInterface $mail; public Vonage $vonage; public string $sender; public function __constructor(MailInterface $mail, Vonage $vonage, $sender = 'PhpContainer') { $this->mail = $mail; $this->vonage = $vonage; $this->sender = $sender; } } $container = new Container(); $container->transient(MailInterface::class, MailTrap::class); $container->transient(NotifierInterface::class, Notifier::class); $notifier = $container->get(NotifierInterface::class); print_r($notifier->mail); // $mail would be an instnace of MailTrap (explicit binding) print_r($notifier->vonage); // $vonage would be an instnace of Vonage (implicit binding) print_r($notifier->sender); // $sender would be "PhpContainer" (default value)
使用闭包进行绑定
以下示例说明了如何使用闭包进行绑定。
use MiladRahimi\PhpContainer\Container; $container = new Container(); $container->singleton(Config::class, function () { return new JsonConfig('/path/to/config.json'); }); // $config would be auto-injected $container->singleton(Database::class, function (Config $config) { return new MySQL( $config->get('database.host'), $config->get('database.port'), $config->get('database.name'), $config->get('database.username'), $config->get('database.password') ); });
在单例绑定中,容器执行闭包一次,并在需要时检索结果。相反,在瞬态绑定中,容器每次需要时都会调用闭包。如果您打算将抽象绑定到闭包,而容器不需要立即执行,则可以使用closure()
方法。
使用闭包解析
您可以使用call
方法,允许容器执行提供的函数或闭包并解析其参数。
use MiladRahimi\PhpContainer\Container; $container = new Container(); $container->singleton(MailInterface::class, MailTrap::class); // Direct Closure call $response = $container->call(function(MailerInterface $mailer) { return $mailer->send('info@example.com', 'Hello...'); }); // Direct function call function sendMail(MailerInterface $mailer) { return $mailer->send('info@example.com', 'Hello...'); } $response = $container->call('sendMail'); // Direct method call class UserManager { function sendMail(MailerInterface $mailer) { return $mailer->send('info@example.com', 'Hello...'); } } $response = $container->call([UserManager::class, 'sendMail']);
基于类型和基于名称的绑定
PhpContainer支持基于类型和基于名称的绑定。以下示例演示了这些类型的绑定。
use MiladRahimi\PhpContainer\Container; $container = new Container(); // Type-based binding $container->singleton(Database::class, MySQL::class); $container->call(function(Database $database) { $database->ping(); }); // Name-based binding $container->singleton('$number', 666); $container->call(function($number) { echo $number; // 666 });
错误处理
容器可能会由于多种原因引发ContainerException
。它可能在发生ReflectionException
时出现,这表明提供了抽象缺少具体实现。此外,当容器无法将参数值注入具体构造函数或闭包时,也会发生此异常。
许可证
PhpContainer由Milad Rahimi创建,并按照MIT许可证发布。