| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- <?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:
- -->
|