Table Of Contents
Previous topicNext topicThis Page |
Injection de dépendance/Localisation de Service¶
Phalcon\Di est un composant qui met en œuvre l’Injection de Dépendance et la Localisation de Service et il est lui-même un conteneur pour cela. Comme Phalcon est fortement découplé, Phalcon\Di est essentiel pour intégrer les différents composants dans le framework. Le développeur peut également exploiter ce composant pour injecter des dépendances et gérer les instances globales des différentes classes utilisées dans l’application. A la base, ce composant implémente le patron Inversion de Contrôle. En appliquant cela, les objets ne recoivent pas leur dépendances en utilisant des accesseurs ou des constructeurs, mais en interrogeant un service injecteur de dépendance. Ceci réduit la complexité tant qu’il n’y aura qu’une seule façon d’obtenir les dépendances nécessaires au composant. De plus, ce patron augmente la testabilité du code, le rendant ainsi moins vulnérable aux erreurs. Inscription de services dans le conteneur¶Le framework comme le développeur peuvent inscrire des service. Lorqu’un composant A nécessite un composant B (ou une instance de cette classe) pour fonctionner, il peut demander le composant B au conteneur plutôt que créer une nouvelle instance du composant B. Cette façon de faire procure plusieurs avantages:
Plusieurs styles de définitions permettent d’inscrire les services: Inscription simple¶Comme vu précédemment, il existe plusieurs façons d’inscrire un service. Voici ceux que nous appelons “simple”: Chaîne de caractères (string)¶Ce mode s’attend à un nom de classe valide, retournant un objet de la classe spécifiée, qui si elle n’est pas chargée, le sera en utilisant un chargeur automatique de classes. Ce mode de définition ne permet pas de spécifier des arguments pour constructeur de la classe ni des paramètres: <?php
// Return new Phalcon\Http\Request();
$di->set(
"request",
"Phalcon\\Http\\Request"
);
Instance de classe¶Ce mode s’attend à un objet. Comme l’objet n’a pas besoin d’être résolu puisqu’il est déjà un objet, certains diront que ce n’est pas vraiment une injection de dépendance. Toutefois, cela peut être utile si vous souhaitez forcer la dépendance retournée à être toujours le même objet ou la même valeur: <?php
use Phalcon\Http\Request;
// Return new Phalcon\Http\Request();
$di->set(
"request",
new Request()
);
Fermetures (Closures)/Fonctions anonymes:¶Cette méthode offre une grande liberté pour construire les dépendances comme désirées, cependant il est difficile de changer extérieurement sans avoir à changer complètement la définition de la dépendance: <?php
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;
$di->set(
"db",
function () {
return new PdoMysql(
[
"host" => "localhost",
"username" => "root",
"password" => "secret",
"dbname" => "blog",
]
);
}
);
Certaines limites peuvent être contournées en passant des variables supplémentaires à l’environnement de la fermeture: <?php
use Phalcon\Config;
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;
$config = new Config(
[
"host" => "127.0.0.1",
"username" => "user",
"password" => "pass",
"dbname" => "my_database",
]
);
// Utilisation de la variable $config dans la portée courante.
$di->set(
"db",
function () use ($config) {
return new PdoMysql(
[
"host" => $config->host,
"username" => $config->username,
"password" => $config->password,
"dbname" => $config->name,
]
);
}
);
Vous pouvez également accéder à d’autres services DI en utilisant la méthode <?php
use Phalcon\Config;
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;
$di->set(
"config",
function () {
return new Config(
[
"host" => "127.0.0.1",
"username" => "utilisateur",
"password" => "mot_de_passe",
"dbname" => "ma_base_de_donnees",
]
);
}
);
// Avec le service 'config' du DI
$di->set(
"db",
function () {
$config = $this->get("config");
return new PdoMysql(
[
"host" => $config->host,
"username" => $config->username,
"password" => $config->password,
"dbname" => $config->name,
]
);
}
);
Inscription Complexe¶S’il est nécessaire de changer la définition d’un service sans devoir instancier/résoudre le service, nous devrons alors définir les services en utilisant la syntaxe tableau. La définition d’un service sous forme de tableau peut être un peu plus verbeuse: <?php
use Phalcon\Logger\Adapter\File as LoggerFile;
// Inscription d'un service "logger" avec un nom de classe et ses paramètres
$di->set(
"logger",
[
"className" => "Phalcon\\Logger\\Adapter\\File",
"arguments" => [
[
"type" => "parameter",
"value" => "../apps/logs/error.log",
]
]
]
);
// En utilisant une fonction anonyme
$di->set(
"logger",
function () {
return new LoggerFile("../apps/logs/error.log");
}
);
Les deux inscriptions précédentes produisent le même résultat. Cependant, la définition sous forme de tableau permet une altération des paramètres du service si nécessaire: <?php
// Changement du nom de service
$di->getService("logger")->setClassName("MyCustomLogger");
// Changement du premier paramètre sans instancier le logger
$di->getService("logger")->setParameter(
0,
[
"type" => "parameter",
"value" => "../apps/logs/error.log",
]
);
De plus, en utilisant la syntaxe tableau, vous pouvez exploiter trois type d’injection de dépendance: Injection de constructeur¶Ce type d’injection transmet les dépendances au constructeur de la classe. Admettons que nous ayons le composant suivant: <?php
namespace SomeApp;
use Phalcon\Http\Response;
class SomeComponent
{
/**
* @var Response
*/
protected $_response;
protected $_someFlag;
public function __construct(Response $response, $someFlag)
{
$this->_response = $response;
$this->_someFlag = $someFlag;
}
}
Le service peut être inscrit de cette façon: <?php
$di->set(
"response",
[
"className" => "Phalcon\\Http\\Response"
]
);
$di->set(
"someComponent",
[
"className" => "SomeApp\\SomeComponent",
"arguments" => [
[
"type" => "service",
"name" => "response",
],
[
"type" => "parameter",
"value" => true,
],
]
]
);
Le service “response” (Phalcon\Http\Response) est résolu pour être transmis en premier argument au constructeur, alors que le second est une valeur booléenne (true) transmise telle quelle. Injection d’accesseur¶Les classes peuvent posséder des accesseurs pour injecter des dépendances optionnelles. Nos précédentes classes peuvent être modifiées pour accepter des dépendances avec des accesseurs: <?php
namespace SomeApp;
use Phalcon\Http\Response;
class SomeComponent
{
/**
* @var Response
*/
protected $_response;
protected $_someFlag;
public function setResponse(Response $response)
{
$this->_response = $response;
}
public function setFlag($someFlag)
{
$this->_someFlag = $someFlag;
}
}
Un service avec une injection par accesseur peut être inscrite comme suit: <?php
$di->set(
"response",
[
"className" => "Phalcon\\Http\\Response",
]
);
$di->set(
"someComponent",
[
"className" => "SomeApp\\SomeComponent",
"calls" => [
[
"method" => "setResponse",
"arguments" => [
[
"type" => "service",
"name" => "response",
]
]
],
[
"method" => "setFlag",
"arguments" => [
[
"type" => "parameter",
"value" => true,
]
]
]
]
]
);
Injection de propriétés¶Une stratégie moins courante est d’injecter directement des dépendances ou des paramètres aux attributs publics de la classe: <?php
namespace SomeApp;
use Phalcon\Http\Response;
class SomeComponent
{
/**
* @var Response
*/
public $response;
public $someFlag;
}
Un service avec un injection de propriétés peut être inscrite comme suit: <?php
$di->set(
"response",
[
"className" => "Phalcon\\Http\\Response",
]
);
$di->set(
"someComponent",
[
"className" => "SomeApp\\SomeComponent",
"properties" => [
[
"name" => "response",
"value" => [
"type" => "service",
"name" => "response",
],
],
[
"name" => "someFlag",
"value" => [
"type" => "parameter",
"value" => true,
],
]
]
]
);
Les différents types de paramètre supportés sont les suivants:
La résolution d’un service dont la définition est complexe peut être légèrement plus lente que pour les définitions simples vues précédemment. Cependant, ceci fournit une approche plus robuste pour définir et injecter des services. Le mélange de différents types de définitions est permis. Chacun décide de la méthode d’inscription des service la plus appropriée en fonction des besoins de l’application. Forme tableau¶L’écriture sous forme de tableau est possible pour inscrire des services: <?php
use Phalcon\Di;
use Phalcon\Http\Request;
// Création du conteneur d'Injection de Dépendance
$di = new Di();
// D'après son nom
$di["request"] = "Phalcon\\Http\\Request";
// Chargement tardif avec une fonction anonyme
$di["request"] = function () {
return new Request();
};
// En inscrivant directement une instance
$di["request"] = new Request();
// Avec un tableau de définition
$di["request"] = [
"className" => "Phalcon\\Http\\Request",
];
Dans les exemples précédents, lorsque le framework doit accéder aux données demandées, il interroge le service identifié en tant que ‘request’ dans le conteneur. Le conteneur retourne une instance du service demandé. Le développeur peut éventuellement remplacer les composants selon ses besoins. Chacune des méthodes (vues dans les exemples précédents) utilisée pour définir/inscrire un service a ses avantages et ses inconvénients. C’est au développeur de choisir laquelle utiliser en fonction des exigences. Définir un service par une chaîne de caractères est simple mais manque de souplesse. Définir un service par un tableau offre plus de flexibilité mais rend le code plus compliqué. La fonction lambda est un bon équilibre entre les deux mais risque de nécessiter plus de maintenance que nécessaire. Phalcon\Di offre un chargement tardif pour chaque service qu’il stocke. A moins que le développeur choisisse d’instancier directement et de le stocker dans le conteneur, chaque objet qui lui est confié (via tableau, chaîne de caractères, etc.) sera chargé tardivement c.à.d instancié lors de la demande. Résolution de services¶L’obtention d’un service à partir d’un conteneur peut se faire simplement en utilisant la méthode “get”. Une nouvelle instance du service sera retournée: <?php $request = $di->get("request");
Ou en invoquant la méthode magique: <?php
$request = $di->getRequest();
Ou en utilisant l’écriture tableau: <?php
$request = $di["request"];
Les arguments sont transmis au constructeur en ajoutant un tableau en paramètre de la méthode “get”: <?php
// new MyComponent("some-parameter", "other")
$component = $di->get(
"MyComponent",
[
"some-parameter",
"other",
]
);
Evénements¶Phalcon\Di est capable d’envoyer des événements à un EventsManager s’il existe. Les événements sont déclenchés en utilisant le type “di”. Les événements qui retourne la valeur booléenne faux peuvent interrompre l’opération en cours. Les événements suivants son supportés:
Services partagés¶Les services peuvent être inscrits en tant que service “partagé”. Ceci signifie qu’ils se comporteront toujours comme des singletons. Une fois que le service est résolu une première fois la même instance est systématiquement retournée lorsqu’un consommateur récupère le service depuis le conteneur: <?php
use Phalcon\Session\Adapter\Files as SessionFiles;
// Inscription du service de session comme "toujours partagé"
$di->setShared(
"session",
function () {
$session = new SessionFiles();
$session->start();
return $session;
}
);
// Localisation du service pour la première fois
$session = $di->get("session");
// Retourne l'objet instancié initialement
$session = $di->getSession();
Une autre façon d’inscrire des services partagés est de transmettre “true” au troisième paramètre de “set”: <?php
// Inscription du service de session comme "toujours partagé"
$di->set(
"session",
function () {
// ...
},
true
);
Si un service n’est pas inscrit comme partagé et vous voulez être sûr d’accéder à une instance partagée à chaque fois que le service est obtenu auprès de DI, vous pouvez utiliser la méthode ‘getShared’: <?php
$request = $di->getShared("request");
Manipuler les services individuellement¶Une fois qu’un service est inscrit dans le conteneur de services, vous pouvez le récupérer pour le manipuler individuellement: <?php
use Phalcon\Http\Request;
// Inscription du service "request"
$di->set("request", "Phalcon\\Http\\Request");
// Récupère le service
$requestService = $di->getService("request");
// Modifie sa définition
$requestService->setDefinition(
function () {
return new Request();
}
);
// Le transforme en "partagé"
$requestService->setShared(true);
// Résolution du service (retourne un instance de Phalcon\Http\Request)
$request = $requestService->resolve();
Instanciation de classes via le Conteneur de Services¶Lorsque vous demandez un service au conteneur de services, s’il n’en trouve pas un avec le même nom, il tente de charger une classe avec le même nom. Grâce à ce comportement nous pouvons remplacer n’importe quelle autre simplement en inscrivant un service avec son nom: <?php
// Inscription d'un contrôleur en tant que service
$di->set(
"IndexController",
function () {
$component = new Component();
return $component;
},
true
);
// Inscription d'un contrôleur en tant que service
$di->set(
"MyOtherComponent",
function () {
// Actuellement retourne un autre composant
$component = new AnotherComponent();
return $component;
}
);
// Création d'un instance via le conteneur de service.
$myComponent = $di->get("MyOtherComponent");
Vous pouvez profiter de ceci en instanciant toujours vos classes depuis le conteneur de services (même s’ils ne sont pas inscrits en tant que service). Le DI prendra par défaut un chargeur automatique valide pour charger la classe. En faisant comme ceci, vous pourrez aisément replacer n’importe quelle classe en implémentant une définition pour elle. Injection automatique pour le DI lui-même¶Si une classe ou un composant ai besoin que le DI localise lui-même les services, le DI peut automatiquement s’injecter les instances qu’il crée. Pour ceci, vous devez implémenter l’interface Phalcon\Di\InjectionAwareInterface dans vos classes: <?php
use Phalcon\DiInterface;
use Phalcon\Di\InjectionAwareInterface;
class MyClass implements InjectionAwareInterface
{
/**
* @var DiInterface
*/
protected $_di;
public function setDi(DiInterface $di)
{
$this->_di = $di;
}
public function getDi()
{
return $this->_di;
}
}
Une fois que le service est résolu, la variable <?php
// Inscription du service
$di->set("myClass", "MyClass");
// Résolution du service (NOTE: $myClass->setDi($di) est automatiquement appélée)
$myClass = $di->get("myClass");
Organisation des services en fichiers¶Vous pouvez mieux organiser votre application en déplaçant l’inscription des services dans des fichiers distincts au lieu de tout mettre dans l’amorce de l’application: <?php
$di->set(
"router",
function () {
return include "../app/config/routes.php";
}
);
Ainsi le fichier (”../app/config/routes.php”) renvoi l’objet résolu: <?php
$router = new MyRouter();
$router->post("/login");
return $router;
Accès au DI de manière statique¶Si nécessaire, vous pouvez accéder au dernier DI créé dans une fonction statique de la façon suivante: <?php
use Phalcon\Di;
class SomeComponent
{
public static function someMethod()
{
// Récupère le service de session
$session = Di::getDefault()->getSession();
}
}
Construction du DI par défaut¶Bien que le caractère découplé de Phalcon offre une grande liberté et flexibilité, peut-être que nous voulons simplement l’utiliser comme un framework full-stack. Pour réaliser ceci, le framework fournit une variante de Phalcon\Di appelée Phalcon\Di\FactoryDefault. Cette classe inscrit automatiquement les services appropriés qui sont encapsulés dans le framework afin qu’il agisse comme un full-stack. <?php
use Phalcon\Di\FactoryDefault;
$di = new FactoryDefault();
Convention de nommage des services¶Bien que vous puissiez inscrire les services avec le nom que vous voulez, Phalcon a plusieurs conventions de nommage qui permettent d’obtenir le bon service (built-in) au bon moment.
Création de votre propre DI¶Pour remplacer le DI fournit par Phalcon, vous devez soit implementer l’interface Phalcon\DiInterface, soit étendre un existant. |