<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 24249 -->
<!-- Reviewed: no -->
<sect1 id="zend.loader.pluginloader">
    <title>Chargeur de Plugins</title>

    <para>
        Zend Framework vous propose l'utilisation de composants "pluggables", que vous créez
        vous même. Ceux-ci ne sont pas forcément dans l'include_path. De même, ils ne suivent pas
        forcément les mêmes règles de nommage que les composants de Zend
        Framework.<classname>Zend_Loader_PluginLoader</classname> propose une solution à ce
        problème.
    </para>

    <para>
        <code>PluginLoader</code> suit la convention "une classe par fichier" et les tirets
        bas sont utilisés comme séparateurs de dossiers. Il accepte aussi qu'un préfixe optionnel
        lui soit passé, afin de charger une classe. Tous les chemins sont analysés en ordre LIFO.
        Grâce à ces deux spécificités, vous pouvez "namespacer" vos plugins, et écraser les plugins
        enregistrés plus tôt.
    </para>

    <sect2 id="zend.loader.pluginloader.usage">
        <title>Utilisation basique</title>

        <para>
            Même si nous parlons de "plugins", ce n'est pas réservé aux plugins de contrôleur
            frontal, mais bien à toute classe étant utilisée avec Zend Framework. Imaginons une
            structure de répertoires comme suit, dans laquelle les dossiers "application" et
            "library" sont dans l'include_path :
        </para>

        <programlisting language="txt"><![CDATA[
application/
    modules/
        foo/
            views/
                helpers/
                    FormLabel.php
                    FormSubmit.php
        bar/
            views/
                helpers/
                    FormSubmit.php
library/
    Zend/
        View/
            Helper/
                FormLabel.php
                FormSubmit.php
                FormText.php
]]></programlisting>

        <para>
            Maintenant créons un chargeur de plugins pour utiliser les différentes classes
            d'aides de vue :
        </para>

        <programlisting language="php"><![CDATA[
$loader = new Zend_Loader_PluginLoader();
$loader->addPrefixPath('Zend_View_Helper',
                       'Zend/View/Helper/')
       ->addPrefixPath('Foo_View_Helper',
                       'application/modules/foo/views/helpers')
       ->addPrefixPath('Bar_View_Helper',
                       'application/modules/bar/views/helpers');
]]></programlisting>

        <para>
            Il devient alors possible de charger une aide de vue en spécifiant juste le nom de
            sa classe :
        </para>

        <programlisting language="php"><![CDATA[
// charge l'aide 'FormText' :
$formTextClass = $loader->load('FormText');
// 'Zend_View_Helper_FormText'

// charge l'aide 'FormLabel' :
$formLabelClass = $loader->load('FormLabel');
// 'Foo_View_Helper_FormLabel'

// charge l'aide 'FormSubmit' :
$formSubmitClass = $loader->load('FormSubmit');
// 'Bar_View_Helper_FormSubmit'
]]></programlisting>

        <para>Une fois chargée, la classe devient instanciable.</para>

        <note>
            <title>Plusieurs dossiers peuvent être assignés à un même préfixe</title>

            <para>
                Vous pouvez "namespacer" vos composants en enregistrant plusieurs chemins pour
                un même préfixe.<classname>Zend_Loader_PluginLoader</classname> cherchera alors en
                ordre LIFO (dernier arrivé, premier sorti). Ceci est pratique pour court-circuiter
                ses composants et utiliser ceux en incubateur, par exemple.
            </para>
        </note>

        <note>
            <title>Paramétrage des chemins dans le constructeur</title>

            <para>
                En constructeur, passez un tableau de paires prefix / path ou prefix / paths
                -- plusieurs dossiers par préfixe :
            </para>

            <programlisting language="php"><![CDATA[
$loader = new Zend_Loader_PluginLoader(array(
    'Zend_View_Helper' => 'Zend/View/Helper/',
    'Foo_View_Helper'  => 'application/modules/foo/views/helpers',
    'Bar_View_Helper'  => 'application/modules/bar/views/helpers'
));
]]></programlisting>
        </note>

        <para>
            <classname>Zend_Loader_PluginLoader</classname> peut aussi permettre de partager
            des plugins grâce au registre. Indiquez le nom du registre de cette manière :
        </para>

        <programlisting language="php"><![CDATA[
// Stocke les plugins dans le registre à l'index 'foobar':
$loader = new Zend_Loader_PluginLoader(array(), 'foobar');
]]></programlisting>

        <para>
            Si un autre composant instancie le <code>PluginLoader</code> en utilisant le même
            nom de registre, alors tous les chemins et plugins déjà chargés seront
            disponibles.
        </para>
    </sect2>

    <sect2 id="zend.loader.pluginloader.paths">
        <title>Manipulation des chemins des Plugins</title>

        <para>
            Pour afficher ou supprimer des chemins déjà enregistrés, utilisez l'une des
            méthodes suivantes :
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <methodname>getPaths($prefix = null)</methodname> retourne les chemin sous la forme
                    prefix / path si <varname>$prefix</varname> n'est pas renseigné. Sinon, ce sont les
                    chemins enregistrés pour le préfixe en question qui sont renvoyés.
                </para>
            </listitem>

            <listitem>
                <para>
                    <methodname>clearPaths($prefix = null)</methodname> va effacer tous les chemins. Si
                    <varname>$prefix</varname> est passé, ce sont les chemins correspondants à ce préfixe
                    qui seront supprimés.
                </para>
            </listitem>

            <listitem>
                <para>
                    <methodname>removePrefixPath($prefix, $path = null)</methodname> permet de supprimer
                    un chemin précis, d'un préfixe spécifié. Si <varname>$path</varname> n'est pas
                    renseigné, tous les chemins du préfixe seront effacés.
                </para>
            </listitem>
        </itemizedlist>
    </sect2>

    <sect2 id="zend.loader.pluginloader.checks">
        <title>Test des Plugins et récupération des noms de classe</title>

        <para>
            Lorsque vous voulez savoir si une classe de plugin a été chargée,
            <methodname>isLoaded()</methodname> prend en paramètre le nom du plugin, et retourne sont
            statut.
        </para>

        <para>
            Une autre utilisation de <code>PluginLoader</code> peut être de récupérer le nom
            des classes des plugins chargés.<methodname>getClassName()</methodname> vous le permet. Utilisée en
            conjonction avec <methodname>isLoaded()</methodname>, vous pouvez écrire par exemple ceci :
        </para>

        <programlisting language="php"><![CDATA[
if ($loader->isLoaded('Adapter')) {
    $class   = $loader->getClassName('Adapter');
    $adapter = call_user_func(array($class, 'getInstance'));
}
]]></programlisting>
    </sect2>

    <sect2 id="zend.loader.pluginloader.performance">
        <title>Obtenir de meilleures performances avec les Plugins</title>

        <para>
            Le chargement des plugins peut être une opération consommatrice en ressources. En
            interne, il doit parcourir chaque préfixe, ainsi que chaque chemin dans ce préfixe
            jusqu'à ce qu'il trouve un fichier qui correspond - et qui définit de plus la classe
            voulue. Dans le cas où le fichier existe mais ne défini pas la classe, une erreur sera
            ajouté à la pile d'erreur <acronym>PHP</acronym>, opération qui est elle aussi consommatrice. La question
            qui vient à l'esprit est : comment maintenir la flexibilité des plugins et la
            performance ?
        </para>

        <para>
            <classname>Zend_Loader_PluginLoader</classname> offre une fonctionnalité intégrée
            pour ce cas, un fichier de cache des inclusions de classe. Quand il est activé, ceci
            crée un fichier qui contient toutes les inclusions qui ont fonctionnées et qui peuvent
            donc être appelées dans votre fichier d'initialisation. En utilisant ceci, vous pouvez
            considérablement accroître les performances de vos serveurs de production.
        </para>

        <example id="zend.loader.pluginloader.performance.example">
            <title>Utilisation du fichier de cache des inclusions de classe de
            PluginLoader</title>

            <para>
                Pour utiliser le fichier de cache des inclusions de classe, collez simplement
                le code suivant dans votre fichier d'initialisation :
            </para>

            <programlisting language="php"><![CDATA[
$classFileIncCache = APPLICATION_PATH .  '/../data/pluginLoaderCache.php';
if (file_exists($classFileIncCache)) {
    include_once $classFileIncCache;
}
Zend_Loader_PluginLoader::setIncludeFileCache($classFileIncCache);
]]></programlisting>

            <para>
                Évidemment, le chemin et le fichier varieront en fonction de vos besoins. Ce
                code doit intervenir aussi vite que possible, pour s'assurer que tous les composants
                à base de plugins pourront l'utiliser.
            </para>

            <para>
                En cours du développement, vous pouvez souhaiter désactiver le cache. Une
                méthode permettant ceci est d'utiliser une clé de configuration pour déterminer ou
                non si le chargeur de plugins doit mettre les informations en cache.
            </para>

            <programlisting language="php"><![CDATA[
$classFileIncCache = APPLICATION_PATH .  '/../data/pluginLoaderCache.php';
if (file_exists($classFileIncCache)) {
    include_once $classFileIncCache;
}
if ($config->enablePluginLoaderCache) {
    Zend_Loader_PluginLoader::setIncludeFileCache($classFileIncCache);
}
]]></programlisting>

            <para>
                Cette technique vous permet de restreindre les modifications au seul fichier
                de configuration plutôt que dans votre code.
            </para>
        </example>
    </sect2>
</sect1>