nette / di
💎 Nette 依赖注入容器:灵活、编译过的完整功能 DIC,具有完美可用的自动装配和支持所有新的 PHP 功能。
Requires
- php: 8.1 - 8.3
- ext-ctype: *
- ext-tokenizer: *
- nette/neon: ^3.3 || ^4.0
- nette/php-generator: ^4.1.3
- nette/robot-loader: ^4.0
- nette/schema: ^1.2.5
- nette/utils: ^4.0
Requires (Dev)
- nette/tester: ^2.5.2
- phpstan/phpstan: ^1.0
- tracy/tracy: ^2.9
- dev-master / 4.0.x-dev
- v3.2.x-dev
- v3.2.2
- v3.2.2-RC
- v3.2.1
- v3.2.0
- v3.1.x-dev
- v3.1.10
- v3.1.8
- v3.1.3
- v3.1.2
- v3.1.1
- v3.1.0
- v3.0.x-dev
- v3.0.17
- v3.0.16
- v3.0.15
- v3.0.14
- v3.0.13
- v3.0.12
- v3.0.11
- v3.0.10
- v3.0.9
- v3.0.8
- v3.0.7
- v3.0.6
- v3.0.5
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.4.x-dev
- v2.4.17
- v2.4.16
- v2.4.15
- v2.4.14
- v2.4.13
- v2.4.12
- v2.4.11
- v2.4.10
- v2.4.9
- v2.4.8
- v2.4.7
- v2.4.6
- v2.4.5
- v2.4.4
- v2.4.3
- v2.4.2
- v2.4.1
- v2.4.0
- v2.3.x-dev
- v2.3.14
- v2.3.13
- v2.3.12
- v2.3.11
- v2.3.10
- v2.3.9
- v2.3.8
- v2.3.7
- v2.3.6
- v2.3.5
- v2.3.4
- v2.3.3
- v2.3.2
- v2.3.1
- v2.3.0
- v2.2.x-dev
- v2.2.6
- v2.2.5
- v2.2.4
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- dev-lock-windows
This package is auto-updated.
Last update: 2024-09-18 21:50:39 UTC
README
介绍
依赖注入(DI)的目的是让类从获取其操作所需对象的责任中解放出来(这些对象称为 服务)。而是在它们实例化时传递这些服务。
Nette DI 是框架中最有趣的部分之一。它是一个编译过的 DI 容器,非常快且易于配置。
文档可以在 网站上找到。
支持我
你喜欢 Nette DI 吗?你在期待新功能吗?
谢谢!
安装
推荐安装方式是通过 Composer
composer require nette/di
它需要 PHP 版本 8.1,并支持 PHP 8.4。
用法
让我们有一个用于发送新闻通讯的应用程序。代码被最大限度地简化,并在 GitHub 上提供。
有一个代表电子邮件的对象
class Mail { public string $subject; public string $message; }
一个可以发送电子邮件的对象
interface Mailer { function send(Mail $mail, string $to): void; }
日志记录支持
interface Logger { function log(string $message): void; }
最后,一个提供发送新闻通讯的类
class NewsletterManager { private Mailer $mailer; private Logger $logger; public function __construct(Mailer $mailer, Logger $logger) { $this->mailer = $mailer; $this->logger = $logger; } public function distribute(array $recipients): void { $mail = new Mail; $mail->subject = '...'; $mail->message = '...'; foreach ($recipients as $recipient) { $this->mailer->send($mail, $recipient); } $this->logger->log('...'); } }
代码遵循依赖注入原则,即 每个对象只使用我们传递给它的变量。
此外,我们还可以实现自己的 Logger
或 Mailer
,如下所示
class SendMailMailer implements Mailer { public function send(Mail $mail, string $to): void { mail($to, $mail->subject, $mail->message); } } class FileLogger implements Logger { private string $file; public function __construct(string $file) { $this->file = $file; } public function log(string $message): void { file_put_contents($this->file, $message . "\n", FILE_APPEND); } }
DI 容器是最高建筑师,它可以创建单个对象(在 DI 术语中称为服务)并根据我们的需求组装和配置它们。
我们的应用程序的容器可能看起来像这样
class Container { private ?Logger $logger; private ?Mailer $mailer; public function getLogger(): Logger { if (!isset($this->logger)) { $this->logger = new FileLogger('log.txt'); } return $this->logger; } public function getMailer(): Mailer { if (!isset($this->mailer)) { $this->mailer = new SendMailMailer; } return $this->mailer; } public function createNewsletterManager(): NewsletterManager { return new NewsletterManager($this->getMailer(), $this->getLogger()); } }
实现看起来是这样,因为
- 单个服务仅在需要时创建(延迟加载)
- 双重调用
createNewsletterManager
将使用相同的日志记录器和邮件发送器实例
让我们实例化 Container
,让它创建管理器,然后我们可以开始用新闻通讯轰炸用户了 :-)
$container = new Container; $manager = $container->createNewsletterManager(); $manager->distribute(...);
对依赖注入来说重要的是,没有类依赖于容器。因此,它可以很容易地被另一个容器替换。例如,被 Nette DI 生成的容器。
Nette DI
Nette DI 是容器的生成器。我们通常用配置文件来指导它。这是配置文件,它导致生成与上面提到的 Container
类几乎相同的类
services: - FileLogger( log.txt ) - SendMailMailer - NewsletterManager
优点是配置的简短性。
Nette DI 实际上生成了容器的 PHP 代码。因此,它非常快。开发者可以看到代码,因此他知道它正在做什么。他甚至可以跟踪它。
Nette DI 的用法非常简单。将(上述)配置保存到文件 config.neon
,然后创建一个容器
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp'); $class = $loader->load(function($compiler) { $compiler->loadConfig(__DIR__ . '/config.neon'); }); $container = new $class;
然后使用容器创建对象 NewsletterManager
并发送电子邮件
$manager = $container->getByType(NewsletterManager::class); $manager->distribute(['[email protected]', ...]);
容器只生成一次,代码存储在缓存(在目录 __DIR__ . '/temp'
)中。因此,配置文件的加载放在 $loader->load()
中的闭包中,所以它只调用一次。
在开发期间,激活自动刷新模式非常有用,该模式可以在任何类或配置文件更改时自动重新生成容器。只需在 ContainerLoader
构造函数中将 true
作为第二个参数附加即可
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp', autoRebuild: true);
服务
服务在 DI 容器中注册,并且它们的依赖项会自动传递。
services: manager: NewsletterManager
此服务构造函数中声明的所有依赖项都将自动传递。构造函数传递是服务依赖注入的首选方式。
如果我们想通过setter传递依赖项,我们可以在服务定义中添加setup
部分。
services: manager: factory: NewsletterManager setup: - setAnotherService
服务类
class NewsletterManager { private AnotherService $anotherService; public function setAnotherService(AnotherService $service): void { $this->anotherService = $service; } ...
我们还可以添加inject: yes
指令。此指令将启用自动调用inject*
方法,并将依赖项传递给具有#[Inject]属性的公共变量。
services: foo: factory: FooClass inject: yes
依赖项Service1
将通过调用inject*
方法传递,依赖项Service2
将分配给$service2
变量。
use Nette\DI\Attributes\Inject; class FooClass { private Service1 $service1; // 1) inject* method: public function injectService1(Service1 $service): void { $this->service1 = $service1; } // 2) Assign to the variable with the #[Inject] attribute: #[Inject] public Service2 $service2; }
然而,这种方法并不理想,因为变量必须声明为公共的,而且没有方法可以确保传递的对象是特定类型的。我们还失去了在代码中处理分配的依赖项的能力,并且违反了封装原则。
工厂
我们可以使用从接口生成的工厂。接口必须声明方法的返回类型。Nette将生成接口的正确实现。
接口必须恰好有一个名为create
的方法。我们的工厂接口可以声明如下:
interface BarFactory { function create(): Bar; }
create
方法将使用以下定义实例化一个Bar
。
class Bar { private Logger $logger; public function __construct(Logger $logger) { $this->logger = $logger; } }
工厂将在config.neon
文件中注册。
services: - BarFactory
Nette将检查声明的服务是否为接口。如果是,它还将生成相应的工厂实现。定义也可以写成更详细的形式:
services: barFactory: implement: BarFactory
此完整定义允许我们使用arguments
和setup
部分声明对象的附加配置,类似于所有其他服务。
在我们的代码中,我们只需要获取工厂实例并调用create
方法。
class Foo { private BarFactory $barFactory; function __construct(BarFactory $barFactory) { $this->barFactory = $barFactory; } function bar(): void { $bar = $this->barFactory->create(); } }