performance-view.xml 15 KB

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