Réutilisabilité du code
IT&L@bs
CO PMM
Version 1.01, le 29 novembre 2012             Nicolas Le Nardou
                                              Architecte / Expert Technique PHP

                                              nicolas.lenardou@orange.com
           Réutilisabilité du code   Page 1
Introduction : contexte projet

> Centre de services d’un grand groupe de presse

> 20-30 sites en PHP
   - eZ Publish, Symfony 2, WordPress, from scratch, …


> Forte audience : environ 10M de pages vues / jour

> Périmètres fonctionnels très proches

> Pression sur les coûts de développement




     Réutilisabilité du code      Page 2
Introduction : contexte projet

> A son écriture, le partage du code entre plusieurs sites est :
    - Soit déjà acté
    - Soit déjà en cours de discussion


> Le partage avec les autres sites du CDS est toujours de l’ordre du
  possible




     Réutilisabilité du code         Page 3
Introduction : problématique

> Comment se construire un référentiel de code commun à tous nos
  sites ?
   - Quel que soit le socle technique
   - Sans renoncer aux apports de ces différents socles (pas de politique du
     plus petit dénominateur commun)


> Objectifs :
   - Mutualiser la maintenance du code
   - Réduire les temps de développement




     Réutilisabilité du code        Page 4
sommaire


> 1 – Rappel des principes de conception SOLID

> 2 – Etude de cas concrets

> 3 – Point sur les tests unitaires




     Réutilisabilité du code      Page 5
Conception SOLID
Rappel




     Réutilisabilité du code
conception SOLID

> Single Responsibility

> Open / Closed

> Liskov substitution

> Interface segregation

> Dependency injection




     Réutilisabilité du code   Page 7
SOLID vs STUPID

> Singleton

> Tight coupling

> Untestability

> Premature Optimization

> Indescriptive Naming

> Duplication



     Réutilisabilité du code   Page 8
SOLID : single responsibility

> Principe de responsabilité unique :
> « Une classe ne fait qu’une et une seule chose »

> Envie de rajouter une fonctionnalité ? Il est temps de créer une
  nouvelle classe.

> Une classe au fonctionnement clairement défini et borné sera plus
  facilement réutilisable

> Une classe aux multiples responsabilités sera fatalement dupliquée
  pour être adaptée au nouveau besoin




     Réutilisabilité du code     Page 9
SOLID : open / closed

> « Une classe doit être fermée à la modification et ouverte à
  l’extension »

> Une évolution ne devrait pas vous faire casser du code, juste en
  ajouter !

> Une classe doit prévoir de pouvoir être étendue sans être réécrite.




     Réutilisabilité du code     Page 10
SOLID : Liskov substitution

> « On doit pouvoir substituer à un objet d’une classe X, tout objet
  d’une sous classe de X »

> Corolaire : Une classe utilisant un objet de classe X ne doit pas
  avoir connaissance des sous classes de X (sous peine de violer le
  principe open/closed)




     Réutilisabilité du code     Page 11
SOLID : Interface segregation

> « Un objet ne devra pas dépendre d’un autre objet mais de son
  interface »

> Il faut expliciter la dépendance réelle au travers d’une interface




     Réutilisabilité du code      Page 12
SOLID : Dependency Injection

> « Un objet ne doit pas instancier un autre objet, il doit le recevoir de
  l’extérieur »

> Inversion de contrôle

> Pas d’utilisation du mot clé new dans une classe


> Injection par constructeur ou mutateur




      Réutilisabilité du code     Page 13
Cas concret #1




    Réutilisabilité du code
cas concret #1

> Besoin : Injecter dans nos pages des tags javascript (tracking, pub,
  …)

> Implémentation : Une classe TagServer qui calcule la valeur d’un
  tag en fonction d’un contexte en entrée (url, contenu, …) et d’un jeu
  de règles
    - Moteur de règles
    - Jeu de règles en configuration


> Contrainte : A déployer sur :
    1.      Un site eZ Publish
    2.      Un site Symfony 2.x




         Réutilisabilité du code       Page 15
cas concret #1

> Les mauvaises solutions :

   - Faire 2 développements distincts

   - Dupliquer la classe et la modifier

   - Nombreux paramètres dans le constructeur

   - Ou toute autre abomination …




     Réutilisabilité du code         Page 16
