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

    <para>
        Wenn man den MVC 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 PHP
        geschrieben sind muß man weder den Overhead eines Kompilierungssystems zu PHP auf sich
        nehmen, noch muß man darauf achten das das kompilierte PHP 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 Zend_View wichtige Flexibilität; statt
            der Notwendigkeit Zend_View 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
            Zend_View 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 role="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 <code>partial()</code> 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 <code>partial()</code> View Helfer akzeptiert drei Argumente:
            </para>

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

                <listitem><para>
                    <code>$module</code>: 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 <code>$model</code> Argument verwendet.
                </para></listitem>

                <listitem><para>
                    <code>$model</code>: 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 <code>partial()</code> kommen vom zweiten und dritten
                Argument. Das <code>$module</code> Argument erlaubt es <code>partial()</code>
                temporär einen Skriptpfad für die angegebenen Module hinzuzufügen damit das
                partielle Viewskript es zu diesem Modul auflöst; das <code>$model</code> 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
                <code>render()</code> 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 MVC Modul darstellt,
                gibt es keinen Grund den Overhead von <code>partial()</code> zu verwenden;
                stattdessen sollte man <classname>Zend_View</classname>'s eingebaute
                <code>render()</code> 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 <code>action()</code> des View Helfers ein, welche es erlaubt
            eine MVC Aktion abzuschicken und deren dargestellten Inhalt aufzufangen. Das biete
            einen wichtigen Schritt in die Prinzipien von DRY, 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 <code>action()</code> 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 <code>action()</code> 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 <code>action()</code> 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
                <code>dispatchLoopStartup()</code> Plugin wie das folgende schreiben um eine Login
                Formularbox bei jeder Seite hinzuzufügen:
            </para>

            <programlisting role="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 <code>UserController::indexAction()</code> Methode könnte dann den
                <code>responseSegment</code> Parameter verwenden um anzuzeigen welches
                Antwortsegment darzustellen ist. Im Layoutskript, würde man dann einfach das
                Antwortsegment darstellen:
            </para>

            <programlisting role="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 <code>action()</code> View Helfer da er
                Objekte nicht klonen und den internen Status resetieren muß. Zustzlich stellt es
                sicher das alle Pre/Post Dispatch Plugins aufgerufen werden, was von spezieller
                Wichtigkeit ist wenn man Frontcontroller Plugins für die Behandlung von ACLs 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 <code>action()</code> 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 role="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 <code>action()</code>, würde man es einfach wie folgt einfügen:
            </para>

            <programlisting role="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 role="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 role="php"><![CDATA[
<?php $this->bugList() ?>
]]></programlisting>

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