| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- EN-Revision: 24249 -->
- <!-- 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 dass 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 welche 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
- * zurückgesetzt 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($this);
- }
- $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 zurücksetzen 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>
|