Abhängigkeiten automatisch injizieren
In Webman ist die automatische Injektion von Abhängigkeiten eine optionale Funktion, die standardmäßig deaktiviert ist. Wenn Sie die automatische Injektion von Abhängigkeiten benötigen, wird empfohlen, php-di zu verwenden. Im Folgenden finden Sie die Verwendung von Webman in Kombination mit php-di
.
Installation
composer require psr/container ^1.1.1 php-di/php-di ^6.3 doctrine/annotations ^1.14
Ändern Sie die Konfiguration in config/container.php
, der endgültige Inhalt sollte wie folgt aussehen:
$builder = new \DI\ContainerBuilder();
$builder->addDefinitions(config('dependence', []));
$builder->useAutowiring(true);
$builder->useAnnotations(true);
return $builder->build();
In
config/container.php
wird schließlich eine Container-Instanz zurückgegeben, die demPSR-11
Standard entspricht. Wenn Siephp-di
nicht verwenden möchten, können Sie hier eine andere Container-Instanz erstellen und zurückgeben, die demPSR-11
Standard entspricht.
Konstruktorinjektion
Erstellen Sie app/service/Mailer.php
(falls das Verzeichnis nicht existiert, erstellen Sie es bitte selbst) mit folgendem Inhalt:
<?php
namespace app\service;
class Mailer
{
public function mail($email, $content)
{
// Der Code zum Senden von E-Mails ist hier weggelassen
}
}
Der Inhalt von app/controller/UserController.php
sieht wie folgt aus:
<?php
namespace app\controller;
use support\Request;
use app\service\Mailer;
class UserController
{
private $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
public function register(Request $request)
{
$this->mailer->mail('hello@webman.com', 'Hello und willkommen!');
return response('ok');
}
}
Normalerweise muss der folgende Code geschrieben werden, um eine Instanz von app\controller\UserController
zu erstellen:
$mailer = new Mailer;
$user = new UserController($mailer);
Wenn php-di
verwendet wird, müssen Entwickler Mailer
im Controller nicht manuell instanziieren; Webman wird dies automatisch für Sie erledigen. Wenn beim Instanziieren von Mailer
andere Klassen abhängen, wird Webman diese auch automatisch instanziieren und injizieren. Die Entwickler benötigen keine weiteren Initialisierungsarbeiten.
Hinweis
Nur Instanzen, die vom Framework oderphp-di
erzeugt wurden, können die automatische Abhängigkeitsinjektion durchführen. Manuell mitnew
erstellte Instanzen können dies nicht. Wenn eine Injektion erforderlich ist, verwenden Sie bitte dassupport\Container
Interface, um dienew
Anweisung zu ersetzen, zum Beispiel:
use app\service\UserService;
use app\service\LogService;
use support\Container;
// Mit new erstellte Instanzen können nicht injiziert werden
$user_service = new UserService;
// Mit new erstellte Instanzen können nicht injiziert werden
$log_service = new LogService($path, $name);
// Instanzen, die mit Container erstellt wurden, können injiziert werden
$user_service = Container::get(UserService::class);
// Instanzen, die mit Container erstellt wurden, können injiziert werden
$log_service = Container::make(LogService::class, [$path, $name]);
Annotationsinjektion
Neben der Konstruktorinjektion können wir auch Annotationsinjektion verwenden. Führen Sie das oben genannte Beispiel fort, ändern Sie app\controller\UserController
wie folgt:
<?php
namespace app\controller;
use support\Request;
use app\service\Mailer;
use DI\Annotation\Inject;
class UserController
{
/**
* @Inject
* @var Mailer
*/
private $mailer;
public function register(Request $request)
{
$this->mailer->mail('hello@webman.com', 'Hello und willkommen!');
return response('ok');
}
}
Dieses Beispiel injiziert mit dem @Inject
Annotation, und der Typ des Objekts wird durch die @var
Annotation deklariert. Dieses Beispiel hat die gleiche Wirkung wie die Konstruktorinjektion, ist jedoch im Code kompakter.
Hinweis
Webman unterstützt vor Version 1.4.6 keine Parameterinjektion in Controllern. Der folgende Code wird bei Webman <= 1.4.6 nicht unterstützt.
<?php
namespace app\controller;
use support\Request;
use app\service\Mailer;
class UserController
{
// Vor Version 1.4.6 wurde die Parameterinjektion im Controller nicht unterstützt
public function register(Request $request, Mailer $mailer)
{
$mailer->mail('hello@webman.com', 'Hello und willkommen!');
return response('ok');
}
}
Benutzerdefinierte Konstruktorinjektion
Manchmal sind die Parameter, die im Konstruktor übergeben werden, möglicherweise keine Instanzen von Klassen, sondern Strings, Zahlen, Arrays und andere Daten. Zum Beispiel benötigt der Konstruktor von Mailer die IP und den Port des SMTP-Servers:
<?php
namespace app\service;
class Mailer
{
private $smtpHost;
private $smtpPort;
public function __construct($smtp_host, $smtp_port)
{
$this->smtpHost = $smtp_host;
$this->smtpPort = $smtp_port;
}
public function mail($email, $content)
{
// Der Code zum Senden von E-Mails ist hier weggelassen
}
}
In solchen Fällen kann die zuvor beschriebene automatische Konstruktorinjektion nicht direkt verwendet werden, da php-di
nicht bestimmen kann, was die Werte von $smtp_host
und $smtp_port
sind. In diesem Fall können Sie versuchen, benutzerdefinierte Injektionen zu verwenden.
Fügen Sie in config/dependence.php
(falls die Datei nicht existiert, erstellen Sie sie bitte selbst) den folgenden Code hinzu:
return [
// ... Andere Konfigurationen werden hier ignoriert
app\service\Mailer::class => new app\service\Mailer('192.168.1.11', 25);
];
Wenn die Abhängigkeitsinjektion eine Instanz von app\service\Mailer
anfordert, wird automatisch diese in der Konfiguration erstellte app\service\Mailer
Instanz verwendet.
Es ist zu beachten, dass in config/dependence.php
new
verwendet wird, um die Klasse Mailer
zu instanziieren. Dies ist in diesem Beispiel unproblematisch, aber stellen Sie sich vor, wenn die Mailer
-Klasse von anderen Klassen abhängt oder die Mailer
-Klasse interne Annotationsinjektionen verwendet, wird die Verwendung von new
keine automatische Injektion ermöglichen. Eine Lösung besteht darin, benutzerdefinierte Schnittstelleninjektionen zu verwenden, um die Klasse über die Methoden Container::get(klassename)
oder Container::make(klassename, [Konstruktorparameter])
zu initialisieren.
Benutzerdefinierte Schnittstelleninjektion
In realen Projekten wünschen wir uns in der Regel, interfacebasiert zu programmieren, anstatt konkrete Klassen zu verwenden. Zum Beispiel sollte in app\controller\UserController
app\service\MailerInterface
anstelle von app\service\Mailer
importiert werden.
Definieren Sie das MailerInterface
.
<?php
namespace app\service;
interface MailerInterface
{
public function mail($email, $content);
}
Definieren Sie die Implementierung des MailerInterface
.
<?php
namespace app\service;
class Mailer implements MailerInterface
{
private $smtpHost;
private $smtpPort;
public function __construct($smtp_host, $smtp_port)
{
$this->smtpHost = $smtp_host;
$this->smtpPort = $smtp_port;
}
public function mail($email, $content)
{
// Der Code zum Senden von E-Mails ist hier weggelassen
}
}
Importieren Sie das MailerInterface
anstelle der konkreten Implementierung.
<?php
namespace app\controller;
use support\Request;
use app\service\MailerInterface;
use DI\Annotation\Inject;
class UserController
{
/**
* @Inject
* @var MailerInterface
*/
private $mailer;
public function register(Request $request)
{
$this->mailer->mail('hello@webman.com', 'Hello und willkommen!');
return response('ok');
}
}
In config/dependence.php
wird das MailerInterface
wie folgt definiert.
use Psr\Container\ContainerInterface;
return [
app\service\MailerInterface::class => function(ContainerInterface $container) {
return $container->make(app\service\Mailer::class, ['smtp_host' => '192.168.1.11', 'smtp_port' => 25]);
}
];
So wird bei Bedarf die Implementierung von Mailer
automatisch verwendet, wenn die Verwendung des MailerInterface
erforderlich ist.
Der Vorteil der Schnittstellenprogrammierung besteht darin, dass wir beim Ersetzen einer Komponente den Anwendungscode nicht ändern müssen; wir müssen nur die konkrete Implementierung in
config/dependence.php
ändern. Dies ist auch bei der Durchführung von Unit-Tests äußerst nützlich.
Weitere benutzerdefinierte Injektionen
In config/dependence.php
können nicht nur Klassenabhängigkeiten definiert werden, sondern auch andere Werte wie Strings, Zahlen, Arrays usw.
Zum Beispiel wird config/dependence.php
wie folgt definiert:
return [
'smtp_host' => '192.168.1.11',
'smtp_port' => 25
];
In diesem Fall können wir @Inject
verwenden, um smtp_host
und smtp_port
in die Eigenschaften der Klasse zu injizieren.
<?php
namespace app\service;
use DI\Annotation\Inject;
class Mailer
{
/**
* @Inject("smtp_host")
*/
private $smtpHost;
/**
* @Inject("smtp_port")
*/
private $smtpPort;
public function mail($email, $content)
{
// Der Code zum Senden von E-Mails ist hier weggelassen
echo "{$this->smtpHost}:{$this->smtpPort}\n"; // Gibt 192.168.1.11:25 aus
}
}
Hinweis: In
@Inject("key")
sind die Anführungszeichen wichtig
Weitere Inhalte
Bitte beachten Sie das php-di Handbuch