I. Prérequis

La version du Zend Framework 2 utilisée pour cet article est la 2.0.0beta4. L'utilisation du Zend Framework 2 nécessite au minimum la version 5.3.3 de PHP.

Zend Framework 2.0 RequirementsZend Framework 2.0 Requirements

II. Le ServiceManager en complément du Di.

Si le composant d'injection de dépendance présente de nombreux avantages, comme une plus grande flexibilité dans la gestion de nos différents services et dans l'injection de nos dépendances, celui-ci a les inconvénients d'être difficile à appréhender et de voir les performances se dégrader. En effet, si les définitions des composants utilisés ne sont pas connues, le composant Di s'occupe alors d'aller récupérer la définition des objets à l'exécution. Seulement, le fait de précompiler les définitions de nos composants et bibliothèques ne sera peut-être pas suffisant pour retrouver de meilleures performances, et cela ajoute encore une couche de complexité à notre application.

Afin de répondre à cette problématique, un nouveau composant laisse la possibilité aux développeurs de créer leurs propres objets de fabrique sans la gestion automatique des définitions et des dépendances, le Zend\ServiceManager. Ce composant, bien qu'autonome, possède un sous-composant Zend\ServiceManager\Di capable de tirer les avantages du composant d'injection de dépendance avec la simplicité du ServiceManager.

Ce nouveau composant permet une approche plus simple et plus performante aux développeurs qui font face à des problèmes complexes de création et de partage de services. Le ServiceManager s'initialise avec des objets de configuration que l'on va examiner afin de comprendre toutes les possibilités du gestionnaire de services.

III. Fabriques d'objets depuis le gestionnaire

Le composant ServiceManager se configure depuis un objet de configuration de type Zend\ServiceManager\Configuration dont voici les différents attributs :

• « invokables » : les classes renseignées comme étant « invokables » seront instanciées directement sans la nécessité d'utiliser une fabrique. Ce type conviendra aux objets sans dépendance particulière ;

• « factories » : la configuration liée à la clé « factories » permet de définir la liste de nos classes de fabrique. Une classe de fabrique est associée à un type d'objet et retourne une instance de celui-ci ;

• « abstract_factories » : les fabriques abstraites ressemblent aux classes de fabriques à la différence que celles-ci ne sont pas attachées à un objet en particulier, elles sont appelées lorsqu'aucune fabrique n'existe pour l'objet demandé ;

• « services » : les valeurs renseignées dans le champ « services » peuvent être de tout type (entier, chaîne de caractères, tableau, objets, etc.) et sont associées à la clé donnée ;

• « aliases » : les alias permettent de définir plusieurs clés pour un même objet. Il est aussi possible de faire des alias d'alias autant que l'on souhaite.

La configuration est passée en paramètre du constructeur du ServiceManager :

création du ServiceManager
Sélectionnez
$serviceManager = new ServiceManager(new Configuration(array(
    'invokables' => array(
        'simple' => 'Invokables\SimpleClass',
    ),
    'factories' => array(
        'cache' => 'Factories\CacheStorageFactory',
    ),
    'abstract_factories' => array(
        'generic' => 'AbstractFactories\AbstractFactory',
    ),
)));
$serviceManager->setService('Configuration', include 'configuration.php');

La récupération d'une instance d'objet depuis le ServiceManager se fait depuis la méthode « get() », comme pour le composant d'injection de dépendance. Cette méthode vérifie l'existence de fabrique pour l'objet demandé suivant la liste :

• classes « invokable » ;

• fabrique d'objets ;

• fabrique abstraite.

Regardons ces différents type de fabriques disponibles.

III-A. Les classes « invokable »

Les classes marquées comme « invokable » seront instanciées directement par le gestionnaire de services sans plus d'instructions :

Fabrique d'une classe "invokable"
Sélectionnez
$simpleClass = $serviceManager->get('simple');
echo get_class($simpleClass) . "\n"; // Invokables\SimpleClass

Lorsque le gestionnaire de service trouve une classe de type « invokable » correspondant à l'identifiant demandé, celui-ci se contente de retourner son instance depuis l'instruction « new » .

III-B. Les fabriques

Les fabriques vont être parcourues lorsqu'aucune classe « invokable » n'existe pour l'objet à instancier, voici un exemple d'utilisation :

Création depuis une fabrique
Sélectionnez
$cache = $serviceManager->get('cache');
echo get_class($cache) . "\n"; // Zend\Cache\Storage\Adapter\Filesystem

La fabrique de cache est la suivante :

CacheStorageFactory.php
Sélectionnez
class CacheStorageFactory implements FactoryInterface
{
     public function createService(ServiceLocatorInterface $serviceLocator)
     {
          $configuration = $serviceLocator->get('Configuration');
          return StorageFactory::factory($configuration['cache']);
     }
} 