cas concret #1 : implémentation eZ Publish

class TagServer
{
    private
        $rules;

    public function __construct()
    {
        $this->rules = array();

            $ini = eZINI::instance('tagserver.ini');
            $ini->assign('Tags', 'Rules', $this->rules);
    }

    // ...
}

        Réutilisabilité du code   Page 17
cas concret #1 : problèmes

public function __construct()
{
        $this->rules = array();

           $ini = eZINI::instance('tagserver.ini');
           $ini->assign('Tags', 'Rules', $this->rules);
}




> Couplage fort : TagServer dépend de eZINI

      La classe n’est réutilisable que sur un autre site eZ Publish



     Réutilisabilité du code          Page 18
cas concret #1 : problèmes

> Solution : injecter l’objet eZINI dans le constructeur

                                            Injection de dépendances (SOLID)




> On pourra ainsi substituer à une occurrence d’eZINI, un objet d’une
  sous classe d’eZINI




     Réutilisabilité du code      Page 19
cas concret #1 : eZINI injecté

class TagServer
{
    private
        $rules;

    public function __construct(eZINI $ini)
    {
        $this->rules = array();

            $ini->assign('Tags', 'Rules', $this->rules);
    }

    // ...
}



        Réutilisabilité du code   Page 20
cas concret #1 : eZINI injecté

> La construction du serveur :


$ini = eZINI::instance('tagserver.ini');
$server = new TagServer($ini);




     Réutilisabilité du code     Page 21
cas concret #1 : eZINI injecté

> Couplage désormais faible

> Mais problème de sémantique : conceptuellement nous n’avons pas
  besoin d’un eZINI, nous avons plutôt besoin de la configuration.

  Il nous faut une interface « Configuration »

                                           Séparation d’interfaces (SOLID)




     Réutilisabilité du code     Page 22
cas concret #1 : interface Configuration

interface Configuration
{
    const SEPARATOR = '/';

    /**
     * Read configuration if exists. Returns default value
     * otherwise.
     *
     * @param string $variableName fully qualified variable name
     * @param mixed $defaultValue
     */
    public function read($variableName, $defaultValue);
}




     Réutilisabilité du code   Page 23
cas concret #1 : interface Configuration

class TagServer
{
    private
        $rules;

    public function __construct(Configuration $config)
    {
        $this->rules = $configuration->read(
            'tagserver/Tags/Rules',
            array()
        );
    }
}




     Réutilisabilité du code   Page 24
cas concret #1 : interface Configuration

> La dépendance avec le framework d’eZ Publish est rompue …
> … mais notre code ne fonctionne plus pour eZ Publish

> Il nous faut une implémentation de Configuration reposant sur eZINI
                                          Substitution de Liskov (SOLID)




     Réutilisabilité du code    Page 25
cas concret #1 : eZConfiguration

class eZConfiguration implements Configuration
{
    public function read($variableName, $defaultValue)
    {
        list($file, $group, $variable) =
                        explode(self::SEPARATOR, $variableName);

            $ini = eZINI::instance($file . '.ini');
            $ini->assign($group, $variable, $defaultValue);

            return $defaultValue;
    }
}




        Réutilisabilité du code     Page 26
cas concret #1 : eZConfiguration

> Appel

$configuration = new eZConfiguration();
$server = new TagServer($configuration);



> Fonctionne à nouveau pour eZ Publish
   - Sans modification de la classe TagServer

                                                Open / Closed (SOLID)




     Réutilisabilité du code       Page 27
cas concret #1 : site Symfony

> Etape suivante : réutiliser notre classe TagServer sur un site
  reposant sur Symfony




     Réutilisabilité du code     Page 28
cas concret #1 : site Symfony

> Bien sûr, la classe eZConfiguration ne fonctionnera pas

> Il nous faut une classe YamlConfiguration




     Réutilisabilité du code    Page 29
cas concret #1 : site Symfony

class YamlConfiguration implements Configuration
{

