<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 16183 -->
<!-- Reviewed: no -->
<sect1 id="performance.view">
    <title>Darstellen der View</title>

    <para>
        Wenn man den <acronym>MVC</acronym> Layer vom Zend Framework verwendet, sind die Chancen
        das man <classname>Zend_View</classname> verwendet recht hoch.
        <classname>Zend_View</classname> ist performant verglichen mit anderen View oder Templating
        Engines; da View Skripte in <acronym>PHP</acronym> geschrieben sind muß man weder den
        Overhead eines Kompilierungssystems zu <acronym>PHP</acronym> auf sich nehmen, noch muß man
        darauf achten das das kompilierte <acronym>PHP</acronym> nicht optimiert ist. Trotzdem
        zeigt <classname>Zend_View</classname> seine eigenen Probleme: Erweiterungen werden durch
        überladen (View Helfer) durchgeführt, und eine Anzahl von View Helfern, die dadurch
        Schlüsselfunktionalitäten bieten machen das auch, mit einem Verlust von Geschwindigkeit.
    </para>

    <sect2 id="performance.view.pluginloader">
        <title>Wie kann ich die Auflösung von View Helfern schneller machen?</title>

        <para>
            Die meisten <classname>Zend_View</classname> "Methoden" werden in Wirklichkeit durch
            Überladen des Helfersystems angeboten. Das bietet <classname>Zend_View</classname>
            wichtige Flexibilität; statt der Notwendigkeit <classname>Zend_View</classname> zu
            erweitern und alle Helfermethoden anzubieten die man in der eigenen Anwendung verwenden
            will, kann man eigene Helfermethoden in separaten Klassen definieren und Sie bei Bedarf
            konsumieren wie wenn es direkte Methoden von <classname>Zend_View</classname> wären.
            Das hält das View Objekt selbst relativ dünn, und stellt sicher das Objekte nur
            erstellt werden wenn Sie auch benötigt werden.
        </para>

        <para>
            Intern verwendet <classname>Zend_View</classname> den <link
            linkend="zend.loader.pluginloader">PluginLoader</link> um nach Helferklassen zu sehen.
            Das bedeutet das für jeden Helfer den man aufruft, <classname>Zend_View</classname> den
            Helfernamen zum PluginLoader übergeben muß, welcher dann den Klassennamen erkennen muß
            damit er instanziiert werden kann. Nachfolgende Aufrufe des Helfers sind viel schneller,
            da <classname>Zend_View</classname> eine interne Registry von geladenen Helfern behält,
            aber wenn man viele Helfer verwendet, werden die Aufrufe hinzuaddiert.
        </para>

        <para>
            Die Frage ist also: Wie kann man die Auflösung der Helfer schneller machen?
        </para>

        <sect3 id="performance.view.pluginloader.cache">
            <title>Verwenden des PluginLoader Include-File Caches</title>

            <para>
                Die einfachste, billigste Lösung ist die gleiche für die <link
                linkend="performance.classloading.pluginloader">generelle PluginLoader
                Geschwindigkeit</link>: <link
                linkend="zend.loader.pluginloader.performance.example">Verwenden des PluginLoader
                Include-File Caches</link>. Einzelberichte und aussagen haben gezeigt das diese
                Technik eine Geschwindigkeitssteigerung von 25-30% auf Systemen ohne Opcode Cache
                bringt, und eine 40-64% Steigerung auf Systemen mit Opcode Cache.
            </para>
        </sect3>

        <sect3 id="performance.view.pluginloader.extend">
            <title>Erweitern von Zend_View um oft verwendet Helfermethoden anzubieten</title>

            <para>
                Eine andere Lösung für jene die die Geschwindigkeit sogar noch mehr steigern wollen
                ist es <classname>Zend_View</classname>zu erweitern um manuell die Helfermethoden
                die man am meisten in seiner Anwendung verwendet hinzuzufügen. Solche Helfermethoden
                können einfach manuell die betreffenden Helferklassen instanziiert und auf Sie
                verwesen wird, oder indem die komplette Implementation des Helfers in die Methode
                eingefügt wird.
            </para>

            <programlisting language="php"><![CDATA[
class My_View extends Zend_View
{
    /**
     * @var array Registry der verwendeten Helferklasse
     */
    protected $_localHelperObjects = array();

    /**
     * Proxy zum URL View Helfer
     *
     * @param  array $url  Options Optionen die an die Assemble Methode des
     *                     Route Objekts übergeben werden
     * @param  mixed $name Der Name der zu verwendenden Route. Wenn null wir
     *                     die aktuelle Route verwendet
     * @param  bool $reset Ob die Routenstandard mit den angegebenen resetiert
     *                     werden sollen oder nicht
     * @return string Url für das Link Href Attribut.
     */
    public function url(array $urlOptions = array(), $name = null,
        $reset = false, $encode = true
    ) {
        if (!array_key_exists('url', $this->_localHelperObjects)) {
            $this->_localHelperObjects['url'] = new Zend_View_Helper_Url();
            $this->_localHelperObjects['url']->setView($view);
        }
        $helper = $this->_localHelperObjects['url'];
        return $helper->url($urlOptions, $name, $reset, $encode);
    }

    /**
     * Eine Meldung ausgeben
     *
     * Direkte Implementierung.
     *
     * @param  string $string
     * @return string
     */
    public function message($string)
    {
        return "<h1>" . $this->escape($message) . "</h1>\n";
    }
}
]]></programlisting>

            <para>
                Wie auch immer, diese Techik reduziert den Overhead des Helfersystems substanziell
                indem es den Aufruf vom PluginLoader komplett vermeidet, und entweder vom
                Autoloader profitiert, oder durch dessen Überbrückung.
            </para>
        </sect3>
    </sect2>

    <sect2 id="performance.view.partial">
        <title>Wie kann ich partielle View schneller machen?</title>

        <para>
            Jene die Partielle sehr oft verwenden und die Ihre Anwendungen profilieren werden oft
            sofort feststellen das der <methodname>partial()</methodname> View Helfer viel des
            Overheads verwendet, durch die Notwendigkeit das View Objekt zu klonen. Ist es möglich
            das schneller zu machen?
        </para>

        <sect3 id="performance.view.partial.render">
            <title>Verwende partial() nur wenn es wirklich notwendig ist</title>

            <para>
                Der <methodname>partial()</methodname> View Helfer akzeptiert drei Argumente:
            </para>

            <itemizedlist>
                <listitem><para>
                    <varname>$name</varname>: Den Namen des View Skripts das dargestellt werden
                    soll
                </para></listitem>

                <listitem><para>
                    <varname>$module</varname>: Der Name des Moduls in dem das View Skript sitzt;
                    oder, wenn kein drittes Argument angegeben wurde und es ein Array oder Objekt
                    ist, wird es als <varname>$model</varname> Argument verwendet.
                </para></listitem>

                <listitem><para>
                    <varname>$model</varname>: Ein Array oder Objekt das dem Partial übergeben
                    wird, und die reinen Daten repräsentiert die der View übergeben werden.
                </para></listitem>
            </itemizedlist>

            <para>
                Die Power der Verwendung von <methodname>partial()</methodname> kommen vom zweiten
                und dritten Argument. Das <varname>$module</varname> Argument erlaubt es
                <methodname>partial()</methodname> temporär einen Skriptpfad für die angegebenen
                Module hinzuzufügen damit das partielle Viewskript es zu diesem Modul auflöst; das
                <varname>$model</varname> Argument erlaubt es Variablen explizit für die Verwendung
                in der partiellen View zu übergeben. Wenn man keines der Argumente übergibt, kann
                man <emphasis>stattdessen <methodname>render()</methodname> verwenden</emphasis>!
            </para>

            <para>
                Grundsätzlich, solange man nicht Variablen an das Partial übergibt und einen reinen
                Variablenraum benötigt, oder ein Viewskript von einem anderen
                <acronym>MVC</acronym> Modul darstellt, gibt es keinen Grund den Overhead von
                <methodname>partial()</methodname> zu verwenden; stattdessen sollte man
                <classname>Zend_View</classname>'s eingebaute <methodname>render()</methodname>
                Methode verwendet um das Viewskript darzustellen.
            </para>
        </sect3>
    </sect2>

    <sect2 id="performance.view.action">
        <title>Wie kann ich Aufrufe zu action() vom View Helfers schneller machen?</title>

        <para>
            Version 1.5.0 führte die <methodname>action()</methodname> des View Helfers ein, welche
            es erlaubt eine <acronym>MVC</acronym> Aktion abzuschicken und deren dargestellten
            Inhalt aufzufangen. Das biete einen wichtigen Schritt in die Prinzipien von
            <acronym>DRY</acronym>, und erlaubt die Wiederverwendung von Code. Trotzdem, wie jede
            die Ihre Anwendung profilieren schnell feststellen werden, ist es auch eine sehr teure
            Operation. Intern muß der <methodname>action()</methodname> Viewhelfer neue Anfrage und
            Antwort Objekte klonen, den Dispatcher aufrufen, den angefragten Controller und die
            Aktion aufrufen, usw.
        </para>

        <para>
            Wie kann man das schneller machen?
        </para>

        <sect3 id="performance.view.action.actionstack">
            <title>Verwende den ActionStack wenn möglich</title>

            <para>
                Zur selben Zeit die der <methodname>action()</methodname> View Helfer eingeführt,
                besteht der
                <link linkend="zend.controller.actionhelpers.actionstack">ActionStack</link> auf
                einem Action Helfer und einem Front Controller Plugin. Zusammen erlauben Sie es
                zusätzliche Aktionen einzufügen die wärend des Dispatch Zyklus auf den Stack
                aufgerufen werden. Wenn man <methodname>action()</methodname> von eigenen Layout
                View Skripts aufruft, kann es sein das man stattdessen den ActionStack verwenden
                will, und die Views zu diskreten Antwortsegmenten darstellen will. Als Beispiel
                könnte man ein <methodname>dispatchLoopStartup()</methodname> Plugin wie das
                folgende schreiben um eine Login Formularbox bei jeder Seite hinzuzufügen:
            </para>

            <programlisting language="php"><![CDATA[
class LoginPlugin extends Zend_Controller_Plugin_Abstract
{
    protected $_stack;

    public function dispatchLoopStartup(
        Zend_Controller_Request_Abstract $request
    ) {
        $stack = $this->getStack();
        $loginRequest = new Zend_Controller_Request_Simple();
        $loginRequest->setControllerName('user')
                     ->setActionName('index')
                     ->setParam('responseSegment', 'login');
        $stack->pushStack($loginRequest);
    }

    public function getStack()
    {
        if (null === $this->_stack) {
            $front = Zend_Controller_Front::getInstance();
            if (!$front->hasPlugin('Zend_Controller_Plugin_ActionStack')) {
                $stack = new Zend_Controller_Plugin_ActionStack();
                $front->registerPlugin($stack);
            } else {
                $stack = $front->getPlugin('ActionStack')
            }
            $this->_stack = $stack;
        }
        return $this->_stack;
    }
}
]]></programlisting>

            <para>
                Die <methodname>UserController::indexAction()</methodname> Methode könnte dann den
                <varname>$responseSegment</varname> Parameter verwenden um anzuzeigen welches
                Antwortsegment darzustellen ist. Im Layoutskript, würde man dann einfach das
                Antwortsegment darstellen:
            </para>

            <programlisting language="php"><![CDATA[
<?php $this->layout()->login ?>
]]></programlisting>

            <para>
                Wärend der ActionStack trotzdem noch einen Dispatch Zyklus benötigt, ist das
                trotzdem immer noch billiger als der <methodname>action()</methodname> View Helfer
                da er Objekte nicht klonen und den internen Status resetieren muß. Zustzlich stellt
                es sicher das alle Pre und Post Dispatch Plugins aufgerufen werden, was von
                spezieller Wichtigkeit ist wenn man Frontcontroller Plugins für die Behandlung von
                <acronym>ACL</acronym>'s in speziellen Aktionen verwendet.
            </para>
        </sect3>

        <sect3 id="performance.view.action.model">
            <title>Helfer bevorzugen die das Modell vor action() abfragen</title>

            <para>
                In den meisten Fällen ist die Verwendung von <methodname>action()</methodname> einfach nur
                Overkill. Wenn man die meiste Businesslogik in eigenen Modellen verschachtelt hat
                das Modell einfach abfragt und die Ergebnisse an das View Skript übergibt, ist es
                typischerweise schneller und sauberer einfach einen View Helfer zu schreiben der
                das Modell holt, es abfragt und mit der Information irgendwas macht.
            </para>

            <para>
                Nehmen wir als Beispiel die folgende Controller Action und das View Skript an:
            </para>

            <programlisting language="php"><![CDATA[
class BugController extends Zend_Controller_Action
{
    public function listAction()
    {
        $model = new Bug();
        $this->view->bugs = $model->fetchActive();
    }
}

// bug/list.phtml:
echo "<ul>\n";
foreach ($this->bugs as $bug) {
    printf("<li><b>%s</b>: %s</li>\n",
           $this->escape($bug->id),
           $this->escape($bug->summary));
}
echo "</ul>\n";
]]></programlisting>

            <para>
                Mit Verwendung von <methodname>action()</methodname>, würde man es einfach wie
                folgt einfügen:
            </para>

            <programlisting language="php"><![CDATA[
<?php $this->action('list', 'bug') ?>
]]></programlisting>

            <para>
                Das könnte zu einem View helfer geändert werden die wie folgt aussieht:
            </para>

            <programlisting language="php"><![CDATA[
class My_View_Helper_BugList extends Zend_View_Helper_Abstract
{
    public function bugList()
    {
        $model = new Bug();
        $html  = "<ul>\n";
        foreach ($model->fetchActive() as $bug) {
            $html .= sprintf("<li><b>%s</b>: %s</li>\n",
                $this->view->escape($bug->id),
                $this->view->escape($bug->summary)
            );
        }
        $html .= "</ul>\n";
        return $html;
    }
}
]]></programlisting>

            <para>
                Der Helfer würde dann wie folgt aufgerufen werden:
            </para>

            <programlisting language="php"><![CDATA[
<?php $this->bugList() ?>
]]></programlisting>

            <para>
                Das hat zwei Vorteile: Es übernimmt nicht länger den Overhead vom
                <methodname>action()</methodname> View Helfer, und präsentiert eine bessere und
                semantisch verständlichere <acronym>API</acronym>.
            </para>
        </sect3>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
