performance-view.xml 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- Reviewed: no -->
  3. <!-- EN-Revision: 15103 -->
  4. <sect1 id="performance.view">
  5. <title>ビューのレンダリング</title>
  6. <para>
  7. Zend FrameworkのMVCレイヤを使うときには、
  8. <classname>Zend_View</classname>を使うようになる機会があります。
  9. <classname>Zend_View</classname>は、
  10. 他のビューやテンプレートエンジンに比べて良く機能します。
  11. ビュースクリプトはPHPで記述されているので、
  12. PHPにカスタムで加えたコンパイルのオーバーヘッドを招きませんし、
  13. コンパイルされたPHPが最適されていないかどうか心配する必要もありません。
  14. しかしながら、<classname>Zend_View</classname>には特有の問題があります:
  15. エクステンションはオーバーロード経由で実行されます(ビューヘルパ)。
  16. ビューヘルパの多くは重要な機能を担っていますが、
  17. 性能面でのコストもあります。
  18. </para>
  19. <sect2 id="performance.view.pluginloader">
  20. <title>どのようにしたらビューヘルパの解決を速く出来ますか?</title>
  21. <para>
  22. ほとんどの<classname>Zend_View</classname> "メソッド"は、
  23. 実際ヘルパ方式でオーバーロード経由で提供されています。
  24. これによりZend_Viewに重要な柔軟性が与えられています;
  25. Zend_Viewを拡張してアプリケーションで利用するであろうすべてのヘルパメソッドを提供する必要の代わりに、
  26. 分離されたクラスにヘルパメソッドを定義して、
  27. まるでZend_Viewそのもののメソッドであるかのように使い切ることができます。
  28. このことによりビューオブジェクト自身は比較的身軽に保たれ、
  29. オブジェクトが必要なときだけ生成されることが保証されます。
  30. </para>
  31. <para>
  32. Internally, <classname>Zend_View</classname> uses the <link
  33. linkend="zend.loader.pluginloader">PluginLoader</link> to look
  34. up helper classes. This means that for each helper you call,
  35. <classname>Zend_View</classname> needs to pass the helper name to the
  36. PluginLoader, which then needs to determine the class name, load the
  37. class file if necessary, and then return the class name so it may be
  38. instantiated. Subsequent uses of the helper are much faster, as
  39. <classname>Zend_View</classname> keeps an internal registry of loaded helpers,
  40. but if you use many helpers, the calls add up.
  41. </para>
  42. <para>
  43. それでは質問です: どのようにしたらヘルパの解決を速く出来ますか?
  44. </para>
  45. <sect3 id="performance.view.pluginloader.cache">
  46. <title>ファイルキャッシュを含むPluginLoaderを使う</title>
  47. <para>
  48. The simplest, cheapest solution is the same as for <link
  49. linkend="performance.classloading.pluginloader">general
  50. PluginLoader performance</link>: <link
  51. linkend="zend.loader.pluginloader.performance.example">use
  52. the PluginLoader include file cache</link>. Anecdotal
  53. evidence has shown this technique to provide a 25-30%
  54. performance gain on systems without an opcode cache, and a
  55. 40-65% gain on systems with an opcode cache.
  56. </para>
  57. </sect3>
  58. <sect3 id="performance.view.pluginloader.extend">
  59. <title>よく使われるヘルパメソッドを提供するようにZend_Viewを拡張する</title>
  60. <para>
  61. Another solution for those seeking to tune performance even
  62. further is to extend <classname>Zend_View</classname> to manually add the
  63. helper methods they most use in their application. Such helper
  64. methods may simply manually instantiate the appropriate helper
  65. class and proxy to it, or stuff the full helper implementation
  66. into the method.
  67. </para>
  68. <programlisting role="php"><![CDATA[
  69. class My_View extends Zend_View
  70. {
  71. /**
  72. * @var array Registry of helper classes used
  73. */
  74. protected $_localHelperObjects = array();
  75. /**
  76. * Proxy to url view helper
  77. *
  78. * @param array $urlOptions Options passed to the assemble method
  79. * of the Route object.
  80. * @param mixed $name The name of a Route to use. If null it will
  81. * use the current Route
  82. * @param bool $reset Whether or not to reset the route defaults
  83. * with those provided
  84. * @return string Url for the link href attribute.
  85. */
  86. public function url(array $urlOptions = array(), $name = null,
  87. $reset = false, $encode = true
  88. ) {
  89. if (!array_key_exists('url', $this->_localHelperObjects)) {
  90. $this->_localHelperObjects['url'] = new Zend_View_Helper_Url();
  91. $this->_localHelperObjects['url']->setView($view);
  92. }
  93. $helper = $this->_localHelperObjects['url'];
  94. return $helper->url($urlOptions, $name, $reset, $encode);
  95. }
  96. /**
  97. * Echo a message
  98. *
  99. * Direct implementation.
  100. *
  101. * @param string $string
  102. * @return string
  103. */
  104. public function message($string)
  105. {
  106. return "<h1>" . $this->escape($message) . "</h1>\n";
  107. }
  108. }
  109. ]]></programlisting>
  110. <para>
  111. Either way, this technique will substantially reduce the
  112. overhead of the helper system by avoiding calls to the
  113. PluginLoader entirely, and either benefiting from autoloading or
  114. bypassing it altogether.
  115. </para>
  116. </sect3>
  117. </sect2>
  118. <sect2 id="performance.view.partial">
  119. <title>How can I speed up view partials?</title>
  120. <para>
  121. Those who use partials heavily and who profile their applications
  122. will often immediately notice that the <code>partial()</code> view
  123. helper incurs a lot of overhead, due to the need to clone the view
  124. object. Is it possible to speed this up?
  125. </para>
  126. <sect3 id="performance.view.partial.render">
  127. <title>Use partial() only when really necessary</title>
  128. <para>
  129. The <code>partial()</code> view helper accepts three arguments:
  130. </para>
  131. <itemizedlist>
  132. <listitem><para>
  133. <code>$name</code>: the name of the view script to render
  134. </para></listitem>
  135. <listitem><para>
  136. <code>$module</code>: the name of the module in which the
  137. view script resides; or, if no third argument is provided
  138. and this is an array or object, it will be the
  139. <code>$model</code> argument.
  140. </para></listitem>
  141. <listitem><para>
  142. <code>$model</code>: an array or object to pass to the
  143. partial representing the clean data to assign to the view.
  144. </para></listitem>
  145. </itemizedlist>
  146. <para>
  147. The power and use of <code>partial()</code> come from the second
  148. and third arguments. The <code>$module</code> argument allows
  149. <code>partial()</code> to temporarily add a script path for the
  150. given module so that the partial view script will resolve to
  151. that module; the <code>$model</code> argument allows you to
  152. explicitly pass variables for use with the partial view.
  153. If you're not passing either argument, <emphasis>use
  154. <code>render()</code> instead</emphasis>!
  155. </para>
  156. <para>
  157. Basically, unless you are actually passing variables to the
  158. partial and need the clean variable scope, or rendering a view
  159. script from another MVC module, there is no reason to incur the
  160. overhead of <code>partial()</code>; instead, use
  161. <classname>Zend_View</classname>'s built-in <code>render()</code> method
  162. to render the view script.
  163. </para>
  164. </sect3>
  165. </sect2>
  166. <sect2 id="performance.view.action">
  167. <title>どのようにしたらアクションメソッドのビューヘルパの呼び出しを速く出来ますか?</title>
  168. <para>
  169. Version 1.5.0 introduced the <code>action()</code> view helper,
  170. which allows you to dispatch an MVC action and capture its rendered
  171. content. This provides an important step towards the DRY principle,
  172. and promotes code reuse. However, as those who profile their
  173. applications will quickly realize, it, too, is an expensive
  174. operation. Internally, the <code>action()</code> view helper needs
  175. to clone new request and response objects, invoke the dispatcher,
  176. invoke the requested controller and action, etc.
  177. </para>
  178. <para>
  179. How can you speed it up?
  180. </para>
  181. <sect3 id="performance.view.action.actionstack">
  182. <title>可能な場合はActionStackを使う</title>
  183. <para>
  184. Introduced at the same time as the <code>action()</code> view
  185. helper, the <link
  186. linkend="zend.controller.actionhelpers.actionstack">ActionStack</link>
  187. consists of an action helper and a front controller plugin.
  188. Together, they allow you to push additional actions to invoke
  189. during the dispatch cycle onto a stack. If you are calling
  190. <code>action()</code> from your layout view scripts, you may
  191. want to instead use the ActionStack, and render your views to
  192. discrete response segments. As an example, you could write a
  193. <code>dispatchLoopStartup()</code> plugin like the following to
  194. add a login form box to each page:
  195. </para>
  196. <programlisting role="php"><![CDATA[
  197. class LoginPlugin extends Zend_Controller_Plugin_Abstract
  198. {
  199. protected $_stack;
  200. public function dispatchLoopStartup(
  201. Zend_Controller_Request_Abstract $request
  202. ) {
  203. $stack = $this->getStack();
  204. $loginRequest = new Zend_Controller_Request_Simple();
  205. $loginRequest->setControllerName('user')
  206. ->setActionName('index')
  207. ->setParam('responseSegment', 'login');
  208. $stack->pushStack($loginRequest);
  209. }
  210. public function getStack()
  211. {
  212. if (null === $this->_stack) {
  213. $front = Zend_Controller_Front::getInstance();
  214. if (!$front->hasPlugin('Zend_Controller_Plugin_ActionStack')) {
  215. $stack = new Zend_Controller_Plugin_ActionStack();
  216. $front->registerPlugin($stack);
  217. } else {
  218. $stack = $front->getPlugin('ActionStack')
  219. }
  220. $this->_stack = $stack;
  221. }
  222. return $this->_stack;
  223. }
  224. }
  225. ]]></programlisting>
  226. <para>
  227. The <code>UserController::indexAction()</code> method might then
  228. use the <code>responseSegment</code> parameter to indicate which
  229. response segment to render to. In the layout script, you would
  230. then simply render that response segment:
  231. </para>
  232. <programlisting role="php"><![CDATA[
  233. <?php $this->layout()->login ?>
  234. ]]></programlisting>
  235. <para>
  236. While the ActionStack still requires a dispatch cycle, this is
  237. still cheaper than the <code>action()</code> view helper as it
  238. does not need to clone objects and reset internal state.
  239. Additionally, it ensures that all pre/post dispatch plugins are
  240. invoked, which may be of particular concern if you are using
  241. front controller plugins for handling ACLs to particular
  242. actions.
  243. </para>
  244. </sect3>
  245. <sect3 id="performance.view.action.model">
  246. <title>Favor helpers that query the model over action()</title>
  247. <para>
  248. In most cases, using <code>action()</code> is simply overkill.
  249. If you have most business logic nested in your models and are
  250. simply querying the model and passing the results to a view
  251. script, it will typically be faster and cleaner to simply write
  252. a view helper that pulls the model, queries it, and does
  253. something with that information.
  254. </para>
  255. <para>
  256. ひとつの例として、下記のようなコントローラーアクションと
  257. ビュースクリプトを考えてみましょう:
  258. </para>
  259. <programlisting role="php"><![CDATA[
  260. class BugController extends Zend_Controller_Action
  261. {
  262. public function listAction()
  263. {
  264. $model = new Bug();
  265. $this->view->bugs = $model->fetchActive();
  266. }
  267. }
  268. // bug/list.phtml:
  269. echo "<ul>\n";
  270. foreach ($this->bugs as $bug) {
  271. printf("<li><b>%s</b>: %s</li>\n",
  272. $this->escape($bug->id),
  273. $this->escape($bug->summary)
  274. );
  275. }
  276. echo "</ul>\n";
  277. ]]></programlisting>
  278. <para>
  279. Using <code>action()</code>, you would then invoke it with the
  280. following:
  281. </para>
  282. <programlisting role="php"><![CDATA[
  283. <?php $this->action('list', 'bug') ?>
  284. ]]></programlisting>
  285. <para>
  286. This could be refactored to a view helper that looks like the
  287. following:
  288. </para>
  289. <programlisting role="php"><![CDATA[
  290. class My_View_Helper_BugList extends Zend_View_Helper_Abstract
  291. {
  292. public function direct()
  293. {
  294. $model = new Bug();
  295. $html = "<ul>\n";
  296. foreach ($model->fetchActive() as $bug) {
  297. $html .= sprintf(
  298. "<li><b>%s</b>: %s</li>\n",
  299. $this->view->escape($bug->id),
  300. $this->view->escape($bug->summary)
  301. );
  302. }
  303. $html .= "</ul>\n";
  304. return $html;
  305. }
  306. }
  307. ]]></programlisting>
  308. <para>
  309. You would then invoke the helper as follows:
  310. </para>
  311. <programlisting role="php"><![CDATA[
  312. <?php $this->bugList() ?>
  313. ]]></programlisting>
  314. <para>
  315. This has two benefits: it no longer incurs the overhead of the
  316. <code>action()</code> view helper, and also presents a more
  317. semantically understandable API.
  318. </para>
  319. </sect3>
  320. </sect2>
  321. </sect1>
  322. <!--
  323. vim:se ts=4 sw=4 et:
  324. -->