    public function read($variableName, $defaultValue)
    {
        list($file, $group, $variable) =
                            explode(self::SEPARATOR, $variableName);

           $loader = Yaml::parse($file);
           if(array_key_exists($loader[$group][$variable]))
           {
               return $loader[$group][$variable];
           }

           return $defaultValue;
    }

}

        Réutilisabilité du code    Page 30
cas concret #1 : site Symfony

> Construction du serveur :

$configuration = new YamlConfiguration();
$server = new TagServer($configuration);



> Et …. c’est tout !




     Réutilisabilité du code   Page 31
cas concret #1 : bilan

> Coût du déploiement de notre classe TagServer sur un autre
  framework PHP

                                       ≈
  Coût de développement d’une classe d’adaptation pour accéder à
  la configuration



> Aucune modification de notre classe TagServer n’a été nécessaire
> Les classes de la couche d’adaptation sont elles-mêmes
  réutilisables
      constitution d’une boîte à outils très rapidement



     Réutilisabilité du code          Page 32
Cas concret #2




    Réutilisabilité du code
cas concret #2

> Nous voulons ajouter des logs à notre classe TagServer

> Contraintes :
   - Possibilité de les activer / désactiver
   - Possibilité de se reposer sur le système de log du socle technique utilisé




     Réutilisabilité du code         Page 34
cas concret #2

class TagServer
{
    private
        $logger;

    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
    }
}




     Réutilisabilité du code   Page 35
cas concret #2 : empilement de paramètres

class TagServer
{
    private
        $rules,
        $logger;

    public function __construct(Configuration $configuration, Logger $logger)
    {
        $this->logger = $logger;

        $this->rules = $configuration->read(
            'tagserver/Tags/Rules',
            array()
        );
    }
}



        Réutilisabilité du code     Page 36
cas concret #2 : dépendance faible

> Contrairement à la configuration, le logger est une dépendance
  faible

> Un logger n’est pas requis pour le fonctionnement de notre classe



  Injection par mutateur




     Réutilisabilité du code    Page 37
cas concret #2 : injection par mutateur

class TagServer
{
    private
        $logger;

    public function __construct(Configuration $configuration)
    {
        $this->logger = null;
        /* ... */
    }

    public function setLogger(Logger $logger)
    {
        $this->logger = $logger;

           return $this;
    }
}
        Réutilisabilité du code   Page 38
cas concret #2 : injection par mutateur

    private function writeLog($message)
    {
        if($this->logger !== null)
        {
            $this->logger->write($message);
        }
    }


> Et l’appel :

$server = new TagServer(new eZConfiguration());
$server->setLogger(new eZLogger());




     Réutilisabilité du code     Page 39
cas concret #2 : overhead

> Les cas présentés sont simples et petits

> A dimension d’un projet réel, les overheads de code pour construire
  les objets peuvent devenir pénibles à gérer.

> Par exemple, il a fort à parier que le logger soit nécessaire sur de
  nombreuses classes.




     Réutilisabilité du code      Page 40
cas concret #2 : conteneur d’injection

> Solution : recours à un conteneur d’injection

> Pimple (Sensio Labs)
> DI Component de Symfony (Sensio Labs)



> Objet en charge de l’instanciation des autres objets




     Réutilisabilité du code     Page 41
cas concret #2 : conteneur commun

abstract class Container extends Pimple
{
    public function __construct()
    {
        $this['tagServer'] = function ($container){

                   $server = new TagServer($container['configuration']);

                   $server->setLogger($container['logger']);


                   return $server;
            };
    }
}




        Réutilisabilité du code      Page 42
cas concret #2 : conteneur d’injection




     Conteneur commun
 à tous les socles techniques




   Conteneurs spécifiques



    Réutilisabilité du code     Page 43
cas concret #2 : conteneur spécifique (eZ Publish)

class eZContainer extends Container
{
    public function __construct()
    {
        parent::__construct();

            $this['configuration'] = function ($container){
                return new eZConfiguration();
            };

            $this['logger'] = $this->share(function ($container){
                return new eZLogger();
            });

    }
}



        Réutilisabilité du code   Page 44
cas concret #2 : conteneur d’injection

> Et la construction de notre classe :

 $container = new eZContainer();
 $server = $container['tagServer'];




      Réutilisabilité du code     Page 45
