Vous êtes sur la page 1sur 8

Pratique

Créer son propre conteneur IoC :


comment font-ils à Hollywood ?
Niveau de difficulté : lll
Piotr Szarwas

Imaginez que la société, pour laquelle vous avez


créé une application, se soit agrandie et qu'elle
vous demande de migrer la base de données
actuelle vers une base de données centrale,
dont le fonctionnement repose sur LDAP.
Malheureusement, si l'architecture de votre
application n'a pas été conçue correctement,
vous avez devant vous un travail long et
pénible.

H
eureusement, vous avez à votre ble un simple conteneur IoC dont le fonc-
disposition un conteneur IoC (en tionnement repose en grande partie sur
anglais Inversion of Control) que le principe d'Hollywood susmentionné. Le
nous avons présenté plusieurs fois dans code du conteneur et un exemple d'implé-
nos magazines PHP Solutions. Si vous mentation se trouveront sur le site Internet
connaissez certains articles précédents, http://flexi.sf.net/, comme c'était le cas des
vous savez ce que IoC sait faire à peu articles précédents.
près : le conteneur vous proposera un
graphe des objets préparés, par exemple, Qu'est-ce qu’un
il permettra de configurer facilement les conteneur IoC ?
décorateurs. Un conteneur IoC est une fabrique d'ob-
jets configurable, capable non seulement
Ne nous appelez pas, de créer les objets mais aussi de les

Auteur nous vous appellerons configurer de sorte qu'ils soient prêts


La phrase ci-dessus est bien connue des
Piotr Szarwas travaille chez SUPERME- programmeurs comme étant le principe Ce qu'il faut savoir...
DIA Interactive et écrit sa thèse dans le d'Hollywood (en anglais Hollywood Prin- Vous devriez connaître la programmation
département Physique de l'École Poly- ciple). D'après ce principe, il faut créer orientée objets en PHP.
technique de Varsovie. Depuis 2003, il
conçoit des applications Web en se ser- un logiciel cohérent avec de faibles liens
vant de PHP4/5. Actuellement, il travaille entre les objets, faciles à tester et à main- Cet article explique...
sur le framework pour PHP, basé sur les tenir. Vous apprendrez dans cet article
solutions Hibernate et Spring. Dans cet article, appartenant au cycle comment créer votre propre conteneur
Pour contacter l'auteur : IoC, suivant le paradigme Hollywood
piotr.szarwas@gmail.com Motifs de conception et bonnes pratiques
Principle.
de programmation, nous créerons ensem-

38 www.phpsolmag.org PHP Solutions N° 5/2006


Comment créer son propre conteneur IoC ? Pratique

à utiliser immédiatement après la création.


