performance-view.xml 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 16183 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="performance.view">
  5. <title>Darstellen der View</title>
  6. <para>
  7. Wenn man den <acronym>MVC</acronym> Layer vom Zend Framework verwendet, sind die Chancen
  8. das man <classname>Zend_View</classname> verwendet recht hoch.
  9. <classname>Zend_View</classname> ist performant verglichen mit anderen View oder Templating
  10. Engines; da View Skripte in <acronym>PHP</acronym> geschrieben sind muß man weder den
  11. Overhead eines Kompilierungssystems zu <acronym>PHP</acronym> auf sich nehmen, noch muß man
  12. darauf achten das das kompilierte <acronym>PHP</acronym> nicht optimiert ist. Trotzdem
  13. zeigt <classname>Zend_View</classname> seine eigenen Probleme: Erweiterungen werden durch
  14. überladen (View Helfer) durchgeführt, und eine Anzahl von View Helfern, die dadurch
  15. Schlüsselfunktionalitäten bieten machen das auch, mit einem Verlust von Geschwindigkeit.
  16. </para>
  17. <sect2 id="performance.view.pluginloader">
  18. <title>Wie kann ich die Auflösung von View Helfern schneller machen?</title>
  19. <para>
  20. Die meisten <classname>Zend_View</classname> "Methoden" werden in Wirklichkeit durch
  21. Überladen des Helfersystems angeboten. Das bietet <classname>Zend_View</classname>
  22. wichtige Flexibilität; statt der Notwendigkeit <classname>Zend_View</classname> zu
  23. erweitern und alle Helfermethoden anzubieten die man in der eigenen Anwendung verwenden
  24. will, kann man eigene Helfermethoden in separaten Klassen definieren und Sie bei Bedarf
  25. konsumieren wie wenn es direkte Methoden von <classname>Zend_View</classname> wären.
  26. Das hält das View Objekt selbst relativ dünn, und stellt sicher das Objekte nur
  27. erstellt werden wenn Sie auch benötigt werden.
  28. </para>
  29. <para>
  30. Intern verwendet <classname>Zend_View</classname> den <link
  31. linkend="zend.loader.pluginloader">PluginLoader</link> um nach Helferklassen zu sehen.
  32. Das bedeutet das für jeden Helfer den man aufruft, <classname>Zend_View</classname> den
  33. Helfernamen zum PluginLoader übergeben muß, welcher dann den Klassennamen erkennen muß
  34. damit er instanziiert werden kann. Nachfolgende Aufrufe des Helfers sind viel schneller,
  35. da <classname>Zend_View</classname> eine interne Registry von geladenen Helfern behält,
  36. aber wenn man viele Helfer verwendet, werden die Aufrufe hinzuaddiert.
  37. </para>
  38. <para>
  39. Die Frage ist also: Wie kann man die Auflösung der Helfer schneller machen?
  40. </para>
  41. <sect3 id="performance.view.pluginloader.cache">
  42. <title>Verwenden des PluginLoader Include-File Caches</title>
  43. <para>
  44. Die einfachste, billigste Lösung ist die gleiche für die <link
  45. linkend="performance.classloading.pluginloader">generelle PluginLoader
  46. Geschwindigkeit</link>: <link
  47. linkend="zend.loader.pluginloader.performance.example">Verwenden des PluginLoader
  48. Include-File Caches</link>. Einzelberichte und aussagen haben gezeigt das diese
  49. Technik eine Geschwindigkeitssteigerung von 25-30% auf Systemen ohne Opcode Cache
  50. bringt, und eine 40-64% Steigerung auf Systemen mit Opcode Cache.
  51. </para>
  52. </sect3>
  53. <sect3 id="performance.view.pluginloader.extend">
  54. <title>Erweitern von Zend_View um oft verwendet Helfermethoden anzubieten</title>
  55. <para>
  56. Eine andere Lösung für jene welche die Geschwindigkeit sogar noch mehr steigern
  57. wollen ist es <classname>Zend_View</classname>zu erweitern um manuell die
  58. Helfermethoden die man am meisten in seiner Anwendung verwendet hinzuzufügen. Solche
  59. Helfermethoden können einfach manuell die betreffenden Helferklassen instanziiert
  60. und auf Sie verwesen wird, oder indem die komplette Implementation des Helfers in
  61. die Methode eingefügt wird.
  62. </para>
  63. <programlisting language="php"><![CDATA[
  64. class My_View extends Zend_View
  65. {
  66. /**
  67. * @var array Registry der verwendeten Helferklasse
  68. */
  69. protected $_localHelperObjects = array();
  70. /**
  71. * Proxy zum URL View Helfer
  72. *
  73. * @param array $url Options Optionen die an die Assemble Methode des
  74. * Route Objekts übergeben werden
  75. * @param mixed $name Der Name der zu verwendenden Route. Wenn null wir
  76. * die aktuelle Route verwendet
  77. * @param bool $reset Ob die Routenstandard mit den angegebenen resetiert
  78. * werden sollen oder nicht
  79. * @return string Url für das Link Href Attribut.
  80. */
  81. public function url(array $urlOptions = array(), $name = null,
  82. $reset = false, $encode = true
  83. ) {
  84. if (!array_key_exists('url', $this->_localHelperObjects)) {
  85. $this->_localHelperObjects['url'] = new Zend_View_Helper_Url();
  86. $this->_localHelperObjects['url']->setView($view);
  87. }
  88. $helper = $this->_localHelperObjects['url'];
  89. return $helper->url($urlOptions, $name, $reset, $encode);
  90. }
  91. /**
  92. * Eine Meldung ausgeben
  93. *
  94. * Direkte Implementierung.
  95. *
  96. * @param string $string
  97. * @return string
  98. */
  99. public function message($string)
  100. {
  101. return "<h1>" . $this->escape($message) . "</h1>\n";
  102. }
  103. }
  104. ]]></programlisting>
  105. <para>
  106. Wie auch immer, diese Techik reduziert den Overhead des Helfersystems substanziell
  107. indem es den Aufruf vom PluginLoader komplett vermeidet, und entweder vom
  108. Autoloader profitiert, oder durch dessen Überbrückung.
  109. </para>
  110. </sect3>
  111. </sect2>
  112. <sect2 id="performance.view.partial">
  113. <title>Wie kann ich partielle View schneller machen?</title>
  114. <para>
  115. Jene die Partielle sehr oft verwenden und die Ihre Anwendungen profilieren werden oft
  116. sofort feststellen das der <methodname>partial()</methodname> View Helfer viel des
  117. Overheads verwendet, durch die Notwendigkeit das View Objekt zu klonen. Ist es möglich
  118. das schneller zu machen?
  119. </para>
  120. <sect3 id="performance.view.partial.render">
  121. <title>Verwende partial() nur wenn es wirklich notwendig ist</title>
  122. <para>
  123. Der <methodname>partial()</methodname> View Helfer akzeptiert drei Argumente:
  124. </para>
  125. <itemizedlist>
  126. <listitem><para>
  127. <varname>$name</varname>: Den Namen des View Skripts das dargestellt werden
  128. soll
  129. </para></listitem>
  130. <listitem><para>
  131. <varname>$module</varname>: Der Name des Moduls in dem das View Skript sitzt;
  132. oder, wenn kein drittes Argument angegeben wurde und es ein Array oder Objekt
  133. ist, wird es als <varname>$model</varname> Argument verwendet.
  134. </para></listitem>
  135. <listitem><para>
  136. <varname>$model</varname>: Ein Array oder Objekt das dem Partial übergeben
  137. wird, und die reinen Daten repräsentiert die der View übergeben werden.
  138. </para></listitem>
  139. </itemizedlist>
  140. <para>
  141. Die Power der Verwendung von <methodname>partial()</methodname> kommen vom zweiten
  142. und dritten Argument. Das <varname>$module</varname> Argument erlaubt es
  143. <methodname>partial()</methodname> temporär einen Skriptpfad für die angegebenen
  144. Module hinzuzufügen damit das partielle Viewskript es zu diesem Modul auflöst; das
  145. <varname>$model</varname> Argument erlaubt es Variablen explizit für die Verwendung
  146. in der partiellen View zu übergeben. Wenn man keines der Argumente übergibt, kann
  147. man <emphasis>stattdessen <methodname>render()</methodname> verwenden</emphasis>!
  148. </para>
  149. <para>
  150. Grundsätzlich, solange man nicht Variablen an das Partial übergibt und einen reinen
  151. Variablenraum benötigt, oder ein Viewskript von einem anderen
  152. <acronym>MVC</acronym> Modul darstellt, gibt es keinen Grund den Overhead von
  153. <methodname>partial()</methodname> zu verwenden; stattdessen sollte man
  154. <classname>Zend_View</classname>'s eingebaute <methodname>render()</methodname>
  155. Methode verwendet um das Viewskript darzustellen.
  156. </para>
  157. </sect3>
  158. </sect2>
  159. <sect2 id="performance.view.action">
  160. <title>Wie kann ich Aufrufe zu action() vom View Helfers schneller machen?</title>
  161. <para>
  162. Version 1.5.0 führte die <methodname>action()</methodname> des View Helfers ein, welche
  163. es erlaubt eine <acronym>MVC</acronym> Aktion abzuschicken und deren dargestellten
  164. Inhalt aufzufangen. Das biete einen wichtigen Schritt in die Prinzipien von
  165. <acronym>DRY</acronym>, und erlaubt die Wiederverwendung von Code. Trotzdem, wie jede
  166. die Ihre Anwendung profilieren schnell feststellen werden, ist es auch eine sehr teure
  167. Operation. Intern muß der <methodname>action()</methodname> Viewhelfer neue Anfrage und
  168. Antwort Objekte klonen, den Dispatcher aufrufen, den angefragten Controller und die
  169. Aktion aufrufen, usw.
  170. </para>
  171. <para>
  172. Wie kann man das schneller machen?
  173. </para>
  174. <sect3 id="performance.view.action.actionstack">
  175. <title>Verwende den ActionStack wenn möglich</title>
  176. <para>
  177. Zur selben Zeit die der <methodname>action()</methodname> View Helfer eingeführt,
  178. besteht der
  179. <link linkend="zend.controller.actionhelpers.actionstack">ActionStack</link> auf
  180. einem Action Helfer und einem Front Controller Plugin. Zusammen erlauben Sie es
  181. zusätzliche Aktionen einzufügen die wärend des Dispatch Zyklus auf den Stack
  182. aufgerufen werden. Wenn man <methodname>action()</methodname> von eigenen Layout
  183. View Skripts aufruft, kann es sein das man stattdessen den ActionStack verwenden
  184. will, und die Views zu diskreten Antwortsegmenten darstellen will. Als Beispiel
  185. könnte man ein <methodname>dispatchLoopStartup()</methodname> Plugin wie das
  186. folgende schreiben um eine Login Formularbox bei jeder Seite hinzuzufügen:
  187. </para>
  188. <programlisting language="php"><![CDATA[
  189. class LoginPlugin extends Zend_Controller_Plugin_Abstract
  190. {
  191. protected $_stack;
  192. public function dispatchLoopStartup(
  193. Zend_Controller_Request_Abstract $request
  194. ) {
  195. $stack = $this->getStack();
  196. $loginRequest = new Zend_Controller_Request_Simple();
  197. $loginRequest->setControllerName('user')
  198. ->setActionName('index')
  199. ->setParam('responseSegment', 'login');
  200. $stack->pushStack($loginRequest);
  201. }
  202. public function getStack()
  203. {
  204. if (null === $this->_stack) {
  205. $front = Zend_Controller_Front::getInstance();
  206. if (!$front->hasPlugin('Zend_Controller_Plugin_ActionStack')) {
  207. $stack = new Zend_Controller_Plugin_ActionStack();
  208. $front->registerPlugin($stack);
  209. } else {
  210. $stack = $front->getPlugin('ActionStack')
  211. }
  212. $this->_stack = $stack;
  213. }
  214. return $this->_stack;
  215. }
  216. }
  217. ]]></programlisting>
  218. <para>
  219. Die <methodname>UserController::indexAction()</methodname> Methode könnte dann den
  220. <varname>$responseSegment</varname> Parameter verwenden um anzuzeigen welches
  221. Antwortsegment darzustellen ist. Im Layoutskript, würde man dann einfach das
  222. Antwortsegment darstellen:
  223. </para>
  224. <programlisting language="php"><![CDATA[
  225. <?php $this->layout()->login ?>
  226. ]]></programlisting>
  227. <para>
  228. Wärend der ActionStack trotzdem noch einen Dispatch Zyklus benötigt, ist das
  229. trotzdem immer noch billiger als der <methodname>action()</methodname> View Helfer
  230. da er Objekte nicht klonen und den internen Status resetieren muß. Zustzlich stellt
  231. es sicher das alle Pre und Post Dispatch Plugins aufgerufen werden, was von
  232. spezieller Wichtigkeit ist wenn man Frontcontroller Plugins für die Behandlung von
  233. <acronym>ACL</acronym>'s in speziellen Aktionen verwendet.
  234. </para>
  235. </sect3>
  236. <sect3 id="performance.view.action.model">
  237. <title>Helfer bevorzugen die das Modell vor action() abfragen</title>
  238. <para>
  239. In den meisten Fällen ist die Verwendung von <methodname>action()</methodname> einfach nur
  240. Overkill. Wenn man die meiste Businesslogik in eigenen Modellen verschachtelt hat
  241. das Modell einfach abfragt und die Ergebnisse an das View Skript übergibt, ist es
  242. typischerweise schneller und sauberer einfach einen View Helfer zu schreiben der
  243. das Modell holt, es abfragt und mit der Information irgendwas macht.
  244. </para>
  245. <para>
  246. Nehmen wir als Beispiel die folgende Controller Action und das View Skript an:
  247. </para>
  248. <programlisting language="php"><![CDATA[
  249. class BugController extends Zend_Controller_Action
  250. {
  251. public function listAction()
  252. {
  253. $model = new Bug();
  254. $this->view->bugs = $model->fetchActive();
  255. }
  256. }
  257. // bug/list.phtml:
  258. echo "<ul>\n";
  259. foreach ($this->bugs as $bug) {
  260. printf("<li><b>%s</b>: %s</li>\n",
  261. $this->escape($bug->id),
  262. $this->escape($bug->summary));
  263. }
  264. echo "</ul>\n";
  265. ]]></programlisting>
  266. <para>
  267. Mit Verwendung von <methodname>action()</methodname>, würde man es einfach wie
  268. folgt einfügen:
  269. </para>
  270. <programlisting language="php"><![CDATA[
  271. <?php $this->action('list', 'bug') ?>
  272. ]]></programlisting>
  273. <para>
  274. Das könnte zu einem View helfer geändert werden die wie folgt aussieht:
  275. </para>
  276. <programlisting language="php"><![CDATA[
  277. class My_View_Helper_BugList extends Zend_View_Helper_Abstract
  278. {
  279. public function bugList()
  280. {
  281. $model = new Bug();
  282. $html = "<ul>\n";
  283. foreach ($model->fetchActive() as $bug) {
  284. $html .= sprintf("<li><b>%s</b>: %s</li>\n",
  285. $this->view->escape($bug->id),
  286. $this->view->escape($bug->summary)
  287. );
  288. }
  289. $html .= "</ul>\n";
  290. return $html;
  291. }
  292. }
  293. ]]></programlisting>
  294. <para>
  295. Der Helfer würde dann wie folgt aufgerufen werden:
  296. </para>
  297. <programlisting language="php"><![CDATA[
  298. <?php $this->bugList() ?>
  299. ]]></programlisting>
  300. <para>
  301. Das hat zwei Vorteile: Es übernimmt nicht länger den Overhead vom
  302. <methodname>action()</methodname> View Helfer, und präsentiert eine bessere und
  303. semantisch verständlichere <acronym>API</acronym>.
  304. </para>
  305. </sect3>
  306. </sect2>
  307. </sect1>
  308. <!--
  309. vim:se ts=4 sw=4 et:
  310. -->