IoC de Laravel pour les nuls

Le conteneur d’inversion de contrôle de Laravel !

dependance Laravel

L’injection de dépendance nous permet de supprimer les dépendances entre les classes; et IoC est la réponse de Laravel à ce problème.

Les dépendances

Nous pouvons avoir une classe Personne avec une classe Adresse, comme une classe Entreprise avec aussi une classe Adresse.

class Personne
{
  function __construct($nom=nul)
  {
      $this->adresse= new Adresse();
      $this->nom= $nom;
  }
}

Ici, nous nous retrouvons avec une dépendance forte, l’appel à notre classe « Adresse » est codé en dur dans le code de la classe « personne » mais aussi dans les classes Entreprise, Magasin… Si je désire demain utiliser AdressePlus, il va falloir modifier 3 classes au minimum :( et pire encore, ma classe Personne est utilisée aussi dans d’autres projets, et 50 versions de ma classe Personne n’est tous simplement pas gérable.

Avec les Frameworks (php) et les bibliothèques (via composer), ce problème de dépendance est encore plus important : une classe d’une bibliothèque x ne doit pas être dépendante d’une classe d’une bibliothèque Y, surtout que je ne contribue pas à l’écriture de ces bibliothèques. Mettre un Zend.utilitaire dans une classe Laravel.Controller est le début de la fin.

L’injection de dépendance est la réponse, et Laravel a créer sa solution : IoC. Le plus simple (pour un nul) est d’écrire sa propre solution : L’injection de dépendance made in bibi.

IoC

La réponse en fait est très simple, il suffit de remplacer tous les appels « new …() » par un appel à notre méthode magique Ioc::make(). Notre classe Ioc va contenir la liste des  classes « dépendantes » pour pouvoir les gérer très facilement sans modification du code source des classes. Nous allons avoir une liste clé-valeur du type :
« Adresse »=> »AdressePlus » , « utilitaire » => »Zend\Util »

class ioc
{
   protected static $alias;

   public static function boot()
   {
      self::$alias= array(
                    "Adresse"=>    "AdressePlus",
                    "Utilitaire"=> "Zend\Util"
      );
   }

   public static function make($nomClasse, $params=null)
   {
      $classe=  array_key_exists($nomClasse,self::$alias) ? static::$alias[$nomClasse] : $nomClasse;
      $objet = new $classe($params);
      return $objet;
   }

   public static function bind($nomClasse, $nomReel)
   {
    self::$alias[$nomClasse]=$nomReel;
   }
}

Avec la méthode boot() nous chargeons au début de l’application la liste des classes; Laravel utilise lui ServiceProvider dans app/config/app.php;
La méthode bind() nous permet de gérer nos dépendances à l’exécution pour un complément avec boot().

La méthode make() elle, cherche dans la liste ($alias) une correspondance de classe et crée l’objet.

class AdressePlus {  }

Ioc::boot(); // generation de la liste
$adress = Ioc::make('Adresse');
var_dump($adress);

Ioc::bind('Test','AdressePlus');
$adress = Ioc::make('Test');
var_dump($adress);

Maintenant , la dépendance dans notre classe Personne est gérée par Ioc

class Personne
{
  function __constructor($nom=null)
  {
      $this->adresse= Ioc::make('Adresse');
      $this->nom= $nom;
  }
}

Nous pouvons donc changer notre dépendance sans toucher a la classe Personne. Mais nous pouvons aussi faire plus fort :

class Adresse {  }
class AdressePlus extends Adresse { public $ville='rennes'; }

Ioc::boot();

Ioc::bind('Adresse','AdressePlus');
$adress = Ioc::make('Personne');
var_dump($adress);

Nous utilisons la classe AdressePlus dans Personne sans changer le code source de Personne. Dans Laravel, ceci nous permet d’étendre les classes principales du Framework sans rien toucher au code source.

Résolution automatique d’une classe

Nous avons  un très bon moyen de supprimer les dépendances entre les classes, mais Laravel nous en donne encore plus. Il peut injecter automatiquement un objet. Pour cela, il  utilise la Reflexion PHP pour voir le type du paramètre du constructeur et le créer automatiquement.

Cette fonctionnalité devrait ressembler a ceci :

   static function make($nomClasse, $params=null)
   {
      $classe=  array_key_exists($nomClasse,self::$alias) ? static::$alias[$nomClasse] : $nomClasse;

      if ((self::parametre_is_classe($classe))&&($params==null)){
        $param=self::get_parametre_classe($classe);
        $params=self::make($param);
      }

      $objet = new $classe($params);
      return $objet;
   }

La mise en oeuvre avec notre classe personne :

class Personne
{
  function __construct(Adresse $adresse)
  {
      $this->adresse= $adresse;
      $this->nom= $nom;
  }
}
$personne= Ioc::make('Personne');

Laravel utilise cette fonctionnalité, en particulier, avec le constructeur des Controller. Il est à noté que la classe peut être une Interface, par exemple passer une InterfaceRepository au Contrôleur.

http://laravel.fr/docs/4/ioc#practical-usage

Bien sur, Laravel dispose d’un système IoC 100 fois plus performant que le notre :) mais je les soupçonne d’avoir copié notre classe comme point de départ.

Un package permet de « voir » le conteneur IoC de Laravel daylerees/Container-debug.

Share Button

Vous devriez aimer...