Des livres entiers peuvent être consacrés
au sujet des méthodes différentes de di- ������� ������� �������
vers des objets. Malgré les apparences, ce
processus est assez compliqué et si vous
l'effectuez incorrectement, l'application
écrite sera difficile à maintenir. Il existe de
nombreux modèles testés pour créer cor-
rectement les objets. Nous vous invitons
������� �������
à lire l'article Chaîne d'assemblage
orientée objets : passons aux motifs de
conceptions, paru dans PHP Solutions
numéro 2/2006 et consacré à ce sujet.
L'utilisation du conteneur IoC pour
assembler les objets en groupes fonc-
tionnels peut faciliter considérablement
�������
la réalisation et les tests des applications
créées. Il en est ainsi car vous assem-
blez les objets en groupes fonctionnels
seulement au moment du lancement de
Figure 1. Exemple de l'arbre d'objets que le conteneur IoC devrait créer
l'application. Il est donc possible d'échan-
ger les classes dont est composée l'ap- Un exemple réel Si vous ne connaissiez pas le conte-
plication donnée à tout moment de la vie Imaginez le problème suivant : il y a un neur IoC à l'époque, vous avez peut-être
de l'application. Cet échange peut être certain temps, vous avez créé une appli- utilisé le motif singleton ou les registres
bien évidemment difficile, en particulier si cation, contenant une classe responsable pour transférer cette classe entre les
vous n'écrivez pas les interfaces et n'utili- de l'authentification et de l'autorisation couches différentes de l'application. Ima-
sez pas le typage fort. Depuis un certain des utilisateurs. Cette classe opérait sur ginez maintenant que le client, pour qui
temps, ces deux fonctionnalités sont dis- une base de données. Vous l'avez utilisée vous avez écrit l'application, demande
ponibles en PHP et il est recommandé de dans de nombreux endroits car c'est une de modifier la base d'utilisateurs car sa
les utiliser. classe importante. société se développe. On vous demande
donc de migrer toutes les bases d'uti-
lisateurs dans une base centrale dont
Listing 1. Fragment de index.php qui configure les objets et lance le front controller le fonctionnement repose sur LDAP.
<?php
Imaginez maintenant que vous deviez
parcourir toute l'application parce que
$sessionFilter = new SessionFilter()); vous avez placé les appels des classes
$actionResolver = à la main ou via le motif singleton ou les
new FilePerActionResolvingStrategy($currentDir.'/controllers/');
registres. Un problème supplémentaire
$viewResolver = new PHPViewResolvingStrategy( $currentDir.'/views/');
$localeResolver = new NullLocaleResolvingStrategy();
réside dans le test d'une telle application.
$httpRequest = new HttpRequest(); Vous ne voudriez sûrement pas vous
$frontController = trouver dans une telle situation.
new FrontControllerImpl( Si vous aviez utilisé les interfaces
$actionResolver,$viewResolver,$localeResolver, $filterChain);
et le conteneur IoC, cela aurait été plus
echo $frontController->doService( $httpRequest );
facile. Il fallait alors créer une nouvelle
?> classe qui implémente une interface ap-
propriée. Il était nécessaire ensuite de
Listing 2. Fragment index.php qui configure les objets et qui lance le front changer la définition de la classe, char-
controller, en utilisant IoC cette fois-ci gée de gérer les utilisateurs dans le
<?php fichier de configuration du conteneur.
Nous expliquerons comment le faire
$mappingBuilder = new MappingBuilderFromArray( $iocMap ); dans un instant.
$iocContainer = Il faudrait parler d'une autre qua-
new DefaultIoCContainter( $mappingBuilder->getApplicationMap() );
lité de IoC. Grâce à IoC, les objets sont
$frontController = $iocContainer->create( "frontController" );
echo $frontController->doService( $iocContainer->create( "httpRequest" ) liés faiblement et sont donc plus faciles
); à tester à l'aide des Unit Tests (tests
?> unitaires). N'oubliez pas que les relations
entre les objets n'apparaissent qu'au

PHP Solutions N° 5/2006 www.phpsolmag.org 39


Pratique Comment créer son propre conteneur IoC ?

moment du lancement de l'application.


