Dipendenza di auto-iniezione
In webman, la dipendenza di auto-iniezione è una funzionalità opzionale e di default è disattivata. Se hai bisogno della dipendenza di auto-iniezione, ti consigliamo di utilizzare php-di. Di seguito è riportato come webman è combinato con php-di
.
Installazione
compositore richiede psr/container ^1.1.1 php-di/php-di ^6 doctrine/annotations ^1.14
Modificare la configurazione config/container.php
, il cui contenuto finale è il seguente:
$builder = new \DI\ContainerBuilder();
$builder->addDefinitions(config('dipendenza', []));
$builder->useAutowiring(true);
$builder->useAnnotations(true);
return $builder->build();
Il file
config/container.php
restituisce un'istanza del contenitore in conformità con lo standardPSR-11
. Se non si desidera utilizzarephp-di
, è possibile creare e restituire un'altra istanza del contenitore in conformità con lo standardPSR-11
qui.
Iniezione del costruttore
Crea il file app/service/Mailer.php
(se la cartella non esiste, creala) con il seguente contenuto:
<?php
namespace app\service;
class Mailer
{
public function mail($email, $content)
{
// Codice di invio email omesso
}
}
Il contenuto di app/controller/UserController.php
è il seguente:
<?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', 'Ciao e benvenuto!');
return response('ok');
}
}
Nelle normali circostanze, il codice seguente è necessario per istanziare app\controller\UserController
:
$mailer = new Mailer;
$user = new UserController($mailer);
Dopo aver utilizzato php-di
, il programmatore non deve istanziare manualmente il Mailer
all'interno del controller e webman lo farà automaticamente per te. Se durante il processo di istanziazione del Mailer
ci sono altre dipendenze di classe, webman le istanzierà e le inietterà automaticamente. Il programmatore non ha bisogno di alcun lavoro iniziale.
Nota
Solo le istanze create dal framework o daphp-di
possono completare l'iniezione di dipendenze automatica. Le istanze create manualmente connew
non possono completare l'iniezione di dipendenze. Per eseguire l'iniezione, è necessario utilizzare l'interfacciasupport\Container
al posto dello statementnew
, ad esempio:
use app\service\UserService;
use app\service\LogService;
use support\Container;
// Le istanze create con la parola chiave 'new' non possono essere iniettate come dipendenze
$user_service = new UserService;
// Le istanze create con la parola chiave 'new' non possono essere iniettate come dipendenze
$log_service = new LogService($path, $name);
// Le istanze create con Container possono essere iniettate come dipendenze
$user_service = Container::get(UserService::class);
// Le istanze create con Container possono essere iniettate come dipendenze
$log_service = Container::make(LogService::class, [$path, $name]);
Iniezione dell'annotazione
Oltre all'iniezione del costruttore delle dipendenze automatiche, è possibile utilizzare anche l'iniezione dell'annotazione. Continuando con l'esempio precedente, il controller app\controller\UserController
viene modificato come segue:
<?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', 'Ciao e benvenuto!');
return response('ok');
}
}
In questo esempio, l'iniezione avviene tramite l'annotazione @Inject
e il tipo dell'oggetto è dichiarato tramite l'annotazione @var
. Questo esempio è equivalente all'iniezione del costruttore, ma il codice è più conciso.
Nota
Webman non supporta l'iniezione dei parametri del controller prima della versione 1.4.6, ad esempio il seguente codice non è supportato quando webman <= 1.4.6:
<?php
namespace app\controller;
use support\Request;
use app\service\Mailer;
class UserController
{
// Prima della versione 1.4.6, l'iniezione dei parametri del controller non è supportata
public function register(Request $request, Mailer $mailer)
{
$mailer->mail('hello@webman.com', 'Ciao e benvenuto!');
return response('ok');
}
}
Iniezione personalizzata dei costruttori
A volte i parametri passati al costruttore possono non essere istanze della classe, ma stringhe, numeri, array e così via. Ad esempio, il costruttore del Mailer richiede l'indirizzo IP e la porta del server SMTP:
<?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)
{
// Codice di invio email omesso
}
}
In questo caso, non è possibile utilizzare direttamente l'iniezione del costruttore come descritto in precedenza, poiché php-di
non può determinare i valori di $smtp_host
e $smtp_port
. In questo caso, si può provare con l'iniezione personalizzata.
Nel file config/dependence.php
(se il file non esiste, crealo) aggiungi il seguente codice:
return [
// ... Altre configurazioni omesse qui
app\service\Mailer::class => new app\service\Mailer('192.168.1.11', 25);
];
In questo modo, quando la dipendenza ha bisogno di ottenere un'istanza di app\service\Mailer
, userà automaticamente l'istanza di app\service\Mailer
creata in questa configurazione.
Notiamo che in config/dependence.php
si utilizza new
per istanziare la classe Mailer
, che non è un problema in questo esempio, ma immagina se la classe Mailer
dipendesse da altre classi o se all'interno della classe Mailer
fosse utilizzata l'iniezione dell'annotazione, l'inizializzazione con new
non causerà l'iniezione di dipendenze automatica. La soluzione è utilizzare l'iniezione personalizzata dell'interfaccia, utilizzando il metodo Container::get(NomeClasse)
o Container::make(NomeClasse, [Parametri del costruttore])
per inizializzare la classe.
Iniezione personalizzata dell'interfaccia
Nel contesto di un progetto reale, preferiamo programmare attraverso delle interfacce piuttosto che classi concrete. Ad esempio,invece di importare app\service\Mailer
, dovremmo importare app\service\MailerInterface
nella classe app\controller\UserController
.
Definiamo l'interfaccia MailerInterface
.
<?php
namespace app\service;
interface MailerInterface
{
public function mail($email, $content);
}
Definiamo l'implementazione dell'interfaccia 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)
{
// Codice di invio email omesso
}
}
Importiamo l'interfaccia MailerInterface
anziché l'implementazione concreta.
<?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', 'Ciao e benvenuto!');
return response('ok');
}
}
config/dependence.php
definisce l'implementazione dell'interfaccia MailerInterface
come segue.
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]);
}
];
In questo modo, quando il business necessita di utilizzare l'interfaccia MailerInterface
, verrà automaticamente utilizzata l'implementazione Mailer
.
Il vantaggio della programmazione orientata all'interfaccia è che, se dobbiamo sostituire un componente, non è necessario modificare il codice di business, ma solo l'implementazione concreta in
config/dependence.php
. Questo è particolarmente utile anche per il testing unitario.
Altre Iniezioni personalizzate
In config/dependence.php
, oltre a definire le dipendenze delle classi, è possibile definire anche altri tipi di valori come stringhe, numeri, array, ecc.
Ad esempio, in config/dependence.php
viene definito quanto segue:
return [
'smtp_host' => '192.168.1.11',
'smtp_port' => 25
];
A questo punto possiamo utilizzare l'annotazione @Inject
per iniettare smtp_host
e smtp_port
nelle proprietà della classe.
<?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)
{
// Codice di invio email omesso
echo "{$this->smtpHost}:{$this->smtpPort}\n"; // Stamperà 192.168.1.11:25
}
}
Nota:
@Inject("key")
usa le virgolette.
Ulteriori informazioni
Si prega di fare riferimento al manuale di php-di