L'objet de configuration a été défini par l'instruction :

configuration
Sélectionnez
$serviceManager->setService('Configuration', include 'configuration.php');
configuration.php
Sélectionnez
return array(
    'cache' => array(
        'adapter' => array(
            'name' => 'filesystem',
            'options' => array(
                'namespace' => 'zfcache',
                'cache_dir' => __DIR__ . '/data/',
                'ttl' => 0,
            ),
        ),
    ),
);

La fabrique retourne l'instance de notre objet de cache. Nous comprenons immédiatement l'avantage d'une telle fabrique par rapport au composant Di qui aurait créé la définition de cet objet avant d'exécuter de complexes instructions afin de l'instancier. Le développeur a la main sur la fabrique de son objet grâce au ServiceManager.

III-C. Les fabriques abstraites

Les fabriques abstraites interviennent lorsqu'aucune fabrique ne peut instancier l'objet demandé au gestionnaire. Contrairement à la fabrique vue précédemment, la fabrique abstraite n'est attachée à aucun objet en particulier, elle est plus générique et implémente l'interface Zend\ServiceManager\AbstractFactoryInterface qui définit deux méthodes :

Zend\ServiceManager\AbstractFactoryInterface.php
Sélectionnez
namespace Zend\ServiceManager;

interface AbstractFactoryInterface
{
    public function canCreateServiceWithName($name /*, $requestedName */);
    public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name /*, $requestedName */);
}

La première méthode « canCreateServiceWithName » s'assure que la fabrique abstraite peut instancier un objet de nom « $name », et la seconde méthode permet l'instanciation de cet objet. Voici un exemple :

Création depuis une fabrique abstraite
Sélectionnez
$bar = $serviceManager->get('bar');
echo get_class($bar) . "\n"; // AbstractFactories\Classes\Bar

Comme aucune classe « invokable » et aucune fabrique n'existent pour l'objet « bar », la fabrique abstraite AbstractFactories\AbstractFactory que l'on a définie dans la configuration va être appelée :

AbstractFactories\AbstractFactory.php
Sélectionnez
class AbstractFactory implements AbstractFactoryInterface
{
    public function canCreateServiceWithName($name)
    {   
        return in_array(strtolower($name), array('foo', 'bar'));
    }

    public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name)
    {
        $className = 'AbstractFactories\\Classes\\' . ucfirst($name);
        return new $className;
    }
}

Celle-ci étant capable d'instancier l'objet « bar », elle va donc pouvoir retourner l'instance demandée.

IV. La surcouche du composant Di

Le composant ServiceManager n'a pas été introduit afin de remplacer le composant d'injection de dépendance, mais afin de lui être complémentaire en offrant d'autres possibilités de fabriques d'objets. Cependant, les deux composants peuvent fonctionner ensemble, une passerelle existe avec le sous-composant Zend\ServiceManager\Di.

La fabrique abstraite disponible dans le composant Zend\ServiceManager\Di permet d'ajouter le Di comme fabrique abstraite et dernière couche à utiliser. En effet, ceci va permettre l'utilisation du composant d'injection de dépendance si aucune classe « invokable », fabrique ou fabrique abstraite n'a été définie par le développeur, ce qui permet de s'assurer de la bonne instanciation de l'objet et de conserver les fabriques les plus performantes et moins génériques en premier. Voici son utilisation :

Création avec le DiAbstractServiceFactory
Sélectionnez
$diAbstractFactory = new DiAbstractServiceFactory($di);
$serviceManager->addAbstractFactory($diAbstractFactory);
$date = $serviceManager->get('Zend\Date\Date');
echo $date->toString('Y') . "\n"; // 2012

Comme aucune fabrique définie par le développeur n'existe pour l'alias « Zend\Date\Date », c'est le composant d'injection de dépendance qui s'en chargera.

V. Conclusion et ressources

Le ServiceManger du Zend Framework 2 offre une plus grande souplesse tout en étant complémentaire à l'injection de dépendance.
Cet article est tiré en partie du livre "Au cœur de Zend Framework 2"livre "Au cœur de Zend Framework 2" disponible dans le début du quatrième trimestre de l'année 2012. Dans ce livre, vous retrouverez en détail tout le fonctionnement du ServiceManager afin d'en maîtriser l'utilisation. Ce tutoriel est volontairement simple afin de présenter le gestionnaire de services dans ses grandes lignes.

Retrouvez mes modules ou autres fonctionnalités, créés pour le Zend Framework 2, sur mon compte github disponible à l'adresse https://github.com/blanchonvincenthttps://github.com/blanchonvincent.

Retrouvez aussi la liste des tutoriels du Zend Framework 2 sur mon compte developpez.com : blanchon-vincent.developpez.comblanchon-vincent.developpez.com.