Grâce aux liens faibles, les classes sont
��������������� ���������������� plus souples et génériques, autrement
dit, il est possible de les utiliser dans un
plus grand nombre de cas.
Revenons à nos moutons : il serait
possible de créer une classe d'authenti-
fication et d'autorisation via LDAP, de la
tester en dehors de l'application, ce qui
������������������� �������� est plus facile, et ensuite, de la placer
dans l'application à l'aide du conteneur
IoC. Cette approche vous fera écono-
miser davantage de temps ! De plus,
cette classe pourrait être réutilisée dans
d'autres projets.
Quelles sont les exigences faites au
��������������
conteneur IoC ? Premièrement, le conte-
neur devrait appeler les arbres d'objets
complexes. Imaginez un objet qui néces-
site deux autres objets pour fonctionner.
Ces deux objets nécessitent à leur tour
Figure 2. Diagramme UML des classes qui remplacent le fichier de configuration du
des objets externes pour fonctionner cor-
conteneur IoC par les objets compréhensible pour le conteneur
rectement. Vous pouvez créer ainsi n'im-
porte quel arbre d'objets complexe (Figure
Listing 3. Fragment de la configuration de votre conteneur IoC, grâce à laquelle 1). Dans une telle situation, le conteneur
vous êtes capables de restituer l'arbre d'objets, créé manuellement dans le Listing 1 IoC devrait créer un objet, constituant la
<?php racine de la structure et ensuite, créer
$frameworkPath = "chemin du dossier avec le framework" les objets restants et les assembler en un
$iocMap = array( arbre. Mais, le conteneur, comment peut-il
savoir quels objets créer et comment les
"sessionFilter" => array(
assembler ? Les objets sont assemblés
"className" => "SessionFilter",
"file" => $frameworkPath. de la même manière que vous le faites
"/web/mvc/controllers/filters/SessionFilter.class.php", à la main : vous précisez l'objet au cons-
"singleton" => true, tructeur, au setter ou vous l'attribuez à une
"properties" => array(), "constructorParams" => array() variable publique. Imaginez que l'objet
),
A nécessite l'objet B externe pour fonction-
"actionResolver" => array( ner. Il est possible de transférer l'objet B
"className" => "FilePerActionResolvingStrategy", à l'objet A de trois manières :
"file" => $frameworkPath.
"/web/mvc/actions/resolvers/FilePerActionResolvingStrategy.class.php", • $a = new A(new B()) – via le cons-
"singleton" => true, "properties" => array(),
tructeur,
"constructorParams" => array( $currentDir.'/controllers/' )
), • $a->setB(new B()) – via le setter,
• $a->b = new B() – via la variable pu-
"viewResolver" => array( blique.
"className" => "PHPViewResolvingStrategy",
"file" => $frameworkPath.
Oubliez donc les méthodes archaïques,
"/web/mvc/views/resolvers/PHPViewResolvingStrategy.class.php",
"singleton" => true, "properties" => array(), telles que le singleton, le registre ou
"constructorParams" => array( $currentDir.'/views/' ) l'appel manuel de l'objet B à l'intérieur
), de l'objet A. Ces méthodes créent des
liens forts et réduisent la souplesse du
"locateResolver" => array(
code.
"className" => "NullLocaleResolvingStrategy",
"file" => $frameworkPath. Vous savez donc que le conteneur
"/locale/resolvers/NullLocaleResolvingStrategy.class.php", doit assembler les objets en arbres.
"singleton" => true, "properties" => array(), Il existe plusieurs possibilités pour dé-
"constructorParams" => array() crire cet arbre. La première d'entre elles
),
est un fichier de configuration externe.
);
?> Il décrit tous les objets et les arbres
d'objets. Le fichier définit les objets, les
constantes numériques ou caractère

40 www.phpsolmag.org PHP Solutions N° 5/2006


Comment créer son propre conteneur IoC ? Pratique

à placer dans les paramètres appro- Imaginez que la classe A a besoin choisir ? Ces problèmes n'apparaissent
priés du constructeur ou du setter. Le de n'importe quelle classe qui implé- pas dans de simples projets où le fi-
format du fichier n'est pas important ; il mente l'interface BI pour fonctionner. Et chier de configuration contient plusieurs
devrait seulement être lisible car il faut imaginez que vous disposiez de deux objets mais dans les projets contenant
l'écrire manuellement. Pour cette rai- classes qui implémentent cette interface plusieurs dizaines d'objets ; vous les ren-
son, l'analyse du fichier de configuration dans le fichier de configuration ; laquelle contrerez sûrement. Le conteneur que
dans votre application sera transférée
à des classes séparées pour modifier fa- Listing 4. Implémentation des classes qui mappent le fichier de configuration dans
cilement le format et la source des don- le modèle orienté objet : classe DefaultParamMap
nées de configuration. Il est possible de
<?php
créer un fichier de configuration en vous
basant sur XML et Xschema et d'utiliser abstract class DefaultParamMap {
un outil graphique pour créer des fichiers
de configuration. Cet outil vérifierait en private $name;
plus si le fichier XML est correct. Vous private $value;
private $type;
réaliserez votre fichier de configuration
en vous basant sur les tableaux asso- public function __construct($name, $value, $type) {
ciatifs. $this->setName($name);
La deuxième méthode de définition de $this->setValue($value);
l'arbre est une extension de la première $this->setType($type);
}
méthode. Elle demande toutefois d'utiliser
un typage fort dans le code. Imaginez public function getName() {
que l'objet A a besoin de n'importe quelle return $this->name;
classe pour fonctionner correctement ; }
cette classe implémente l'interface BI ou
protected function setName( $name ) {
tout simplement les classes B. Depuis un
$this->name = $name;
certain temps, il est possible de forcer en }
PHP les types d'objets. Pour cette raison, il
est possible de coder le constructeur ou le public function getValue() {
setter de la classe 1 de manière suivante : return $this->value;
}

class A { protected function setValue( $value ) {


public function $this->value = $value;
__construct(BI $b){} }
public function
public function getType() {
setB(BI $b){}
return $this->type;
} }

ou, si vous ne voulez pas utiliser l'interface protected function setType( $type ) {
mais uniquement la classe B : $this->type = $type;
}

