2
0

performance-view.xml 15 KB

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