<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 16619 -->
<!-- Reviewed: no -->
<sect1 id="performance.classloading">
    <title>Laden von Klassen</title>

    <para>
        Jeder der jemals Profiling von Zend Framework Anwendungen durchführen muß, wird sofort
        feststellen das das Laden von Klassen relativ teuer ist im Zend Framework. Zwischen der
        reinen Anzahl von Klassendateien die für viele Komponenten geladen werden müssen, und der
        Verwendung von Plugins die keine 1:1 Verknüpfung zwischen Ihrem Klassennamen und dem
        Dateisystem haben, können die Aufrufe zu <methodname>include_once()</methodname> und
        <methodname>require_once()</methodname> problematisch sein. Diese Kapitel versucht einige
        konkrete Lösungen für diese Probleme zu geben.
    </para>

    <sect2 id="performance.classloading.includepath">
        <title>Wie kann ich meinen include_path optimieren?</title>

        <para>
            Eine triviale Optimierung die man machen kann um die Geschwindigkeit für das Laden der
            Klassen zu erhöhen ist es, auf den include_path besonders Rücksicht zu nehmen. Im
            speziellen, sollte man vier Dinge tun: Absolute Pfade verwenden (oder Pfade relativ zu
            absoluten Pfaden), die Anzahl der Include Pfade die man definiert reduzieren, den
            include_path von Zend Framework so früh wie möglich zu haben, und am Ende von
            include_path nur den aktuellen Verzechnispfad inkludieren.
        </para>

        <sect3 id="performance.classloading.includepath.abspath">
            <title>Absolute Pfade verwenden</title>

            <para>
                Auch wenn das wie eine Mikro-Optimierung aussieht, ist es Fakt das wenn man es
                nicht durchführt, die Vorteile von <acronym>PHP</acronym>'s RealPath Cache sehr
                klein sind, und als Ergebnis, das OpCode Caching nicht so schnell sein wird wie man
                erwarten könnte.
            </para>

            <para>
                Es gibt zwei einfache Wege um das Sicherzustellen. Erstens, kann man die Pfade in
                der <filename>php.ini</filename>, <filename>httpd.conf</filename>, oder
                <filename>.htaccess</filename> Hardcoded hineinschreiben. Zweitens, kann man
                <acronym>PHP</acronym>'s <methodname>realpath()</methodname> Funktion verwendet
                wenn man den include_path setzt:
            </para>

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

            <para>
                Man <emphasis>kann</emphasis> relative Pfade verwenden -- solange Sie relativ zu
                einem absoluten Pfad sind:
            </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>
                Wie auch immer, selbst auf diese Art, ist es typischerweise eine triviale Aufgabe
                um den Pfad einfach an <methodname>realpath()</methodname> zu übergeben.
            </para>
        </sect3>

        <sect3 id="performance.classloading.includepath.reduce">
            <title>Die Anzahl der Include Pfade die man definiert reduzieren</title>

            <para>
                Include Pfade werden in der Reihenfolge in der Sie im include_path vorkommen
                durchsucht. Natürlich bedeutet das, das man ein Ergebnis schneller erhält wenn die
                Datei im ersten Scan gefunden wird, statt im letzten. Deswegen ist eine
                offensichtliche Verbesserung wenn man einfach die Anzahl der Pfade im include_path
                nur auf die reduziert die man benötigt. Man muß sich jeden include_path den man
                definiert hat anschauen, und feststellen ob aktuell irgendeine Funktionalität in
                diesem Pfad ist, die in der eigenen Anwendung verwendet wird; wenn nicht, dann
                entfernen Sie ihn.
            </para>

            <para>
                Eine weitere Optimierung ist es Pfade zu kombinieren. Zum Beispiel folgt Zend
                Framework der Namenskonvention von <acronym>PEAR</acronym>; deswegen kann man, wenn
                man <acronym>PEAR</acronym> Bibliotheken verwendet (oder Bibliotheken von anderen
                Frameworks oder Komponentenbibliotheken die dem <acronym>PEAR</acronym> CS folgen),
                versuchen alle diese Bibliotheken in den gleichen include_path zu geben. Das kann
                oft durchgeführt werden indem etwas einfaches wie ein SymLink auf eine oder mehrere
                Bibliotheken in ein generelles Verzeichnis gelegt wird.
            </para>
        </sect3>

        <sect3 id="performance.classloading.includepath.early">
            <title>Definiere den include_path zum Zend Framework so früh wie möglich</title>

            <para>
                Als Fortführung des vorherigen Vorschlags, ist eine weitere offensichtliche
                Optimierung die Definierung vom include_path vom Zend Framework so früh wie möglich
                im include_path. In den meisten Fällen sollte er der Erste in der Liste sein. Das
                stellt sicher das die Dateien die vom Zend Framework included werden schon beim
                Ersten Scan gefunden werden.
            </para>
        </sect3>

        <sect3 id="performance.classloading.includepath.currentdir">
            <title>Definiere das aktuelle Verzeichnis als letztes oder gar nicht</title>

            <para>
                Die meisten Beispiele von include_path zeigen die Verwendung des aktuellen
                Verzeichnisses oder '.'. Das ist üblich um sicherzustellen das Skripte die im
                gleichen Verzeichnis wie die Datei die Sie benötigt geladen werden können.
                Trotzdem, zeigen die gleichen Beispiele typischerweise dieses Pfadelement als
                erstes Element im include_path -- was bedeuetet das der aktuelle Verzeichnisbaum
                immer zuerst gescannt wird. In den meisten Fällen, wenn man Zend Framework
                Anwendungen hat, ist das nicht gewünscht, und der Pfad kann ohne Probleme als
                letztes Element in der Liste verschoben werden.
            </para>

            <example id="performance.classloading.includepath.example">
                <title>Beispiel: Optimierter include_path</title>

                <para>
                    Fügen wir also alle diese Vorschläge zusammen. Unsere Annahme wird sein, das
                    man ein oder mehrere <acronym>PEAR</acronym> Bibliotheken in Verbindung mit dem
                    Zend Framework verwendet -- möglicherweise die PHPUnit und
                    <classname>Archive_Tar</classname> Bibliotheken -- und das man
                    offensichtlicherweise die Dateien relativ zur aktuellen Datei einfügen muß.
                </para>

                <para>
                    Zuerst, erstellen wir ein Bibliotheksverzeichnis in unserem Projekt. Innerhalb
                    dieses Verzeichnisses, erstellen wir einen Symlink zu unserer Zend Framework
                    <filename>library/Zend</filename> Verzeichnis, wie auch dem notwendigen
                    Verzeichnis von unserer <acronym>PEAR</acronym> Installation:
                </para>

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

                <para>
                    Das erlaubt es und unseren eigenen Blbiliothekscode hinzuzufügen wenn das
                    notwendig werden sollte, wärend andere verwendete Bibliotheken intakt bleiben.
                </para>

                <para>
                    Als nächstes erstellen wir unseren include_path programmtechnisch in unserer
                    <filename>public/index.php</filename> Datei. Das erlaubt es uns unseren Code in
                    unserem Dateisystem zu verschieben, ohne das es notwendig ist jedesmal den
                    include_path zu bearbeiten.
                </para>

                <para>
                    Wir borgen uns Ideen von jedem der obigen Vorschläge aus: Wir verwenden
                    absolute Pfade, die durch die Verwendung von
                    <methodname>realpath()</methodname> erkannt werden; wir fügen den Zend
                    Framework so früh wie möglich in den include_path ein; wir haben bereits
                    Include Pfade erstellt; und wir geben das aktuelle Verzeichnis als letzten Pfad
                    hinein. Faktisch, machen wir es hier sehr gut -- wir werden mit nur zwei Pfaden
                    enden.
                </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>Wie kann man unnötige require_once Anweisungen entfernen?</title>

        <para>
            Lazy Loading ist eine Optimierungstechnik die entwickelt wurde um die teure Operation
            des Ladens einer Klassendatei bis zum Letztmöglichen Moment zu verzögern -- bzw., wenn
            ein Objekt dieser Klasse instanziiert wird, wenn eine statische Klassenmethode
            aufgerufen wird, oder wenn auf eine Klassenkonstante oder statische Eigenschaft
            referenziert wird. <acronym>PHP</acronym> unterstützt das durch Autoloading, welches es
            erlaubt ein oder mehrere Callbacks zu definieren die in Reihenfolge aufgerufen werden
            um einen Klassennamen mit einer Datei zu verbinden.
        </para>

        <para>
            Trotzdem sind die meisten Vorteile man Autoloading erwarten könnte, hinfällig wenn der
            Bibliothekscode weiterhin <methodname>require_once()</methodname> Aufrufe durchführt --
            was präzise der Fall ist beim Zend Framework. Die Frage ist also: Wie kann man diese
            <methodname>require_once()</methodname> Aufrufe entfernen um die Geschwindigkeit vom
            Autoloader zu maximieren?
        </para>

        <sect3 id="performance.classloading.striprequires.sed">
            <title>Aufrufe von require_once mit find und sed entfernen</title>

            <para>
                Ein einfacher Weg um <methodname>require_once()</methodname> Aufrufe zu entfernen
                ist die Verwendung der <acronym>UNIX</acronym> Utilities 'find' und 'set' in
                Verbindung um jeden Aufruf auszukommentieren. Führe die folgenden Anweisungen aus
                (wobei '%' der Shell Prompt ist):
            </para>

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

            <para>
                Dieser Ein-Zeiler (wegen der Lesbarkeit in zwei Zeilen gebrochen) geht durch jede
                <acronym>PHP</acronym> Datei und sagt Ihr das jede Instanz von 'require_once' mit
                '// require_once' ersetzt werden soll, was jede dieser Anweisungen effektiv
                auskommentiert.
            </para>

            <para>
                Dieses Kommando sollte in einem automatischen Build oder Release Prozess ganz
                trivial hinzugefügt werden. Es sollte trotzdem klar sein das man, wenn man diese
                Technik verwendet, Autoloading verwendetn <emphasis>muss</emphasis>; man kann das
                von der eigenen "<filename>public/index.php</filename>" Datei mit dem folgenden
                Code tun:
            </para>

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

    <sect2 id="performance.classloading.pluginloader">
        <title>Wie kann ich das Laden der Plugins beschleunigen?</title>

        <para>
            Viele Komponenten haben Plugins, welche es erlauben eigene Klassen zu Erstellen und in
            der Komponente zu verwenden, sowie bestehende Standardplugins vom Zend Framework, zu
            überladen. Das bietet eine wichtige Flexibilität für den Framework, aber zu einem
            Preis: Das Laden der Plugins ist eine recht teure Aufgabe.
        </para>

        <para>
            Der Pluginlader erlaubt es Klassenpräfixe / Pfadpaare zu registrieren, was es erlaubt
            Klassendateien in nicht-standard Pfaden zu spezifizieren. Jeder Präfix kann mehrere mit
            Ihm assoziierte Pfade haben. Intern durchläuft der Pluginlader jeden Präfix, und dann
            jeden Ihm angehängten Pfad, testet od die Datei existiert und unter diesem Pfad lesbar
            ist. Dann lädt er Sie, und testet ob die Klasse nach der er sucht vorhanden ist. Sie
            man sich vorstellen kann, kann das zu vielen Aufrufe auf das Dateisystem führen.
        </para>

        <para>
            Multipliziert mit der anzahl der Komponenten die den PluginLoader verwenden, und man
            bekommt eine Idee von der Reichweite des Problems. Zu der Zeit zu der das geschrieben
            wird, verwenden die folgenden Komponenten den PluginLoader:
        </para>

        <itemizedlist>
            <listitem><para>
                <classname>Zend_Controller_Action_HelperBroker</classname>: Helfer
            </para></listitem>

            <listitem><para>
                <classname>Zend_Dojo</classname>: View Helfer, Form Elemente und Dekorator
            </para></listitem>

            <listitem><para>
                <classname>Zend_File_Transfer</classname>: Adapter
            </para></listitem>

            <listitem><para>
                <classname>Zend_Filter_Inflector</classname>: Filter (verwendet vom ViewRenderer
                Action Helfer und <classname>Zend_Layout</classname>)
            </para></listitem>

            <listitem><para>
                <classname>Zend_Filter_Input</classname>: Filter und Prüfungen
            </para></listitem>

            <listitem><para>
                <classname>Zend_Form</classname>: Elemente, Prüfungen, Filter, Dekoratore, Captcha
                und File Transfer Adapter
            </para></listitem>

            <listitem><para>
                <classname>Zend_Paginator</classname>: Adapter
            </para></listitem>

            <listitem><para>
                <classname>Zend_View</classname>: Helfer, Filter
            </para></listitem>
        </itemizedlist>

        <para>
            Wie kann man die Anzahl der so gemachten Aufrufe reduzieren?
        </para>

        <sect3
            id="performance.classloading.pluginloader.includefilecache">
            <title>Verwenden des PluginLoaders Include-File Caches</title>

            <para>
                Zend Framework 1.7.0 fügt einen Include-File Cache zum PluginLoader hinzu. Diese
                Funktionalität schreibt "<methodname>include_once()</methodname>" Aufrufe in eine
                Datei, welche man dann in der Bootstrap Datei einfügen (include) kann. Wärend das
                einen extra <methodname>include_once()</methodname> Aufruf im Code bedeutet, stellt
                es auch sicher das der PluginLoader so früh wie möglich zurückkehrt.
            </para>

            <para>
                Die PluginLoader Dokumentation
                <link linkend="zend.loader.pluginloader.performance.example"> enthält ein kompettes
                Beispiel seiner Verwendung</link>.
            </para>
        </sect3>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
