performance-view.xml 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- Reviewed: no -->
  3. <!-- EN-Revision: 18210 -->
  4. <sect1 id="performance.view">
  5. <title>ビューのレンダリング</title>
  6. <para>
  7. Zend Frameworkの<acronym>MVC</acronym>レイヤを使うときには、
  8. <classname>Zend_View</classname>を使うようになる機会があります。
  9. <classname>Zend_View</classname>は、
  10. 他のビューやテンプレートエンジンに比べて良く機能します。
  11. ビュースクリプトは<acronym>PHP</acronym>で記述されているので、
  12. <acronym>PHP</acronym>にカスタムで加えたコンパイルのオーバーヘッドを招きませんし、
  13. コンパイルされた<acronym>PHP</acronym>が最適されていないかどうか心配する必要もありません。
  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. これのおかげで <classname>Zend_View</classname> に重要な柔軟性が与えられています;
  25. <classname>Zend_View</classname> を拡張してアプリケーションで利用するであろう、
  26. すべてのヘルパメソッドを提供する必要の代わりに、
  27. 分離されたクラスにヘルパメソッドを定義して、
  28. まるで <classname>Zend_View</classname> そのもののメソッドであるかのように使い切ることができます。
  29. このおかげでビューオブジェクト自身は比較的身軽に保たれ、
  30. オブジェクトが必要なときだけ生成されることが保証されます。
  31. </para>
  32. <para>
  33. 内部的には、
  34. <classname>Zend_View</classname>はヘルパクラスを探すために
  35. <link linkend="zend.loader.pluginloader">プラグインローダー</link>を使います。
  36. これはヘルパを呼び出すたびに<classname>Zend_View</classname>がプラグインローダーに
  37. ヘルパ名を渡す必要があることを意味しています。
  38. プラグインローダーはそれからクラス名を決定し、
  39. 必要に応じてクラスファイルを読み込み、
  40. さらにインスタンス化されるかもしれないクラス名を返します。
  41. 読み込まれたヘルパを<classname>Zend_View</classname>が内部のレジストリに保持するので、
  42. その次にヘルパを使うときにはより速くなります。
  43. ただし、多くのヘルパを使うと呼び出しは増加します。
  44. </para>
  45. <para>
  46. それでは質問です: どのようにしたらヘルパの解決を速くできますか?
  47. </para>
  48. <sect3 id="performance.view.pluginloader.cache">
  49. <title>ファイルキャッシュを含むプラグインローダーを使う</title>
  50. <para>
  51. 最も簡単で安く済む方法は
  52. <link linkend="performance.classloading.pluginloader">プラグインのパフォーマンスの向上</link>と同じです:
  53. プラグインローダーは
  54. <link linkend="zend.loader.pluginloader.performance.example">includeファイルキャッシュ</link>を使います。
  55. 不確かな証拠によれば、
  56. この技術のおかげでopcodeキャッシュがないシステム上では25から30%の、
  57. opcodeキャッシュがあるシステム上では40から65%の利得があるそうです。
  58. </para>
  59. </sect3>
  60. <sect3 id="performance.view.pluginloader.extend">
  61. <title>よく使われるヘルパメソッドを提供するようにZend_Viewを拡張する</title>
  62. <para>
  63. さらにもっと性能を調整することを探求する他の方法は、
  64. アプリケーションで最も使うヘルパメソッドを手動で<classname>Zend_View</classname>に付加して拡張することです。
  65. そのようなヘルパは簡単に適切なヘルパクラスを手動でインスタンス化して、
  66. その代わりとなり、
  67. あるいはメソッドに完全なヘルパ実装を詰め込みます。
  68. </para>
  69. <programlisting language="php"><![CDATA[
  70. class My_View extends Zend_View
  71. {
  72. /**
  73. * @var array 使われたヘルパクラスのレジストリ
  74. */
  75. protected $_localHelperObjects = array();
  76. /**
  77. * urlビューヘルパの代替
  78. *
  79. * @param array $urlOptions ルートオブジェクトを組み立てるメソッドへ渡されるオプション
  80. * @param mixed $name 使用するルート名. nullの場合は現行ルートを使う。
  81. * @param bool $reset それら提供されるデフォルトルートをリセットするか否か
  82. * @return string linkのhref属性のためのUrl
  83. */
  84. public function url(array $urlOptions = array(), $name = null,
  85. $reset = false, $encode = true
  86. ) {
  87. if (!array_key_exists('url', $this->_localHelperObjects)) {
  88. $this->_localHelperObjects['url'] = new Zend_View_Helper_Url();
  89. $this->_localHelperObjects['url']->setView($this);
  90. }
  91. $helper = $this->_localHelperObjects['url'];
  92. return $helper->url($urlOptions, $name, $reset, $encode);
  93. }
  94. /**
  95. * メッセージを返す
  96. *
  97. * 直接実装
  98. *
  99. * @param string $string
  100. * @return string
  101. */
  102. public function message($string)
  103. {
  104. return "<h1>" . $this->escape($message) . "</h1>\n";
  105. }
  106. }
  107. ]]></programlisting>
  108. <para>
  109. この技術はプラグインローダーの呼び出しを完全に避けたり、
  110. オートローディングの恩恵を受けたり、
  111. あるいはまったくそれを迂回したり、
  112. いずれかの方法でヘルパシステムのオーバーヘッドを十分に減らすでしょう。
  113. </para>
  114. </sect3>
  115. </sect2>
  116. <sect2 id="performance.view.partial">
  117. <title>どのようにしたらビューを部分的に高速化できますか?</title>
  118. <para>
  119. 部分的に頻繁に利用したり、アプリケーションのプロファイルを実行したりする人は、
  120. しばしばすぐにビューオブジェクトのクローンを必要とすることになっている、
  121. <methodname>partial()</methodname> ビューヘルパがオーバーヘッドの大部分を占めていることに気付くでしょう。
  122. これを速度向上させられるでしょうか?
  123. </para>
  124. <sect3 id="performance.view.partial.render">
  125. <title>本当に必要な時だけpartial()を使う</title>
  126. <para>
  127. <methodname>partial()</methodname> ビューヘルパには3つの引数があります:
  128. </para>
  129. <itemizedlist>
  130. <listitem><para>
  131. <varname>$name</varname>: レンダリングするビュースクリプトの名前
  132. </para></listitem>
  133. <listitem><para>
  134. <varname>$module</varname>: 表示スクリプトが位置するモジュールの名前;
  135. または3番目の引数が渡されない場合、配列またはオブジェクトで、
  136. <varname>$model</varname>引数
  137. </para></listitem>
  138. <listitem><para>
  139. <varname>$model</varname>: ビューにアサインする純粋なデータを示す部分に渡す配列またはオブジェクト
  140. </para></listitem>
  141. </itemizedlist>
  142. <para>
  143. <methodname>partial()</methodname> の威力や使い道は2番目と3番目の引数に依存します。
  144. <varname>$module</varname> 引数のおかげで
  145. partialビュースクリプトがモジュールを解決するために、
  146. 与えられたモジュールに <methodname>partial()</methodname> が一時的にスクリプトパスを追加できる。;
  147. <varname>$model</varname> 引数のおかげでpartialビューを使うために引数を明示的に渡すことができます。
  148. もしどちらの引数も渡さないのならば、
  149. <emphasis>替わりに</emphasis> <methodname>render()</methodname> を使ってください!
  150. </para>
  151. <para>
  152. 基本的に、あなたが実際に変数をその部分に渡して、純粋な変数の範囲を必要とするか、
  153. または他の<acronym>MVC</acronym>モジュールからビュースクリプトをレンダリングするまで、
  154. <methodname>partial()</methodname>のオーバーヘッドを受け入れる理由がありません。;
  155. その代わり、ビュースクリプトをレンダリングするために、
  156. <classname>Zend_View</classname>組込みの<methodname>render()</methodname>メソッドを使ってください。
  157. </para>
  158. </sect3>
  159. </sect2>
  160. <sect2 id="performance.view.action">
  161. <title>どのようにしたらアクションメソッドのビューヘルパの呼び出しを速くできますか?</title>
  162. <para>
  163. バージョン1.5.0で <methodname>action()</methodname> ビューヘルパが導入されました。
  164. それにより<acronym>MVC</acronym>のアクションをディスパッチして、
  165. レンダリングされたコンテンツを入手できるようになります。
  166. これは<acronym>DRY</acronym>原則に向かう重要なステップで、コードの再利用を促します。
  167. しかしながら、アプリケーションをプロファイルする人がすぐ実感するように、
  168. これも高くつく操作です。
  169. 内部的に、<methodname>action()</methodname> ビューヘルパでは新しいリクエスト及びレスポンスオブジェクトを複製して、
  170. ディスパッチャを呼び出し、求められたコントローラとアクションなどを呼び出す必要があります。
  171. </para>
  172. <para>
  173. どうしたら速くできるでしょう?
  174. </para>
  175. <sect3 id="performance.view.action.actionstack">
  176. <title>可能な場合はアクションスタックを使う</title>
  177. <para>
  178. <methodname>action()</methodname> ビューヘルパと同時期に導入されましたが、
  179. <link linkend="zend.controller.actionhelpers.actionstack">アクションスタック</link>
  180. はアクションヘルパとフロントコントローラプラグインから成り立ちます。
  181. 共に、それらのおかげでディスパッチサイクルの間に呼び出すべき、
  182. 追加のアクションをスタックに押し込むことができます。
  183. もしレイアウトビュースクリプトから <methodname>action()</methodname> を呼び出しているなら、
  184. アクションスタックを使うかわりに、
  185. ディスクリートなレスポンスセグメントにビューをレンダリングしたいかもしれません。
  186. 例えば、各画面にログインフォームの枠を付け加える下記の様な
  187. <methodname>dispatchLoopStartup()</methodname> プラグインを書けるでしょう。:
  188. </para>
  189. <programlisting language="php"><![CDATA[
  190. class LoginPlugin extends Zend_Controller_Plugin_Abstract
  191. {
  192. protected $_stack;
  193. public function dispatchLoopStartup(
  194. Zend_Controller_Request_Abstract $request
  195. ) {
  196. $stack = $this->getStack();
  197. $loginRequest = new Zend_Controller_Request_Simple();
  198. $loginRequest->setControllerName('user')
  199. ->setActionName('index')
  200. ->setParam('responseSegment', 'login');
  201. $stack->pushStack($loginRequest);
  202. }
  203. public function getStack()
  204. {
  205. if (null === $this->_stack) {
  206. $front = Zend_Controller_Front::getInstance();
  207. if (!$front->hasPlugin('Zend_Controller_Plugin_ActionStack')) {
  208. $stack = new Zend_Controller_Plugin_ActionStack();
  209. $front->registerPlugin($stack);
  210. } else {
  211. $stack = $front->getPlugin('ActionStack')
  212. }
  213. $this->_stack = $stack;
  214. }
  215. return $this->_stack;
  216. }
  217. }
  218. ]]></programlisting>
  219. <para>
  220. それから <methodname>UserController::indexAction()</methodname> メソッドは
  221. レンダリングするのがどのレスポンスセグメントかを示す <varname>$responseSegment</varname> パラメータを使うかもしれません。
  222. レイアウトスクリプトでそのレスポンスセグメントを単純にレンダリングするでしょう。
  223. </para>
  224. <programlisting language="php"><![CDATA[
  225. <?php $this->layout()->login ?>
  226. ]]></programlisting>
  227. <para>
  228. アクションスタックがまだディスパッチサイクルを必要とするのに対して、
  229. オブジェクトを複製して内部状態をリセットする必要がないので、
  230. <methodname>action()</methodname> ビューヘルパよりもっと安くつきます。
  231. さらに、それはすべてのプレディスパッチ、
  232. またはポストディスパッチのプラグインが呼び出されることを保証します。
  233. それは、特別なアクションのために<acronym>ACL</acronym>を処理するフロントコントローラプラグインをもし使っているなら、
  234. 特別に関心があることかもしれません。
  235. </para>
  236. </sect3>
  237. <sect3 id="performance.view.action.model">
  238. <title>action()を通じてモデルに問い合わせるお好みヘルパ</title>
  239. <para>
  240. ほとんどの場合、<methodname>action()</methodname> を使うのは過剰です。
  241. もしモデルの中に業務ロジックをはなはだしく折り重ねていて、
  242. モデルに単純に問い合わせて、ビュースクリプトに結果を渡すなら、
  243. モデルを引き出してきて問合せを行い、
  244. その情報で何かを行うビューヘルパを単純に書くことが、
  245. 一般的により速くて誤りがないでしょう。
  246. </para>
  247. <para>
  248. ひとつの例として、下記のようなコントローラーアクションと
  249. ビュースクリプトを考えてみましょう:
  250. </para>
  251. <programlisting language="php"><![CDATA[
  252. class BugController extends Zend_Controller_Action
  253. {
  254. public function listAction()
  255. {
  256. $model = new Bug();
  257. $this->view->bugs = $model->fetchActive();
  258. }
  259. }
  260. // bug/list.phtml:
  261. echo "<ul>\n";
  262. foreach ($this->bugs as $bug) {
  263. printf("<li><b>%s</b>: %s</li>\n",
  264. $this->escape($bug->id),
  265. $this->escape($bug->summary)
  266. );
  267. }
  268. echo "</ul>\n";
  269. ]]></programlisting>
  270. <para>
  271. それから <methodname>action()</methodname> を使って、
  272. 下記のようにして呼び出すでしょう:
  273. </para>
  274. <programlisting language="php"><![CDATA[
  275. <?php $this->action('list', 'bug') ?>
  276. ]]></programlisting>
  277. <para>
  278. これは下記のように見えるビューヘルパにリファクタリングできるでしょう。:
  279. </para>
  280. <programlisting language="php"><![CDATA[
  281. class My_View_Helper_BugList extends Zend_View_Helper_Abstract
  282. {
  283. public function bugList()
  284. {
  285. $model = new Bug();
  286. $html = "<ul>\n";
  287. foreach ($model->fetchActive() as $bug) {
  288. $html .= sprintf(
  289. "<li><b>%s</b>: %s</li>\n",
  290. $this->view->escape($bug->id),
  291. $this->view->escape($bug->summary)
  292. );
  293. }
  294. $html .= "</ul>\n";
  295. return $html;
  296. }
  297. }
  298. ]]></programlisting>
  299. <para>
  300. それからヘルパを下記のように呼び出すでしょう:
  301. </para>
  302. <programlisting language="php"><![CDATA[
  303. <?php $this->bugList() ?>
  304. ]]></programlisting>
  305. <para>
  306. これには2つの利点があります:
  307. それはもはや <methodname>action()</methodname> ビューヘルパのオーバーヘッドを受けず、
  308. より意味的に理解できる<acronym>API</acronym>も表現します。
  309. </para>
  310. </sect3>
  311. </sect2>
  312. </sect1>
  313. <!--
  314. vim:se ts=4 sw=4 et:
  315. -->