當前位置: 妍妍網 > 碼農

PHP 語言官方團隊推薦的依賴註入工具

2024-03-01碼農

依賴註入容器

依賴註入(Dependency Injection,DI)容器就是一個物件,它知道怎樣初始化並配置物件及其依賴的所有物件。

安裝

composer require php-di/php-di

基本用法

1.使用依賴註入

首先,讓我們使用依賴註入來編寫程式碼,而不考慮PHP-DI:

class Mailer
{
public function mail($recipient$content)
{
// send an email to the recipient
}
}

class UserManager
{
private $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
public function register($email$password)
{
// The user just registered, we create his account
// ...
// We send him an email to say hello!
$this->mailer->mail($email'Hello and welcome!');
}
}

正如我們所看到的, UserManager Mailer 作為構造器參數: 這就是依賴註入!

2.建立容器

您可以非常輕松地建立一個為開發預先配置的容器例項:

$container = new DI\Container();

如果你想註冊定義檔(在PHP定義中解釋)或調整一些選項,你可以使用容器構建器:

$builder = new DI\ContainerBuilder();
$builder->...
$container = $builder->build();

3.建立物件

如果沒有PHP-DI,我們將不得不像這樣手動「連線」依賴項:

$mailer = new Mailer();
$userManager = new UserManager($mailer);

相反,我們可以讓PHP-DI找出依賴關系:

$userManager = $container->get('UserManager');

在後台,PHP-DI將建立一個Mailer物件和一個UserManager物件。

它怎麽知道要註入什麽?容器使用一種稱為自動裝配的技術。這不是PHP-DI獨有的,但這仍然很棒。它將掃描程式碼並檢視建構函式中需要哪些參數。

在我們的範例中, UserManager 建構函式接受一個 Mailer 物件:PHP-DI知道它需要建立一個物件。非常簡單,但非常有效。

webman 框架套用

在webman裏依賴自動註入是可選功能,此功能預設關閉。如果你需要依賴自動註入,推薦使用php-di,以下是webman結合php-di的用法。

安裝

composer require psr/container ^1.1.1 php-di/php-di ^6 doctrine/annotations ^1.14

修改配置 config/container.php ,其最終內容如下:

$builder = new \DI\ContainerBuilder();
$builder->addDefinitions(config('dependence', []));
$builder->useAutowiring(true);
$builder->useAnnotations(true);
return$builder->build();

config/container.php 裏最終返回一個符合 PSR-11 規範的容器例項。如果你不想使用 php-di ,可以在這裏建立並返回一個其它符合 PSR-11 規範的容器例項。

建構函式註入

新建 app/service/Mailer.php (如目錄不存在請自行建立)內容如下:

<?php
namespaceapp\service;
classMailer
{
publicfunctionmail($email, $content)
{
// 發送信件程式碼省略
}
}

app/controller/UserController.php 內容如下:

<?php
namespaceapp\controller;
usesupport\Request;
useapp\service\Mailer;
classUserController
{
private $mailer;
publicfunction__construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
publicfunctionregister(Request $request)
{
$this->mailer->mail('[email protected]''Hello and welcome!');
return response('ok');
}
}


正常情況下,需要以下程式碼才能完成 app\controller\UserController 的例項化:

$mailer = new Mailer;
$user = new UserController($mailer);

當使用 php-di 後,開發者無需手動例項化控制器中的 Mailer ,webman會自動幫你完成。如果在例項化 Mailer 過程中有其它類的依賴,webman也會自動例項化並註入。開發者不需要任何的初始化工作。

註意 必須是由框架或者 php-di 建立的例項才能完成依賴自動註入,手動 new 的例項無法完成依賴自動註入,如需註入,需要使用 support\Container 介面替換 new 語句,例如:

useapp\service\UserService;
useapp\service\LogService;
usesupport\Container;
// new關鍵字建立的例項無法依賴註入
$user_service = new UserService;
// new關鍵字建立的例項無法依賴註入
$log_service = new LogService($path, $name);
// Container建立的例項可以依賴註入
$user_service = Container::get(UserService:: class);
// Container建立的例項可以依賴註入
$log_service = Container::make(LogService:: class, [$path, $name]);

註解註入

除了建構函式依賴自動註入,我們還可以使用註解註入。繼續上面的例子, app\controller\UserController 更改成如下:

<?php
namespaceapp\controller;
usesupport\Request;
useapp\service\Mailer;
useDI\Annotation\Inject;
classUserController
{
/**
@Inject
@var Mailer
*/

private $mailer;
publicfunctionregister(Request $request)
{
$this->mailer->mail('[email protected]''Hello and welcome!');
return response('ok');
}
}

這個例子透過 @Inject 註解註入,並且由 @var 註解聲明物件型別。這個例子和建構函式註入效果一樣,但是程式碼更精簡。

註意 webman在1.4.6版本之前不支持控制器參數註入,例如以下程式碼當webman<=1.4.6時是不支持的

<?php
namespaceapp\controller;
usesupport\Request;
useapp\service\Mailer;
classUserController
{
// 1.4.6版本之前不支持控制器參數註入
publicfunctionregister(Request $request, Mailer $mailer)
{
$mailer->mail('[email protected]''Hello and welcome!');
return response('ok');
}
}

自訂建構函式註入

有時候建構函式傳入的參數可能不是類的例項,而是字串、數位、陣列等數據。例如Mailer建構函式需要傳遞smtp伺服器ip和埠:

<?php
namespaceapp\service;
classMailer
{
private $smtpHost;
private $smtpPort;
publicfunction__construct($smtp_host, $smtp_port)
{
$this->smtpHost = $smtp_host;
$this->smtpPort = $smtp_port;
}
publicfunctionmail($email, $content)
{
// 發送信件程式碼省略
}
}


這種情況無法直接使用前面介紹的建構函式自動註入,因為 php-di 無法確定 $smtp_host $smtp_port 的值是什麽。這時候可以嘗試自訂註入。

config/dependence.php (檔不存在請自行建立)中加入如下程式碼:

return [
// ... 這裏忽略了其它配置
app\service\Mailer:: class => new app\service\Mailer('192.168.1.11'25);
];

這樣當依賴註入需要獲取 app\service\Mailer 例項時將自動使用這個配置中建立的 app\service\Mailer 例項。

我們註意到, config/dependence.php 中使用了 new 來例項化 Mailer 類,這個在本範例沒有任何問題,但是想象下如果 Mailer 類依賴了其它類的話或者 Mailer 類內部使用了註解註入,使用 new 初始化將不會依賴自動註入。解決辦法是利用自訂介面註入,透過 Container::get(類名) 或者 Container::make(類名, [建構函式參數]) 方法來初始化類。