cas concret #2 : conteneur d’injection

> Quelques remarques :

   - Le conteneur peut s’appuyer sur de la configuration (ex: Symfony)

   - Risque de dépendance au conteneur + global state

   - Dépendances masquées : quid des outils d’analyse ?




     Réutilisabilité du code       Page 46
Et si on testait ?




     Réutilisabilité du code
testabilité : souvenez-vous

class TagServer
{
    private
        $rules;

    public function __construct()
    {
        $this->rules = array();

             $ini = eZINI::instance('tagserver.ini');
             $ini->assign('Tags', 'Rules', $this->rules);
    }

    // ...
}

        Réutilisabilité du code   Page 48
testabilité : problématique

> Une instance eZ Publish est nécessaire
      Problème de performances des tests


> Un fichier eZINI est également nécessaire
      Eparpillement du code de test
      Maintenabilité affaiblie



> Et si eZINI était un service à bouchonner ? (comme la db, un
  webservice ou le filesystem)




     Réutilisabilité du code          Page 49
testabilité : ArrayConfiguration

> Il faut mocker la configuration      ArrayConfiguration !




     Réutilisabilité du code        Page 50
testabilité : ArrayConfiguration

class ArrayConfiguration implements Configuration
{
    private $values;

    public function __construct(array $values)
    {
        $this->values = $values;
    }

    public function read($variableName, $defaultValue)
    {
        if(array_key_exists($variableName, $this->values))
        {
                 return $this->values[$variableName];
          }

          return $defaultValue;
    }
}
        Réutilisabilité du code         Page 51
testabilité : le test unitaire

class TagServerTest extends PHPUnit_Framework_TestCase
{
    private
        $tagServer;

    public function setUp()
    {
        $configuration = new ArrayConfiguration(array(
            'tagserver/Tags/Rules' => array(/* ... */)
        ));

            $this->tagServer = new TagServer($configuration);
    }
}



        Réutilisabilité du code   Page 52
testabilité : bilan

> C’est testable !

> C’est performant !

> Le test est facile à maintenir !

> Possibilité de tester aussi les cas à la marge :
    -    Configuration manquante
    -    Configuration erronée
    -    Configuration non consistante
    -    …




        Réutilisabilité du code          Page 53
merci!




Réutilisabilité du code     Page 54
si vous avez des questions ?




                          Nicolas Le Nardou
                          Architecte / Expert Technique PHP

                          nicolas.lenardou@orange.com


Réutilisabilité du code                Page 55