class A { }
public function ?>
__construct(B $b){}
public function Listing 5. Implémentation des classes qui mappent le fichier de configuration dans
setB(B $b){}
le modèle orienté objet : classe ConstructorParamMap
} <?php

La deuxième méthode est liée à la liaison require_once 'ioc/DefaultParamMap.class.php';


automatique des objets. Quand elle crée
class ConstructorParamMap extends DefaultParamMap {
l'objet A, elle vérifie son constructeur et
ses setters. Si elle y trouve la déclaration // le nom du paramètre n'est pas important pour le paramètre du constructeur
des objets, définis dans son fichier de con- // nous modifions donc le constructeur de cette classe
figuration, elle les crée automatiquement public function __construct($value, $type) {
et les place dans l'objet A. Cette méthode parent::__construct(null,$value,$type);
}
d'assemblage des objets est très pratique
parce qu'il n'est pas nécessaire de créer }
des fichiers de configuration compliqués ; ?>
elle peut être tout de même dangereuse.

PHP Solutions N° 5/2006 www.phpsolmag.org 41


Pratique Comment créer son propre conteneur IoC ?

nous écrirons ensemble ne contiendra


qu'une option d'assemblage des objets Listing 7. Implémentation des classes qui mappent le fichier de configuration dans
à partir des définitions du fichier de con- le modèle orienté objet : classe ClassMap
figuration. <?php
Il existe une autre méthode d'asso- require_once 'ioc/PropertyParamMap.class.php';
ciation d'objets, similaire à la liaison auto- require_once 'ioc/ConstructorParamMap.class.php';
matique. Elle permet de vérifier le nom du class ClassMap {

setter et de placer la classe appropriée


private $className;
dans l'objet à l'aide de ce setter. Nous private $classFile;
l'analyserons sur l'exemple des objets private $isSingleton;
A et B. Supposez que la classe A soit défi- private $propertiesParams = array();
nie de la manière suivante : private $constructorParams = array();
public function __construct( $className, $classFile, $isSingleton ) {
$this->setName($className);
class A { $this->setClassFile($classFile);
public setB($b){} $this->isSingleton = $isSingleton;
} }
public function getName() {
return $this->className;
Avec une telle structure de la classe et en
}
utilisant la troisième méthode d'assembla- protected function setName($className) {
ge des objets, le conteneur IoC vérifiera si $this->className = $className;
la classe dispose d'un setter. Pour ce faire, }
il regardera si elle contient des méthodes
public function getClassFile() {
qui commencent par le mot set. Il vérifiera
return $this->classFile;
ensuite si son fichier de configuration con- }
tient des classes dont le nom est identique
au reste du nom du setter ; dans cet arti- protected function setClassFile( $classFile ) {
cle, il s'agit de la classe B. Cette méthode if ( !file_exists( $classFile ) ) {
throw new Exception("Le fichier {$classFile} n'existe pas");
est également dangereuse. Il faut faire at-
}
tention à la convention de noms de setters $this->classFile = $classFile;
parce qu'en cas d'erreur, vous pourrez }
obtenir un objet indésirable.
Lorsque les concepts de réalisation public function getConstructorParams() {
return $this->constructorParams;
du conteneur sont définis, définissez la
}
structure du fichier de configuration.
public function setConstructorParam(
ConstructorParamMap $constructorParamMap ) {
Listing 6. Implémentation des $this->constructorParams[] = $constructorParamMap;
classes qui mappent le fichier de }
configuration dans le modèle orienté
objet : classe ProperyParamMap public function setProperty( PropertyParamMap $propertyMap ) {
$this->propertiesParams[$propertyMap->getName()] = $propertyMap;
<?php }

require_once public function getProperty( $propertyName ) {


'ioc/DefaultParamMap.class.php'; if ( $this->hasProperty( $propertyName ) ) {
return $this->propertiesParams[$propertyName];
class PropertyParamMap } else {throw new Exception("Property {$propertyName} n'existe pas");}
extends DefaultParamMap { }

public function public function hasProperty( $propertyName ) {


getSetterMethodName() { return isset( $this->propertiesParams[$propertyName] );
return "set".ucfirst( }
$this->getName());
} public function getProperties() {
return $this->propertiesParams;
public function }
getGetterMethodName() {
return "get".ucfirst( public function isSingleton() {
$this->getName()); return (bool)$this->isSingleton;
} }
}
} ?>
?>

