performance-view.xml 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 24249 -->
  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 dass 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
  78. * zurückgesetzt 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($this);
  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>
  127. <para>
  128. <varname>$name</varname>: Den Namen des View Skripts das dargestellt werden
  129. soll
  130. </para>
  131. </listitem>
  132. <listitem>
  133. <para>
  134. <varname>$module</varname>: Der Name des Moduls in dem das View Skript
  135. sitzt; oder, wenn kein drittes Argument angegeben wurde und es ein Array
  136. oder Objekt ist, wird es als <varname>$model</varname> Argument verwendet.
  137. </para>
  138. </listitem>
  139. <listitem>
  140. <para>
  141. <varname>$model</varname>: Ein Array oder Objekt das dem Partial übergeben
  142. wird, und die reinen Daten repräsentiert die der View übergeben werden.
  143. </para>
  144. </listitem>
  145. </itemizedlist>
  146. <para>
  147. Die Power der Verwendung von <methodname>partial()</methodname> kommen vom zweiten
  148. und dritten Argument. Das <varname>$module</varname> Argument erlaubt es
  149. <methodname>partial()</methodname> temporär einen Skriptpfad für die angegebenen
  150. Module hinzuzufügen damit das partielle Viewskript es zu diesem Modul auflöst; das
  151. <varname>$model</varname> Argument erlaubt es Variablen explizit für die Verwendung
  152. in der partiellen View zu übergeben. Wenn man keines der Argumente übergibt, kann
  153. man <emphasis>stattdessen <methodname>render()</methodname> verwenden</emphasis>!
  154. </para>
  155. <para>
  156. Grundsätzlich, solange man nicht Variablen an das Partial übergibt und einen reinen
  157. Variablenraum benötigt, oder ein Viewskript von einem anderen
  158. <acronym>MVC</acronym> Modul darstellt, gibt es keinen Grund den Overhead von
  159. <methodname>partial()</methodname> zu verwenden; stattdessen sollte man
  160. <classname>Zend_View</classname>'s eingebaute <methodname>render()</methodname>
  161. Methode verwendet um das Viewskript darzustellen.
  162. </para>
  163. </sect3>
  164. </sect2>
  165. <sect2 id="performance.view.action">
  166. <title>Wie kann ich Aufrufe zu action() vom View Helfers schneller machen?</title>
  167. <para>
  168. Version 1.5.0 führte die <methodname>action()</methodname> des View Helfers ein, welche
  169. es erlaubt eine <acronym>MVC</acronym> Aktion abzuschicken und deren dargestellten
  170. Inhalt aufzufangen. Das biete einen wichtigen Schritt in die Prinzipien von
  171. <acronym>DRY</acronym>, und erlaubt die Wiederverwendung von Code. Trotzdem, wie jede
  172. die Ihre Anwendung profilieren schnell feststellen werden, ist es auch eine sehr teure
  173. Operation. Intern muß der <methodname>action()</methodname> Viewhelfer neue Anfrage und
  174. Antwort Objekte klonen, den Dispatcher aufrufen, den angefragten Controller und die
  175. Aktion aufrufen, usw.
  176. </para>
  177. <para>
  178. Wie kann man das schneller machen?
  179. </para>
  180. <sect3 id="performance.view.action.actionstack">
  181. <title>Verwende den ActionStack wenn möglich</title>
  182. <para>
  183. Zur selben Zeit die der <methodname>action()</methodname> View Helfer eingeführt,
  184. besteht der
  185. <link linkend="zend.controller.actionhelpers.actionstack">ActionStack</link> auf
  186. einem Action Helfer und einem Front Controller Plugin. Zusammen erlauben Sie es
  187. zusätzliche Aktionen einzufügen die wärend des Dispatch Zyklus auf den Stack
  188. aufgerufen werden. Wenn man <methodname>action()</methodname> von eigenen Layout
  189. View Skripts aufruft, kann es sein das man stattdessen den ActionStack verwenden
  190. will, und die Views zu diskreten Antwortsegmenten darstellen will. Als Beispiel
  191. könnte man ein <methodname>dispatchLoopStartup()</methodname> Plugin wie das
  192. folgende schreiben um eine Login Formularbox bei jeder Seite hinzuzufügen:
  193. </para>
  194. <programlisting language="php"><![CDATA[
  195. class LoginPlugin extends Zend_Controller_Plugin_Abstract
  196. {
  197. protected $_stack;
  198. public function dispatchLoopStartup(
  199. Zend_Controller_Request_Abstract $request
  200. ) {
  201. $stack = $this->getStack();
  202. $loginRequest = new Zend_Controller_Request_Simple();
  203. $loginRequest->setControllerName('user')
  204. ->setActionName('index')
  205. ->setParam('responseSegment', 'login');
  206. $stack->pushStack($loginRequest);
  207. }
  208. public function getStack()
  209. {
  210. if (null === $this->_stack) {
  211. $front = Zend_Controller_Front::getInstance();
  212. if (!$front->hasPlugin('Zend_Controller_Plugin_ActionStack')) {
  213. $stack = new Zend_Controller_Plugin_ActionStack();
  214. $front->registerPlugin($stack);
  215. } else {
  216. $stack = $front->getPlugin('ActionStack')
  217. }
  218. $this->_stack = $stack;
  219. }
  220. return $this->_stack;
  221. }
  222. }
  223. ]]></programlisting>
  224. <para>
  225. Die <methodname>UserController::indexAction()</methodname> Methode könnte dann den
  226. <varname>$responseSegment</varname> Parameter verwenden um anzuzeigen welches
  227. Antwortsegment darzustellen ist. Im Layoutskript, würde man dann einfach das
  228. Antwortsegment darstellen:
  229. </para>
  230. <programlisting language="php"><![CDATA[
  231. <?php $this->layout()->login ?>
  232. ]]></programlisting>
  233. <para>
  234. Wärend der ActionStack trotzdem noch einen Dispatch Zyklus benötigt, ist das
  235. trotzdem immer noch billiger als der <methodname>action()</methodname> View Helfer
  236. da er Objekte nicht klonen und den internen Status zurücksetzen muß. Zustzlich
  237. stellt es sicher das alle Pre und Post Dispatch Plugins aufgerufen werden, was von
  238. spezieller Wichtigkeit ist wenn man Frontcontroller Plugins für die Behandlung von
  239. <acronym>ACL</acronym>'s in speziellen Aktionen verwendet.
  240. </para>
  241. </sect3>
  242. <sect3 id="performance.view.action.model">
  243. <title>Helfer bevorzugen die das Modell vor action() abfragen</title>
  244. <para>
  245. In den meisten Fällen ist die Verwendung von <methodname>action()</methodname>
  246. einfach nur Overkill. Wenn man die meiste Businesslogik in eigenen Modellen
  247. verschachtelt hat das Modell einfach abfragt und die Ergebnisse an das View Skript
  248. übergibt, ist es typischerweise schneller und sauberer einfach einen View Helfer zu
  249. schreiben der das Modell holt, es abfragt und mit der Information irgendwas macht.
  250. </para>
  251. <para>
  252. Nehmen wir als Beispiel die folgende Controller Action und das View Skript an:
  253. </para>
  254. <programlisting language="php"><![CDATA[
  255. class BugController extends Zend_Controller_Action
  256. {
  257. public function listAction()
  258. {
  259. $model = new Bug();
  260. $this->view->bugs = $model->fetchActive();
  261. }
  262. }
  263. // bug/list.phtml:
  264. echo "<ul>\n";
  265. foreach ($this->bugs as $bug) {
  266. printf("<li><b>%s</b>: %s</li>\n",
  267. $this->escape($bug->id),
  268. $this->escape($bug->summary));
  269. }
  270. echo "</ul>\n";
  271. ]]></programlisting>
  272. <para>
  273. Mit Verwendung von <methodname>action()</methodname>, würde man es einfach wie
  274. folgt einfügen:
  275. </para>
  276. <programlisting language="php"><![CDATA[
  277. <?php $this->action('list', 'bug') ?>
  278. ]]></programlisting>
  279. <para>
  280. Das könnte zu einem View helfer geändert werden die wie folgt aussieht:
  281. </para>
  282. <programlisting language="php"><![CDATA[
  283. class My_View_Helper_BugList extends Zend_View_Helper_Abstract
  284. {
  285. public function bugList()
  286. {
  287. $model = new Bug();
  288. $html = "<ul>\n";
  289. foreach ($model->fetchActive() as $bug) {
  290. $html .= sprintf("<li><b>%s</b>: %s</li>\n",
  291. $this->view->escape($bug->id),
  292. $this->view->escape($bug->summary)
  293. );
  294. }
  295. $html .= "</ul>\n";
  296. return $html;
  297. }
  298. }
  299. ]]></programlisting>
  300. <para>
  301. Der Helfer würde dann wie folgt aufgerufen werden:
  302. </para>
  303. <programlisting language="php"><![CDATA[
  304. <?php $this->bugList() ?>
  305. ]]></programlisting>
  306. <para>
  307. Das hat zwei Vorteile: Es übernimmt nicht länger den Overhead vom
  308. <methodname>action()</methodname> View Helfer, und präsentiert eine bessere und
  309. semantisch verständlichere <acronym>API</acronym>.
  310. </para>
  311. </sect3>
  312. </sect2>
  313. </sect1>