Réutilisabilité du code PHP

  • 1.
    Réutilisabilité du code IT&L@bs COPMM Version 1.01, le 29 novembre 2012 Nicolas Le Nardou Architecte / Expert Technique PHP nicolas.lenardou@orange.com Réutilisabilité du code Page 1
  • 2.
    Introduction : contexteprojet > Centre de services d’un grand groupe de presse > 20-30 sites en PHP - eZ Publish, Symfony 2, WordPress, from scratch, … > Forte audience : environ 10M de pages vues / jour > Périmètres fonctionnels très proches > Pression sur les coûts de développement Réutilisabilité du code Page 2
  • 3.
    Introduction : contexteprojet > A son écriture, le partage du code entre plusieurs sites est : - Soit déjà acté - Soit déjà en cours de discussion > Le partage avec les autres sites du CDS est toujours de l’ordre du possible Réutilisabilité du code Page 3
  • 4.
    Introduction : problématique >Comment se construire un référentiel de code commun à tous nos sites ? - Quel que soit le socle technique - Sans renoncer aux apports de ces différents socles (pas de politique du plus petit dénominateur commun) > Objectifs : - Mutualiser la maintenance du code - Réduire les temps de développement Réutilisabilité du code Page 4
  • 5.
    sommaire > 1 –Rappel des principes de conception SOLID > 2 – Etude de cas concrets > 3 – Point sur les tests unitaires Réutilisabilité du code Page 5
  • 6.
    Conception SOLID Rappel Réutilisabilité du code
  • 7.
    conception SOLID > SingleResponsibility > Open / Closed > Liskov substitution > Interface segregation > Dependency injection Réutilisabilité du code Page 7
  • 8.
    SOLID vs STUPID >Singleton > Tight coupling > Untestability > Premature Optimization > Indescriptive Naming > Duplication Réutilisabilité du code Page 8
  • 9.
    SOLID : singleresponsibility > Principe de responsabilité unique : > « Une classe ne fait qu’une et une seule chose » > Envie de rajouter une fonctionnalité ? Il est temps de créer une nouvelle classe. > Une classe au fonctionnement clairement défini et borné sera plus facilement réutilisable > Une classe aux multiples responsabilités sera fatalement dupliquée pour être adaptée au nouveau besoin Réutilisabilité du code Page 9
  • 10.
    SOLID : open/ closed > « Une classe doit être fermée à la modification et ouverte à l’extension » > Une évolution ne devrait pas vous faire casser du code, juste en ajouter ! > Une classe doit prévoir de pouvoir être étendue sans être réécrite. Réutilisabilité du code Page 10
  • 11.
    SOLID : Liskovsubstitution > « On doit pouvoir substituer à un objet d’une classe X, tout objet d’une sous classe de X » > Corolaire : Une classe utilisant un objet de classe X ne doit pas avoir connaissance des sous classes de X (sous peine de violer le principe open/closed) Réutilisabilité du code Page 11
  • 12.
    SOLID : Interfacesegregation > « Un objet ne devra pas dépendre d’un autre objet mais de son interface » > Il faut expliciter la dépendance réelle au travers d’une interface Réutilisabilité du code Page 12
  • 13.
    SOLID : DependencyInjection > « Un objet ne doit pas instancier un autre objet, il doit le recevoir de l’extérieur » > Inversion de contrôle > Pas d’utilisation du mot clé new dans une classe > Injection par constructeur ou mutateur Réutilisabilité du code Page 13
  • 14.
    Cas concret #1 Réutilisabilité du code
  • 15.
    cas concret #1 >Besoin : Injecter dans nos pages des tags javascript (tracking, pub, …) > Implémentation : Une classe TagServer qui calcule la valeur d’un tag en fonction d’un contexte en entrée (url, contenu, …) et d’un jeu de règles - Moteur de règles - Jeu de règles en configuration > Contrainte : A déployer sur : 1. Un site eZ Publish 2. Un site Symfony 2.x Réutilisabilité du code Page 15
  • 16.
    cas concret #1 >Les mauvaises solutions : - Faire 2 développements distincts - Dupliquer la classe et la modifier - Nombreux paramètres dans le constructeur - Ou toute autre abomination … Réutilisabilité du code Page 16
  • 17.
    cas concret #1: implémentation eZ Publish class TagServer { private $rules; public function __construct() { $this->rules = array(); $ini = eZINI::instance('tagserver.ini'); $ini->assign('Tags', 'Rules', $this->rules); } // ... } Réutilisabilité du code Page 17
  • 18.
    cas concret #1: problèmes public function __construct() { $this->rules = array(); $ini = eZINI::instance('tagserver.ini'); $ini->assign('Tags', 'Rules', $this->rules); } > Couplage fort : TagServer dépend de eZINI La classe n’est réutilisable que sur un autre site eZ Publish Réutilisabilité du code Page 18
  • 19.
    cas concret #1: problèmes > Solution : injecter l’objet eZINI dans le constructeur Injection de dépendances (SOLID) > On pourra ainsi substituer à une occurrence d’eZINI, un objet d’une sous classe d’eZINI Réutilisabilité du code Page 19
  • 20.
    cas concret #1: eZINI injecté class TagServer { private $rules; public function __construct(eZINI $ini) { $this->rules = array(); $ini->assign('Tags', 'Rules', $this->rules); } // ... } Réutilisabilité du code Page 20
  • 21.
    cas concret #1: eZINI injecté > La construction du serveur : $ini = eZINI::instance('tagserver.ini'); $server = new TagServer($ini); Réutilisabilité du code Page 21
  • 22.
    cas concret #1: eZINI injecté > Couplage désormais faible > Mais problème de sémantique : conceptuellement nous n’avons pas besoin d’un eZINI, nous avons plutôt besoin de la configuration. Il nous faut une interface « Configuration » Séparation d’interfaces (SOLID) Réutilisabilité du code Page 22
  • 23.
    cas concret #1: interface Configuration interface Configuration { const SEPARATOR = '/'; /** * Read configuration if exists. Returns default value * otherwise. * * @param string $variableName fully qualified variable name * @param mixed $defaultValue */ public function read($variableName, $defaultValue); } Réutilisabilité du code Page 23
  • 24.
    cas concret #1: interface Configuration class TagServer { private $rules; public function __construct(Configuration $config) { $this->rules = $configuration->read( 'tagserver/Tags/Rules', array() ); } } Réutilisabilité du code Page 24
  • 25.
    cas concret #1: interface Configuration > La dépendance avec le framework d’eZ Publish est rompue … > … mais notre code ne fonctionne plus pour eZ Publish > Il nous faut une implémentation de Configuration reposant sur eZINI Substitution de Liskov (SOLID) Réutilisabilité du code Page 25
  • 26.
    cas concret #1: eZConfiguration class eZConfiguration implements Configuration { public function read($variableName, $defaultValue) { list($file, $group, $variable) = explode(self::SEPARATOR, $variableName); $ini = eZINI::instance($file . '.ini'); $ini->assign($group, $variable, $defaultValue); return $defaultValue; } } Réutilisabilité du code Page 26
  • 27.
    cas concret #1: eZConfiguration > Appel $configuration = new eZConfiguration(); $server = new TagServer($configuration); > Fonctionne à nouveau pour eZ Publish - Sans modification de la classe TagServer Open / Closed (SOLID) Réutilisabilité du code Page 27
  • 28.
    cas concret #1: site Symfony > Etape suivante : réutiliser notre classe TagServer sur un site reposant sur Symfony Réutilisabilité du code Page 28
  • 29.
    cas concret #1: site Symfony > Bien sûr, la classe eZConfiguration ne fonctionnera pas > Il nous faut une classe YamlConfiguration Réutilisabilité du code Page 29
  • 30.
    cas concret #1: site Symfony class YamlConfiguration implements Configuration { public function read($variableName, $defaultValue) { list($file, $group, $variable) = explode(self::SEPARATOR, $variableName); $loader = Yaml::parse($file); if(array_key_exists($loader[$group][$variable])) { return $loader[$group][$variable]; } return $defaultValue; } } Réutilisabilité du code Page 30
  • 31.
    cas concret #1: site Symfony > Construction du serveur : $configuration = new YamlConfiguration(); $server = new TagServer($configuration); > Et …. c’est tout ! Réutilisabilité du code Page 31
  • 32.
    cas concret #1: bilan > Coût du déploiement de notre classe TagServer sur un autre framework PHP ≈ Coût de développement d’une classe d’adaptation pour accéder à la configuration > Aucune modification de notre classe TagServer n’a été nécessaire > Les classes de la couche d’adaptation sont elles-mêmes réutilisables constitution d’une boîte à outils très rapidement Réutilisabilité du code Page 32
  • 33.
    Cas concret #2 Réutilisabilité du code
  • 34.
    cas concret #2 >Nous voulons ajouter des logs à notre classe TagServer > Contraintes : - Possibilité de les activer / désactiver - Possibilité de se reposer sur le système de log du socle technique utilisé Réutilisabilité du code Page 34
  • 35.
    cas concret #2 classTagServer { private $logger; public function __construct(Logger $logger) { $this->logger = $logger; } } Réutilisabilité du code Page 35
  • 36.
    cas concret #2: empilement de paramètres class TagServer { private $rules, $logger; public function __construct(Configuration $configuration, Logger $logger) { $this->logger = $logger; $this->rules = $configuration->read( 'tagserver/Tags/Rules', array() ); } } Réutilisabilité du code Page 36
  • 37.
    cas concret #2: dépendance faible > Contrairement à la configuration, le logger est une dépendance faible > Un logger n’est pas requis pour le fonctionnement de notre classe Injection par mutateur Réutilisabilité du code Page 37
  • 38.
    cas concret #2: injection par mutateur class TagServer { private $logger; public function __construct(Configuration $configuration) { $this->logger = null; /* ... */ } public function setLogger(Logger $logger) { $this->logger = $logger; return $this; } } Réutilisabilité du code Page 38
  • 39.
    cas concret #2: injection par mutateur private function writeLog($message) { if($this->logger !== null) { $this->logger->write($message); } } > Et l’appel : $server = new TagServer(new eZConfiguration()); $server->setLogger(new eZLogger()); Réutilisabilité du code Page 39
  • 40.
    cas concret #2: overhead > Les cas présentés sont simples et petits > A dimension d’un projet réel, les overheads de code pour construire les objets peuvent devenir pénibles à gérer. > Par exemple, il a fort à parier que le logger soit nécessaire sur de nombreuses classes. Réutilisabilité du code Page 40
  • 41.
    cas concret #2: conteneur d’injection > Solution : recours à un conteneur d’injection > Pimple (Sensio Labs) > DI Component de Symfony (Sensio Labs) > Objet en charge de l’instanciation des autres objets Réutilisabilité du code Page 41
  • 42.
    cas concret #2: conteneur commun abstract class Container extends Pimple { public function __construct() { $this['tagServer'] = function ($container){ $server = new TagServer($container['configuration']); $server->setLogger($container['logger']); return $server; }; } } Réutilisabilité du code Page 42
  • 43.
    cas concret #2: conteneur d’injection Conteneur commun à tous les socles techniques Conteneurs spécifiques Réutilisabilité du code Page 43
  • 44.
    cas concret #2: conteneur spécifique (eZ Publish) class eZContainer extends Container { public function __construct() { parent::__construct(); $this['configuration'] = function ($container){ return new eZConfiguration(); }; $this['logger'] = $this->share(function ($container){ return new eZLogger(); }); } } Réutilisabilité du code Page 44
  • 45.
    cas concret #2: conteneur d’injection > Et la construction de notre classe : $container = new eZContainer(); $server = $container['tagServer']; Réutilisabilité du code Page 45
  • 46.
    cas concret #2: conteneur d’injection > Quelques remarques : - Le conteneur peut s’appuyer sur de la configuration (ex: Symfony) - Risque de dépendance au conteneur + global state - Dépendances masquées : quid des outils d’analyse ? Réutilisabilité du code Page 46
  • 47.
    Et si ontestait ? Réutilisabilité du code
  • 48.
    testabilité : souvenez-vous classTagServer { private $rules; public function __construct() { $this->rules = array(); $ini = eZINI::instance('tagserver.ini'); $ini->assign('Tags', 'Rules', $this->rules); } // ... } Réutilisabilité du code Page 48
  • 49.
    testabilité : problématique >Une instance eZ Publish est nécessaire Problème de performances des tests > Un fichier eZINI est également nécessaire Eparpillement du code de test Maintenabilité affaiblie > Et si eZINI était un service à bouchonner ? (comme la db, un webservice ou le filesystem) Réutilisabilité du code Page 49
  • 50.
    testabilité : ArrayConfiguration >Il faut mocker la configuration ArrayConfiguration ! Réutilisabilité du code Page 50
  • 51.
    testabilité : ArrayConfiguration classArrayConfiguration implements Configuration { private $values; public function __construct(array $values) { $this->values = $values; } public function read($variableName, $defaultValue) { if(array_key_exists($variableName, $this->values)) { return $this->values[$variableName]; } return $defaultValue; } } Réutilisabilité du code Page 51
  • 52.
    testabilité : letest unitaire class TagServerTest extends PHPUnit_Framework_TestCase { private $tagServer; public function setUp() { $configuration = new ArrayConfiguration(array( 'tagserver/Tags/Rules' => array(/* ... */) )); $this->tagServer = new TagServer($configuration); } } Réutilisabilité du code Page 52
  • 53.
    testabilité : bilan >C’est testable ! > C’est performant ! > Le test est facile à maintenir ! > Possibilité de tester aussi les cas à la marge : - Configuration manquante - Configuration erronée - Configuration non consistante - … Réutilisabilité du code Page 53
  • 54.
  • 55.
    si vous avezdes questions ? Nicolas Le Nardou Architecte / Expert Technique PHP nicolas.lenardou@orange.com Réutilisabilité du code Page 55