<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 24249 -->
<!-- Reviewed: no -->
<sect1 id="performance.classloading">
    <title>Chargement des classes</title>

    <para>
        Tous ceux qui ont déjà réalisé le profilage d'une application Zend Framework
        reconnaîtront immédiatement que le chargement des classes y est relativement coûteux. Entre
        le nombre important de fichier de classe qui doivent être chargées pour un grand nombre de
        composants et l'utilisation des plugins qui n'impliquent pas une relation 1:1 entre leur nom
        de classe et le système de fichier, les différents appels de <code>include_once</code> et
        <code>require_once</code> peuvent être problématique. Ce chapitre a pour but de fournir des
        solutions concrètes pour solutionner ces problèmes.
    </para>

    <sect2 id="performance.classloading.includepath">
        <title>Comment optimiser mon include_path&#160;?</title>

        <para>
            Une optimisation triviale pour accélérer la vitesse de chargement des classes est
            de faire attention à votre <code>include_path</code>. En particulier, vous devriez faire
            quatre choses&#160;: utilisez des chemins absolus (ou des chemins relatifs à des chemins
            absolus), réduire le nombre des chemins à inclure, définir le dossier de Zend Framework
            le plus tôt possible dans l'<code>include_path</code> et inclure le dossier courant en
            dernier dans votre <code>include_path</code>.
        </para>

        <sect3 id="performance.classloading.includepath.abspath">
            <title>Utiliser des chemins absolus</title>

            <para>
                Tandis que ceci peut sembler une micro-optimisation, le fait est que si vous
                ne le faites pas, vous n'obtiendrez qu'un très petit avantage de la mise en cache du
                realpath de PHP, et en conséquence, le cache d'opcode ne fonctionnera pas tout à
                fait comme vous pourriez l'imaginer.
            </para>

            <para>
                Il y a deux manières simples de s'assurer de ceci. Premièrement, vous pouvez
                le mettre en dur dans votre <filename>php.ini</filename>,
                <filename>httpd.conf</filename>, ou <filename>.htaccess</filename>. Deuxièmement,
                vous pouvez utiliser la fonction <code>realpath()</code> de PHP au moment du
                paramétrage de votre <code>include_path</code>&#160;:
            </para>

            <programlisting language="php"><![CDATA[
$paths = array(
    realpath(dirname(__FILE__) . '/../library'),
    '.',
);
set_include_path(implode(PATH_SEPARATOR, $paths);
]]></programlisting>

            <para>
                Vous <emphasis>pouvez</emphasis> utiliser des chemins relatifs - du moment
                qu'ils sont relatifs à un chemin absolu&#160;:
            </para>

            <programlisting language="php"><![CDATA[
define('APPLICATION_PATH', realpath(dirname(__FILE__)));
$paths = array(
    APPLICATION_PATH . '/../library'),
    '.',
);
set_include_path(implode(PATH_SEPARATOR, $paths);
]]></programlisting>

            <para>
                Néanmoins, c'est typiquement une tâche insignifiante de fournir simplement le
                chemin à <code>realpath()</code>.
            </para>
        </sect3>

        <sect3 id="performance.classloading.includepath.reduce">
            <title>Réduire le nombre de dossier défini dans l'include_path</title>

            <para>
                Les chemins d'inclusion sont scannés dans l'ordre dans lequel ils apparaissent
                dans l'<code>include_path</code>. Évidemment, ceci veut dire que vous aurez un
                résultat plus rapide si le fichier est trouvé dans le premier chemin scanné que si
                vous le trouvez dans le dernier chemin scanné. De plus, une amélioration plutôt
                évidente est de diminuer tout simplement le nombre de chemins dans votre
                <code>include_path</code> à seulement de ce que vous avez réellement besoin.
                Regardez chaque chemin que vous avez défini dans votre include_path pour déterminer
                si vous avez réellement besoin d'une fonctionnalité dans votre application&#160;; si
                ce n'est pas le cas, enlevez le.
            </para>

            <para>
                Une autre optimisation consiste en la combinaison de chemins. Par exemple,
                Zend Framework suit la convention de nommage PEAR&#160;; ainsi , si vous utilisez
                des librairies PEAR (ou d'autres framework ou librairies de composants qui
                respectent la convention de nommage PEAR), essayez de mettre toutes ces librairies
                dans le même chemin de l'<code>include_path</code>. Ceci peut souvent être réalisé
                par quelque chose d'assez simple comme de créer des liens symboliques vers une ou
                plusieurs bibliothèques dans un dossier commun.
            </para>
        </sect3>

        <sect3 id="performance.classloading.includepath.early">
            <title>
                Définir le dossier de Zend Framework le plus tôt possible dans l'include_path
            </title>

            <para>
                Pour continuer avec les suggestions précédentes, une autre optimisation
                évidente est de définir le dossier de Zend Framework le plus tôt possible dans votre
                <code>include_path</code>. Dans la plupart des cas, il devrait être le premier de la
                liste. Ceci permet de s'assurer les fichiers de Zend Framework à inclure le sont dès
                le premier scan.
            </para>
        </sect3>

        <sect3 id="performance.classloading.includepath.currentdir">
            <title>Définir le dossier courant le plus tard possible ou pas du tout</title>

            <para>
                La plupart des exemples d'<code>include_path</code> montre l'utilisation du
                répertoire courant ("<code>.</code>"). Ceci est pratique pour s'assurer que les
                scripts dans le même dossier que le fichier courant peuvent aussi être chargés.
                Cependant ces mêmes exemples montrent souvent ce dossier comme étant le premier de
                la liste des include_path - ce qui veut dire l'arbre de dossiers courant est
                toujours scanné en premier. La plupart du temps, avec Zend Framework, ce n'est pas
                nécessaire, et ce dossier peut tout naturellement être mis en dernière position de
                la liste.
            </para>

            <example id="performance.classloading.includepath.example">
                <title>Exemple : optimisation de l'include_path</title>

                <para>
                    Essayons de mettre ensemble toutes ces suggestions. Considérons que nous
                    utilisons une ou plusieurs composants PEAR en conjonction avec Zend Framework -
                    par exemple les composants PHPUnit et Archive_Tar - et qu'il est
                    occasionnellement nécessaire d'inclure les fichiers relativement au fichier
                    courant.
                </para>

                <para>
                    Premièrement, nous allons créer un dossier pour les librairies dans notre
                    projet. Dans ce même dossier, nous allons créer un lien symbolique vers notre
                    dossier Zend Framework "<filename>library/Zend</filename>", ainsi que les
                    dossiers nécessaires dans notre installation PEAR&#160;:
                </para>

                <programlisting language="php"><![CDATA[
library
    Archive/
    PEAR/
    PHPUnit/
    Zend/
]]></programlisting>

                <para>
                    Ceci nous permet d'ajouter notre propre librairie si nécessaire, tout en
                    laissant intact les librairies partagées.
                </para>

                <para>
                    Ensuite, nous optons pur la création de notre <code>include_path</code>
                    par programme à l'intérieur de notre fichier
                    <filename>public/index.php</filename>. Ceci nous permet de déplacer notre code
                    dans le système de fichiers, sans devoir éditer l'<code>include_path</code> à
                    chaque fois.
                </para>

                <para>
                    Nous emprunterons des idées à chacune des suggestions ci-dessus : nous
                    utiliserons les chemins absolus, déterminé en utilisant le
                    <code>realpath()</code>&#160;; nous positionnerons Zend Framework au plus tôt
                    dans l'<code>include_path</code> ; nous avons déjà vérifié les chemins
                    d'inclusions nécessaires&#160;; et nous mettrons le dossier courant comme
                    dernier chemin. En fait, nous faisons tout bien ici - nous allons donc terminer
                    avec seulement deux chemins.
                </para>

                <programlisting language="php"><![CDATA[
$paths = array(
    realpath(dirname(__FILE__) . '/../library'),
    '.'
);
set_include_path(implode(PATH_SEPARATOR, $paths));
]]></programlisting>
            </example>
        </sect3>
    </sect2>

    <sect2 id="performance.classloading.striprequires">
        <title>Comment éliminer les déclarations require_once non nécessaires ?</title>

        <para>
            Le chargement tardif ("lazy loading") est une technique d'optimisation conçue pour
            repousser l'opération coûteuse de chargement d'une classe jusqu'au dernier moment
            possible - c'est-à-dire lors de l'instanciation d'un objet de cette classe, ou lors de
            l'utilisation d'une constante de classe ou d'une propriété statique. PHP supporte tout
            ceci via l'autoloading (ou "chargement automatique"), ce qui vous permet de définir un
            ou plusieurs callbacks à exécuter dans le but de faire correspondre un nom de classe à
            un fichier.
        </para>

        <para>
            Cependant, la plupart des avantages que vous pourrez retirer de l'autoloading sont
            diminués si le code de votre librairie exécute toujours des appels à
            <code>require_once</code> - ce qui est précisément le cas de Zend Framework. La question
            est donc : comment éliminer ces déclarations <code>require_once</code> dans le but de
            maximiser les performances de l'autoloader.
        </para>

        <sect3 id="performance.classloading.striprequires.sed">
            <title>Effacer les appels de require_once avec find et sed</title>

            <para>
                Une manière simple d'effacer les appels require_once est d'utiliser les
                utilitaires Unix "find" en conjonction avec "sed" pour passe en commentaires tous
                les appels. Essayez d'exécuter les commandes suivantes (où "%" indique le prompteur
                shell)&#160;:
            </para>

            <programlisting language="shell"><![CDATA[
% cd chemin/vers/la/librarie/ZendFramework
% find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \
  -not -wholename '*/Application.php' -print0 | \
  xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'
]]></programlisting>

            <para>
                Cette ligne unique (coupée en deux pour la lisibilité) itère parmi les
                fichiers PHP et y remplace toute les instances de <code>require_once</code> par
                <code>//require_once</code>, c'est-à-dire en commentant toutes ces lignes (tout
                en maintenant les appels à <function>require_once</function> dans
                <classname>Zend_Application</classname> et
                <classname>Zend_Loader_Autoloader</classname>, puisque ces classes tomberont en
                erreur sans ceux-ci).
            </para>

            <para>
                Cette commande peut être simplement ajoutée à un script de construction
                automatique ou à un processus de mise en production, permettent ainsi d'augmenter
                les performances de votre application en production. Il est à noter, cependant, que
                si vous utilisez cette technique, vous <emphasis>devez</emphasis> utiliser
                l'autoloading ; vous pouvez l'activer dans votre fichier
                <filename>public/index.php</filename> en ajoutant le code suivant&#160;:
            </para>

            <programlisting language="php"><![CDATA[
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();
]]></programlisting>
        </sect3>
    </sect2>

    <sect2 id="performance.classloading.pluginloader">
        <title>Comment accélérer le chargement des plugins&#160;?</title>

        <para>
            Certains composants utilisent les plugins, ce qui vous permet de créer vos propres
            classes afin de les utiliser avec le composant, de même que de surcharger les plugins
            standard existants embarqués dans Zend Framework. Ceci fournit une importante
            flexibilité au framework, mais a un prix&#160;: le chargement des plugins est une tâche
            assez coûteuse.
        </para>

        <para>
            Le chargeur de plugins vous permet de définir des paires préfixe de classe /
            chemin, vous autorisant ainsi à spécifier des fichiers de classe dans des chemins de
            dossiers non standard. Chaque préfixe peut avoir de multiples chemins associés. En
            interne, le chargeur de plugins boucle à travers chaque préfixe, et ensuite à travers
            chaque chemin lui étant associé, en testant l'existence du fichier et s'il est
            accessible dans ce chemin. Il le charge ensuite, et teste pour voir si la classe
            recherchée est bien disponible. Comme vous pouvez l'imaginer, tout ceci entraîne des
            appels aux stats du système de fichiers.
        </para>

        <para>
            Multipliez ceci par le nombre de composants qui utilisent le PluginLoader, et vous
            aurez une idée de l'importance de ce problème. Au moment de l'écriture de ce document,
            les composants suivants utilisent le PluginLoader&#160;:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <classname>Zend_Controller_Action_HelperBroker</classname>&#160;: aides
                    d'action
                </para>
            </listitem>

            <listitem>
                <para>
                    <classname>Zend_Dojo</classname>&#160;: aides de vues, éléments de formulaires
                    et décorateurs
                </para>
            </listitem>

            <listitem>
                <para><classname>Zend_File_Transfer</classname>&#160;: adaptateurs</para>
            </listitem>

            <listitem>
                <para>
                    <classname>Zend_Filter_Inflector</classname>&#160;: filtres (utilisé par l'aide
                    d'action <code>ViewRenderer</code> et <classname>Zend_Layout</classname>)
                </para>
            </listitem>

            <listitem>
                <para><classname>Zend_Filter_Input</classname>&#160;: filtres et validateurs</para>
            </listitem>

            <listitem>
                <para>
                    <classname>Zend_Form</classname>&#160;: éléments, validateurs, filtres,
                    décorateurs, captcha et adaptateur pour les transferts de fichiers
                </para>
            </listitem>

            <listitem>
                <para><classname>Zend_Paginator</classname>&#160;: adaptateurs</para>
            </listitem>

            <listitem>
                <para><classname>Zend_View</classname>&#160;: aides de vues, filtres</para>
            </listitem>
        </itemizedlist>

        <para>Comment réduire le nombre des appels réalisés&#160;?</para>

        <sect3 id="performance.classloading.pluginloader.includefilecache">
            <title>Utiliser le fichier de cache des inclusions du PluginLoader</title>

            <para>
                Zend Framework 1.7.0 ajoute un fichier de cache des inclusions au
                PluginLoader. Cette fonctionnalité écrit dans un fichier les appels
                "<code>include_once</code>", que vous pouvez ensuite inclure dans votre fichier
                d'amorçage. Même si ceci introduit de nouveaux appels include_once dans votre code,
                cela permet de s'assurer que le PluginLoader les retournera au plus vite.
            </para>

            <para>
                La documentation du PluginLoader <link
                linkend="zend.loader.pluginloader.performance.example">inclue un exemple complet de
                son utilisation</link>.
            </para>
        </sect3>
    </sect2>
</sect1>