42 www.phpsolmag.org PHP Solutions N° 5/2006


Comment créer son propre conteneur IoC ? Pratique

Configuration sociatif, chaque clé de ce tableau identifie mettre à la place de cet attribut,
du conteneur un objet. Les objets seront chargés depuis constitue une valeur. En ce qui
Afin de configurer le conteneur IoC, uti- le conteneur d'après cet identifiant. Remar- concerne les constantes caractère
lisez un simple modèle orienté objets et quez qu'il est possible de définir plusieurs ou numériques, vous y placez leur
les tableaux associatifs. La configuration configurations pour une seule classe dans valeur et en ce qui concerne les ré-
gérée par le programmeur se trouvera un fichier ; il suffit qu'elles soient identifiées férences aux objets, vous saisissez
dans un tableau spécial, traduit ensuite par d'autres clés du tableau. En dessous le caractère « & » et l'identifiant de
en un ensemble d'objets par une classe de l'identifiant de l'objet se trouve un autre l'objet, défini dans le conteneur IoC,
d'analyse spéciale. Votre conteneur opé- tableau associatif ; il dispose d'un ensem- • constructorParams – tableau con-
rera sur ces objets. Grâce à cette démar- ble défini des clés : tenant les paramètres, prévus au
che, il sera facile de modifier les formats constructeur de l'objet ; cette fois-ci,
et les sources de données de configu- • className – nom de la classe, il ne s'agit pas de tableau associatif.
ration. Vous pourrez aussi configurer le • file – emplacement du fichier conte- Seul l'ordre des valeurs placées est
conteneur sans les fichiers de configura- nant la définition de la classe, important. Elles seront placées dans
tion dans les situations difficiles. • singleton – indicateur qui dit si l'objet le même ordre dans le constructeur
Le Listing 1 présente un extrait du doit être créé une seule fois et placé lors de la création de l'objet. Les
script index.php. Vous y trouverez deux ensuite dans la mémoire cache ou si règles de la saisie des paramètres
sections : l'une est très longue pour une instance de l'objet doit être créé à sont les mêmes que ci-dessus.
configurer les objets et la seconde est chaque fois,
plus courte pour lancer le front control- • properties – tableau associatif où La Figure 2 présente un diagramme
ler. Maintenant, regardez le Listing 2. le nom de l'attribut de l'objet cons- UML et les Listings 4 – 8 présentent
Il présente le même script index.php ; titue la clé. La variable, qu'il faut les implémentations des classes qui
cette fois-ci, le conteneur IoC est chargé
de configurer les objets et de les créer. Listing 8. Implémentation des classes qui mappent le fichier de configuration dans
Au premier abord, vous remarquerez le modèle orienté objet : classe ApplicationMap
que le script est plus simple. Plus l'arbre
<?php
d'objets à créer est grand, plus le code
est lisible. require_once 'ioc/ClassMap.class.php';
Le Listing 3 présente la configu- class ApplicationMap {
ration de votre conteneur IoC. Cette
configuration vous permet de restituer private $classes = array();

l'arbre d'objets, créé manuellement sur


public function __construct() {
le Listing 1. À ce moment-là, vous serez }
probablement nombreux à vous dire :
attendez, cette configuration prend plus public function setClass($objKey, ClassMap $class) {
de place que celle du Listing 1 ; que $this->classes[$objKey] = $class;
}
gagnons-nous alors puisque la quan-
tité du code avec le conteneur est plus public function getClass($objKey) {
grande que sans ce conteneur ? Heu- if ( $this->hasClass( $objKey ) ) {
reusement, ce n'est pas vrai après un return $this->classes[$objKey];
regard sur l'ensemble. Premièrement, } else {
throw new Exception("La classe {$objKey} n'existe pas");
vous écrivez la configuration une seule
}
fois et vous l'utilisez plusieurs fois dans }
une ou plusieurs applications, basées
sur les mêmes classes. Il est donc public function hasClass($objKey) {
possible que vous utiliserez les mêmes return isset($this->classes[$objKey]);
}
versions de modules de l'application
précédente et vous ne changerez que }
le fichier de configuration. De plus, si ?>
vous transférez toutes les informations
sur la structure d'objets dans le fichier Listing 9. Chaque implémentation du conteneur doit implémenter l'interface
IoCContainer
de configuration, vous créerez ainsi une
documentation : les informations sur la <?php
structure de l'application se trouveront
dans un seul endroit. interface IoCContainer {
public function create($className);
Retournons au fichier de configuration.
}
Il semble compliqué au premier abord.
Premièrement, toutes les définitions des ?>
objets se trouvent dans un seul tableau as-

PHP Solutions N° 5/2006 www.phpsolmag.org 43


Pratique Comment créer son propre conteneur IoC ?

mappent le fichier de configuration


dans le modèle orienté objets. Cha- Listing 10 a. Forme élémentaire de l'implémentation complète du conteneur
cune des classes présentées dans le
<?php
diagramme mappe une à une l'une
des données de configuration. La require_once 'ioc/IoCContainer.interface.php';
classe ProperyParamMap est char-
gée de mapper les données placées class DefaultIoCContainter implements IoCContainer {

dans les objets à l'aide des setters et


private $applicationMap;
ConstructorParamMap joue le même private $objectCache = array();
rôle, en ce qui concerne le construc-
teur de l'objet. Les classes ClassMap public function __construct( ApplicationMap $applicationMap ) {
et ApplicationMap sont en revanche $this->setApplicationMap( $applicationMap );
}
des agrégateurs des données de con-
figuration à proprement parler. Nous protected function setApplicationMap(ApplicationMap $value) {
ne présenterons pas le code qui trans- $this->applicationMap = $value;
forme le tableau en objets. Vous pouvez }
télécharger ce code ainsi que d'autres
protected function getApplicationMap() {
éléments du conteneur depuis le site
return $this->applicationMap;
http://flexi.sf.net. }

Conteneur IoC public function create($className) {


Passez à présent à l'implémentation
$classMap = $this->getApplicationMap()->getClass($className);
du conteneur IoC. Premièrement,
if ( $classMap->isSingleton() ) {
chaque implémentation du conteneur if ( $this->inCache( $className ) ) {
doit implémenter l'interface IoCCon- return $this->getFromCache( $className );
tainer. Cette interface ne dispose que } else {
d'une seule méthode publique create $classObj = $this->createObject($classMap);
$this->putInCache($className,$classObj);
(Listing 9). Le Listing 10 présente la
return $classObj;
forme élémentaire de l'implémenta- }
tion complète du conteneur. La classe } else {
DefaultIoCContainer implémente return $this->createObject($classMap);
l'interface IoCContainer. Afin de fonc- }
}
tionner correctement, elle a besoin de
la classe ApplicationMap, contenant la protected function createObject(ClassMap $classMap) {
configuration du conteneur. Cette clas- $className = $classMap->getName();
se ne dispose que d'une seule méthode
publique create. D'après l'identifiant de if ( !class_exists( $className ) ) {
require_once($classMap->getClassFile());
l'objet donné, la méthode retourne l'ins-
}
tance de cet objet ainsi que l'arbre des $paramsArr = $classMap->getConstructorParams();
objets enfants, s'ils ont été définis dans
le fichier de configuration. if( !empty( $paramsArr ) ) {
La méthode create charge la confi-
$params = array();
guration pour l'identifiant donné. Ensuite,
$evalArr = array();
elle vérifie si l'objet est du type singleton $i = 0;
(autrement dit, elle vérifie si une seule foreach ($paramsArr as $constructorParam) {
instance de l'objet donné peut être créée $params[] = $this->getConstructorParamsRecursively($constructorParam);
pendant la vie de l'application). Si c'est $evalArr[] = '$params['.$i++.']';
}
le cas, vous vérifiez s'il n'a pas été créé
$evalStr = '$classObj = new '.$className.'('.implode(",",$evalArr).');';
auparavant. S'il existe, vous le retour-
nez, sinon, vous le créez via la méthode eval($evalStr);
createObject. Premièrement, cette mé- } else {
thode charge le fichier contenant la $classObj = new $className();
}
définition de la classe à créer : le chemin
du fichier est défini dans la configura- $this->setProperties($classMap, $classObj);
tion. Deuxièmement, elle vérifie si l'objet return $classObj;
a besoin des paramètres pour le cons- }
tructeur lors de sa création. Si c'est le
cas, l'objet est créé à l'aide de la fonction
eval, sinon, à l'aide de l'opérateur new.

44 www.phpsolmag.org PHP Solutions N° 5/2006


Comment créer son propre conteneur IoC ? Pratique

Si vous utilisez la version PHP 5.1.3 ou de configuration dans la section tributs d'objets. Elle est conforme à la
supérieure, vous pouvez créer les objets properties. Le conteneur crée lui- convention utilisée dans d'autres appli-
avec les paramètres pour le constructeur même l'information pour savoir quel cations. Remarquez aussi deux métho-
à l'aide de Reflection Api et la méthode setter utiliser ; cette information n'est des : getConstructorParamsRecursively
newInstanceArgs. pas enregistrée dans la configuration. et getPropertyValueRecursively. Elles
Une fois l'objet créé, son ins- Le nom correct du setter se compose se ressemblent. D'après le type de pa-
tance est transmise à la méthode d'un préfixe set et du nom de property ; ramètre qui peut prendre deux valeurs
setProperties où on ajoute les varia- la première lettre est majuscule. Vous value ou référence, le conteneur char-
bles à l'objet à l'aide des setters ; ces devriez connaître cette convention pour ge une constante depuis la configura-
variables sont définies dans le fichier nommer les méthodes d'accès aux at- tion ou lance de nouveau sa méthode
create, afin de créer un objet. Grâce
à ce morceau de code, le conteneur
Listing 10 b. Forme élémentaire de l'implémentation complète du conteneur
– suite peut retourner des arbres entiers d'ob-
jets. Pour savoir si le paramètre est du
protected function getConstructorParamsRecursively(ConstructorParamMap type value ou référence, regardez si le
$constructorParamMap) { caractère & se trouve à côté de la défi-
switch ( $constructorParamMap->getType() ) {
nition de la valeur du paramètre dans
case 'value' : $param = $constructorParamMap->getValue(); break;
case 'reference' : $param = le fichier de configuration.
$this->create( $constructorParamMap->getValue() ); break;
default : throw new Exception( Conclusion
"Le type du paramètre est incorrect {$constructorParamMap- Le conteneur IoC, que nous avons
>getType()}" );
réalisé ensemble, est utilisé depuis un
}
certain temps dans le travail quotidien
return $param; dans ma société. L'expérience a mon-
} tré que son implémentation a facilité
la réalisation des applications. Nous
protected function setProperties(ClassMap $classMap, $object) {
avons gagné le plus dans les tests
$properties = $classMap->getProperties();
et la clarté du code. De plus, après
foreach($properties as $propertyParamMap) { l'adaptation à la spécificité du conte-
$propertySetterName = $propertyParamMap->getSetterMethodName(); neur IoC (nous fournissons les objets
$object->{$propertySetterName} via le constructeur et les setters), une
( $this->getPropertyValueRecursively($propertyParamMap) );
grande partie des classes est devenue
}
} tellement souple que nous les utilisons
dans d'autres projets sans les mo-
protected function getPropertyValueRecursively( PropertyParamMap difier. Nous avons également réussi
$propertyParamMap ) { à supprimer dans le code les aspects
switch ( $propertyParamMap->getType() ) {
liés à l'autorisation, à la connexion et
case 'value' : $param = $propertyParamMap->getValue(); break;
case 'reference' : $param = $ à la validation. Pour savoir comment
this->create( $propertyParamMap->getValue() ); break; nous l'avons réussi, rendez-vous dans
default : throw new Exception( le prochain article. n
"Le type de l'attribut est incorrect {$propertyParamMap->getType()}"
);
}

return $param; Sur Internet


}
• http://en.wikipedia.org/wiki/
protected function putInCache($key,$value) { Hollywood_Principle – Hollywood
$this->objectCache[$key] = $value; Principle,
} • http://www.theserverside.com/tt/
articles/article.tss?l=IOCBeginners
– introduction à Dependency Injec-
protected function getFromCache($key) {
tion,
return $this->objectCache[$key];
• http://www.martinfowler.com/
}
articles/injection.html – Inversion
of Control Containers/Dependency
protected function inCache($key) {
Injection pattern,
return isset($this->objectCache[$key]); • http://picocontainer.codehaus.org/
} – implémentation IoC pour Java,
• http://flexi.sf.net/ – framework créé
} dans cet article, certaines sources
?> analysées dans l'article.

PHP Solutions N° 5/2006 www.phpsolmag.org 45

Vous aimerez peut-être aussi