[GENERIC] Zend_Translate:
[zend.git] / documentation / manual / ja / module_specs / Zend_Session-AdvancedUsage.xml
blob67ec1892a2979fe41b10f7efce367a9a95ca6678
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!-- Reviewed: no -->
3 <!-- EN-Revision: 21740 -->
4 <sect1 id="zend.session.advanced_usage">
6     <title>高度な使用法</title>
8     <para>
9         基本的な使用法の例で Zend Framework のセッションを完全に使用できますが、
10         よりよい方法もあります。ここでは、セッションの処理方法や
11         <classname>Zend_Session</classname> コンポーネントのより行動な使用法を説明します。
12     </para>
14     <sect2 id="zend.session.advanced_usage.starting_a_session">
16         <title>セッションの開始</title>
18         <para>
19             すべてのリクエストで <classname>Zend_Session</classname> の機能を使用してセッション管理したい場合は、
20             起動ファイルでセッションを開始します。
21         </para>
23         <example id="zend.session.advanced_usage.starting_a_session.example">
25             <title>グローバルセッションの開始</title>
27             <programlisting language="php"><![CDATA[
28 Zend_Session::start();
29 ]]></programlisting>
31         </example>
33         <para>
34             起動ファイルでセッションを開始する際には、
35             ヘッダがブラウザに送信される前に確実にセッションが始まるようにします。
36             そうしないと例外が発生してしまい、おそらくユーザが見るページは崩れてしまうでしょう。
37             さまざまな高度な機能を使用するには、まず <methodname>Zend_Session::start()</methodname>
38             が必要です (高度な機能の詳細については後で説明します)。
39         </para>
41         <para>
42             <classname>Zend_Session</classname> を使用してセッションを開始する方法は四通りありますが、
43             そのうち二つは間違った方法です。
44         </para>
46         <orderedlist>
47             <listitem>
48                 <para>
49                     間違い: <acronym>PHP</acronym> の
50                     <ulink url="http://www.php.net/manual/ja/ref.session.php#ini.session.auto-start"><code>session.auto_start</code>
51                     </ulink> を有効にしてはいけません。
52                     もし mod_php (やそれと同等のもの) を使用しており、
53                     <code>php.ini</code> でこの設定が有効になっている、かつそれを無効にすることができない
54                     という場合は、<code>.htaccess</code> ファイル (通常は HTML のドキュメントルートにあります)
55                     に以下の内容を追加します。
56                     <programlisting language="httpd.conf"><![CDATA[
57 php_value session.auto_start 0
58 ]]></programlisting>
59                 </para>
60             </listitem>
61             <listitem>
62                 <para>
63                     間違い: <acronym>PHP</acronym> の
64                     <ulink url="http://www.php.net/session_start"><methodname>session_start()</methodname></ulink>
65                     関数を直接使用してはいけません。
66                     <methodname>session_start()</methodname> を直接使用した後で <classname>Zend_Session_Namespace</classname> を使用した場合は、
67                     <methodname>Zend_Session::start()</methodname> が例外 ("session has already been started")
68                     をスローします。<classname>Zend_Session_Namespace</classname> を使用するか
69                     明示的に <methodname>Zend_Session::start()</methodname> で開始した後で
70                     <methodname>session_start()</methodname> をコールすると、<code>E_NOTICE</code>
71                     が発生し、そのコールは無視されます。
72                 </para>
73             </listitem>
74             <listitem>
75                 <para>
76                     正解: <methodname>Zend_Session::start()</methodname> を使用します。
77                     すべてのリクエストでセッションを使用したい場合は、
78                     この関数コールを起動コードの最初のほうで無条件に記述します。
79                     セッションにはある程度のオーバーヘッドがあります。
80                     セッションを使用したいリクエストとそうでないリクエストがある場合は、
81                 </para>
82                 <itemizedlist mark="opencircle">
83                     <listitem>
84                         <para>
85                             起動コード内で、<methodname>Zend_Session::setOptions()</methodname> を使用して
86                             無条件にオプション <code>strict</code> を <constant>TRUE</constant> にします。
87                         </para>
88                     </listitem>
89                     <listitem>
90                         <para>
91                             セッションを必要とするリクエスト内で、
92                             <classname>Zend_Session_Namespace</classname> のインスタンスを作成する前に
93                             <methodname>Zend_Session::start()</methodname> をコールします。
94                         </para>
95                     </listitem>
96                     <listitem>
97                         <para>
98                             通常どおり、必要に応じて "<code>new Zend_Session_Namespace()</code>"
99                             を使用します。事前に <methodname>Zend_Session::start()</methodname>
100                             がコールされていることを確認しておきましょう。
101                         </para>
102                     </listitem>
103                 </itemizedlist>
104                 <para>
105                     <code>strict</code> オプションにより、<code>new Zend_Session_Namespace()</code>
106                     が自動的に <methodname>Zend_Session::start()</methodname> でセッションを開始することがなくなります。
107                     したがって、このオプションを使用すると、アプリケーションの開発者が
108                     特定のリクエストにはセッションを使用しないという設計をおこなうことができます。
109                     このオプションを使用すると、明示的に
110                     <methodname>Zend_Session::start()</methodname> をコールする前に Zend_Session_Namespace
111                     のインスタンスを作成しようとしたときに例外がスローされます。
112                     開発者は、<methodname>Zend_Session::setOptions()</methodname>
113                     の使用がユーザにどれだけの影響を与えるかを注意するようにしましょう。
114                     これらのオプションは
115                     (もととなる ext/session のオプションと同様)、
116                     全体に副作用を及ぼすからです。
117                 </para>
118             </listitem>
119             <listitem>
120                 <para>
121                     正解: 必要に応じて <classname>Zend_Session_Namespace</classname> のインスタンスを作成します。
122                     <acronym>PHP</acronym> のセッションは、自動的に開始されます。
123                     これはもっともシンプルな使用法で、たいていの場合にうまく動作します。
124                     しかし、デフォルトであるクッキーベースのセッション (強く推奨します)
125                     を使用している場合には、<acronym>PHP</acronym> がクライアントに何らかの出力
126                     (<ulink url="http://www.php.net/headers_sent">HTTP ヘッダ</ulink> など)
127                     をする <emphasis>前に</emphasis>、確実に
128                     最初の <code>new Zend_Session_Namespace()</code> をコールしなければなりません。
129                     詳細は <xref linkend="zend.session.global_session_management.headers_sent" />
130                     を参照ください。
131                 </para>
132             </listitem>
133         </orderedlist>
135     </sect2>
137     <sect2 id="zend.session.advanced_usage.locking">
139         <title>セッション名前空間のロック</title>
141         <para>
142             セッション名前空間をロックし、
143             それ以降その名前空間のデータに手を加えられないようにできます。
144             特定の名前空間を読み取り専用にするには
145             <methodname>lock()</methodname> を、そして
146             読み取り専用の名前空間を読み書きできるようにするには <methodname>unLock()</methodname>
147             を使用します。<methodname>isLocked()</methodname> を使用すると、
148             その名前空間がロックされているかどうかを調べることができます。
149             このロックは一時的なものであり、そのリクエスト内でのみ有効となります。
150             名前空間をロックしても、その名前空間に保存されているオブジェクトの
151             セッターメソッドには何の影響も及ぼしません。
152             しかし、名前空間自体のセッターメソッドは使用できず、
153             名前空間に直接格納されたオブジェクトの削除や置換ができなくなります。同様に、
154             <classname>Zend_Session_Namespace</classname> のインスタンスをロックしたとしても、
155             同じデータをさすシンボルテーブルの使用をとめることはできません
156             (<ulink url="http://www.php.net/references"><acronym>PHP</acronym>
157             のリファレンスについての説明</ulink>も参照ください)。
158         </para>
160         <example id="zend.session.advanced_usage.locking.example.basic">
162             <title>セッション名前空間のロック</title>
164             <programlisting language="php"><![CDATA[
165 $userProfileNamespace = new Zend_Session_Namespace('userProfileNamespace');
167 // このセッションに読み取り専用ロックをかけます
168 $userProfileNamespace->lock();
170 // 読み取り専用ロックを解除します
171 if ($userProfileNamespace->isLocked()) {
172     $userProfileNamespace->unLock();
174 ]]></programlisting>
176         </example>
178     </sect2>
180     <sect2 id="zend.session.advanced_usage.expiration">
182         <title>名前空間の有効期限</title>
184         <para>
185             名前空間およびその中の個々のキーについて、その寿命を制限できます。
186             これは、たとえばリクエスト間で一時的な情報を渡す際に使用します。
187             これにより、認証内容などの機密情報へアクセスできないようにし、
188             セキュリティリスクを下げます。有効期限の設定は経過秒数によって決めることもできますし、
189             "ホップ" 数によって決めることもできます。ホップ数とは、
190             一連のリクエストの回数を表します。
191         </para>
193         <example id="zend.session.advanced_usage.expiration.example">
195             <title>有効期限切れの例</title>
197             <programlisting language="php"><![CDATA[
198 $s = new Zend_Session_Namespace('expireAll');
199 $s->a = 'apple';
200 $s->p = 'pear';
201 $s->o = 'orange';
203 $s->setExpirationSeconds(5, 'a'); // キー "a" だけは 5 秒で有効期限切れとなります
205 // 名前空間全体は、5 "ホップ" で有効期限切れとなります
206 $s->setExpirationHops(5);
208 $s->setExpirationSeconds(60);
209 // "expireAll" 名前空間は、60 秒が経過するか
210 // 5 ホップに達するかのどちらかが発生した時点で
211 // "有効期限切れ" となります
212 ]]></programlisting>
214         </example>
216         <para>
217             現在のリクエストで期限切れになったデータを扱うにあたり、
218             データを取得する際には注意が必要です。
219             データは参照で返されますが、それを変更したとしても
220             期限切れのデータを現在のリクエストから持ち越すことはできません。
221             有効期限を "リセット" するには、取得したデータをいったん一時変数に格納し、
222             名前空間上の内容を削除し、あらためて適切なキーで再設定します。
223         </para>
225     </sect2>
227     <sect2 id="zend.session.advanced_usage.controllers">
229         <title>コントローラでのセッションのカプセル化</title>
231         <para>
232             名前空間を使用すると、コントローラによるセッションへのアクセスの際に
233             変数の汚染を防ぐこともできます。
234             たとえば、認証コントローラでは、セキュリティの観点から
235             そのセッション状態データを他のコントローラとは別に管理することになるでしょう。
236         </para>
238         <example id="zend.session.advanced_usage.controllers.example">
240             <title>コントローラでの名前空間つきセッションによる有効期限の管理</title>
242             <para>
243                 次のコードは、質問を表示するコントローラの一部です。
244                 ここでは論理型の変数を用意して、質問に対する回答を受け付けるかどうかを表しています。
245                 この場合は、表示されている質問に 300 秒以内に答えることになります。
246             </para>
248             <programlisting language="php"><![CDATA[
249 // ...
250 // 質問を表示するコントローラ
251 $testSpace = new Zend_Session_Namespace('testSpace');
252 // この変数にだけ有効期限を設定します
253 $testSpace->setExpirationSeconds(300, 'accept_answer');
254 $testSpace->accept_answer = true;
255 //...
256 ]]></programlisting>
258             <para>
259                 次に、回答を処理するコントローラを示します。
260                 時間内に回答したかどうかをもとにして、回答を受け付けるかどうかを判断しています。
261             </para>
263             <programlisting language="php"><![CDATA[
264 // ...
265 // 回答を処理するコントローラ
266 $testSpace = new Zend_Session_Namespace('testSpace');
267 if ($testSpace->accept_answer === true) {
268     // 時間内
270 else {
271     // 時間切れ
273 // ...
274 ]]></programlisting>
275         </example>
277     </sect2>
279     <sect2 id="zend.session.advanced_usage.single_instance">
281         <title>名前空間内あたりのインスタンス数をひとつに絞り込む</title>
283         <para>
284             <link linkend="zend.session.advanced_usage.locking">セッションのロック</link>
285             を利用すれば、名前空間つきセッションデータを予期せず使用してしまうことはある程度防げます。
286             しかし、<classname>Zend_Session_Namespace</classname> には、
287             単一の名前空間内で複数のインスタンスを作成することを防ぐ機能もあります。
288         </para>
290         <para>
291             この機能を有効にするには、<classname>Zend_Session_Namespace</classname>
292             のインスタンスを作成する際に、コンストラクタの第二引数に <constant>TRUE</constant>
293             を渡します。それ以降は、同一名前空間でインスタンスを作成しようとすると例外がスローされます。
294         </para>
296         <example id="zend.session.advanced_usage.single_instance.example">
298             <title>セッション名前空間へのアクセスを単一のインスタンスに制限する</title>
300             <programlisting language="php"><![CDATA[
301 // 名前空間のインスタンスを作成します
302 $authSpaceAccessor1 = new Zend_Session_Namespace('Zend_Auth');
304 // 同じ名前空間で別のインスタンスを作成します。
305 // しかし今後はインスタンスを作成できないようにします
306 $authSpaceAccessor2 = new Zend_Session_Namespace('Zend_Auth', true);
308 // 参照をすることは可能です
309 $authSpaceAccessor3 = $authSpaceAccessor2;
311 $authSpaceAccessor1->foo = 'bar';
313 assert($authSpaceAccessor2->foo, 'bar');
315 try {
316     $aNamespaceObject = new Zend_Session_Namespace('Zend_Auth');
317 } catch (Zend_Session_Exception $e) {
318     echo 'この名前空間ではインスタンスを作成できません。すでに ' .
319          '$authSpaceAccessor2 があるからです\n';
321 ]]></programlisting>
323         </example>
325         <para>
326             上の例では、コンストラクタの第二引数を用いて
327             "<classname>Zend_Auth</classname>" 名前空間では今後インスタンスを作成させないよう
328             <classname>Zend_Session_Namespace</classname> に指示しています。
329             インスタンスを作成しようとすると、コンストラクタから例外がスローされます。
330             したがって、このセッション名前空間へのアクセスが必要となった場合は、
331             今後は現在あるインスタンス (上の例の場合なら <code>$authSpaceAccessor1</code>、
332             <code>$authSpaceAccessor2</code> あるいは <code>$authSpaceAccessor3</code>)
333             のどれかを使うことになるわけです。
334             たとえば、名前空間への参照を静的変数に格納したり、
335             <ulink url="http://www.martinfowler.com/eaaCatalog/registry.html">レジストリ</ulink>
336             (<xref linkend="zend.registry" /> を参照ください) に格納したり、
337             あるいは名前空間へのアクセスを必要とするその他のメソッドで使用したりします。
338         </para>
340     </sect2>
342     <sect2 id="zend.session.advanced_usage.arrays">
344         <title>配列の使用</title>
346         <para>
347             <acronym>PHP</acronym> のマジックメソッドの実装上の理由で、バージョン 5.2.1 より前の <acronym>PHP</acronym>
348             では名前空間内の配列の修正ができません。
349             もし <acronym>PHP</acronym> 5.2.1 以降を使っている場合は、<link
350             linkend="zend.session.advanced_usage.objects">このセクションは読み飛ばしてください</link>。
351         </para>
353         <example id="zend.session.advanced_usage.arrays.example.modifying">
355             <title>セッション名前空間内での配列データの修正</title>
357             <para>
358                 問題の再現手順は、このようになります。
359             </para>
361             <programlisting language="php"><![CDATA[
362 $sessionNamespace = new Zend_Session_Namespace();
363 $sessionNamespace->array = array();
365 // PHP 5.2.1 より前のバージョンでは、期待通りに動作しません
366 $sessionNamespace->array['testKey'] = 1;
367 echo $sessionNamespace->array['testKey'];
368 ]]></programlisting>
370         </example>
372         <example id="zend.session.advanced_usage.arrays.example.building_prior">
374             <title>セッションに保存する前に配列を作成する</title>
376             <para>
377                 可能なら、先に配列のすべての値を設定してからセッションに格納するようにすればこの問題を回避できます。
378             </para>
380             <programlisting language="php"><![CDATA[
381 $sessionNamespace = new Zend_Session_Namespace('Foo');
382 $sessionNamespace->array = array('a', 'b', 'c');
383 ]]></programlisting>
385         </example>
387         <para>
388             この問題の影響を受けるバージョンの <acronym>PHP</acronym> を使っている場合で、
389             セッション名前空間に代入した後に配列を修正したい場合は、
390             以下の回避策のうちのいずれかを使用します。
391         </para>
393         <example id="zend.session.advanced_usage.arrays.example.workaround.reassign">
395             <title>回避策: 修正した配列を再度代入する</title>
397             <para>
398                 以下のコードでは、保存されている配列のコピーを作成してそれを修正し、
399                 修正したコピーを再度代入してもとの配列を上書きします。
400             </para>
402             <programlisting language="php"><![CDATA[
403 $sessionNamespace = new Zend_Session_Namespace();
405 // 配列を代入します
406 $sessionNamespace->array = array('tree' => 'apple');
408 // そのコピーを作成します
409 $tmp = $sessionNamespace->array;
411 // コピーのほうを修正します
412 $tmp['fruit'] = 'peach';
414 // 修正したコピーをセッション名前空間に書き戻します
415 $sessionNamespace->array = $tmp;
417 echo $sessionNamespace->array['fruit']; // prints "peach"
418 ]]></programlisting>
420         </example>
422         <example id="zend.session.advanced_usage.arrays.example.workaround.reference">
424             <title>回避策: 参照を含む配列を格納する</title>
426             <para>
427                 あるいは、実際の配列への参照を含む配列を格納しておき、
428                 間接的にアクセスするようにします。
429             </para>
431             <programlisting language="php"><![CDATA[
432 $myNamespace = new Zend_Session_Namespace('myNamespace');
433 $a = array(1, 2, 3);
434 $myNamespace->someArray = array( &$a );
435 $a['foo'] = 'bar';
436 echo $myNamespace->someArray['foo']; // "bar" と表示されます
437 ]]></programlisting>
439         </example>
441     </sect2>
443     <sect2 id="zend.session.advanced_usage.objects">
445         <title>セッションでのオブジェクトの使用</title>
447         <para>
448             オブジェクトを <acronym>PHP</acronym> セッション内で持続的に使用したい場合は、
449             <ulink url="http://www.php.net/manual/ja/language.oop.serialization.php">シリアライズ</ulink>
450             を使用します。したがって、<acronym>PHP</acronym> セッションから永続オブジェクトを取得したら、
451             そのシリアライズを解除しなければなりません。
452             ということは、永続オブジェクトをセッションから読み出す前に、
453             そのオブジェクトのクラスが定義されていなければならないということです。
454             クラスが定義されていない場合は、<code>stdClass</code>
455             のオブジェクトとして復元されます。
456         </para>
458     </sect2>
460     <sect2 id="zend.session.advanced_usage.testing">
462         <title>ユニットテストでのセッションの使用</title>
464         <para>
465             Zend Framework 自体のテストには PHPUnit を使用しています。
466             多くの開発者は、このテストスイートを拡張して自分のアプリケーションのコードをテストしています。
467             ユニットテスト中で、セッションの終了後に書き込み関連のメソッドを使用すると
468             "<emphasis>Zend_Session is currently marked as read-only</emphasis>"
469             という例外がスローされます。しかし、<classname>Zend_Session</classname> を使用するユニットテストには要注意です。
470             セッションを閉じたり (<methodname>Zend_Session::writeClose()</methodname>)
471             破棄したり (<methodname>Zend_Session::destroy()</methodname>) したら、
472             それ以降は <classname>Zend_Session_Namespace</classname> のインスタンスへのキーの設定や削除ができなくなります。
473             これは、ext/session や、<acronym>PHP</acronym> の
474             <methodname>session_destroy()</methodname> および <methodname>session_write_close()</methodname>
475             の仕様によるものです, これらには、ユニットテストの setup/teardown
476             時に使用できるような、いわゆる "undo" 機能が備わっていないのです。
477         </para>
479         <para>
480             この問題の回避策は、
481             <code>SessionTest.php</code> および <code>SessionTestHelper.php</code>
482             (どちらも <code>tests/Zend/Session</code> にあります)
483             のユニットテストテスト <methodname>testSetExpirationSeconds()</methodname> を参照ください。
484             これは、<acronym>PHP</acronym> の <methodname>exec()</methodname> によって別プロセスを起動しています。
485             新しいプロセスが、ブラウザからの二番目以降のリクエストをシミュレートします。
486             この別プロセスの開始時にはセッションを "初期化" します。
487             ちょうど、ふつうの <acronym>PHP</acronym> スクリプトがウェブリクエストを実行する場合と同じような動作です。
488             また、呼び出し元のプロセスで <code>$_SESSION</code> を変更すると、
489             子プロセスでそれが反映されます。親側では
490             <methodname>exec()</methodname> を使用する前にセッションを閉じています。
491         </para>
493         <example id="zend.session.advanced_usage.testing.example">
495             <title>PHPUnit で Zend_Session を使用したコードをテストする例</title>
497             <programlisting language="php"><![CDATA[
498 // testing setExpirationSeconds()
499 $script = 'SessionTestHelper.php';
500 $s = new Zend_Session_Namespace('space');
501 $s->a = 'apple';
502 $s->o = 'orange';
503 $s->setExpirationSeconds(5);
505 Zend_Session::regenerateId();
506 $id = Zend_Session::getId();
507 session_write_close(); // release session so process below can use it
508 sleep(4); // not long enough for things to expire
509 exec($script . "expireAll $id expireAll", $result);
510 $result = $this->sortResult($result);
511 $expect = ';a === apple;o === orange;p === pear';
512 $this->assertTrue($result === $expect,
513     "iteration over default Zend_Session namespace failed; " .
514     "expecting result === '$expect', but got '$result'");
516 sleep(2); // long enough for things to expire (total of 6 seconds
517           // waiting, but expires in 5)
518 exec($script . "expireAll $id expireAll", $result);
519 $result = array_pop($result);
520 $this->assertTrue($result === '',
521     "iteration over default Zend_Session namespace failed; " .
522     "expecting result === '', but got '$result')");
523 session_start(); // resume artificially suspended session
525 // We could split this into a separate test, but actually, if anything
526 // leftover from above contaminates the tests below, that is also a
527 // bug that we want to know about.
528 $s = new Zend_Session_Namespace('expireGuava');
529 $s->setExpirationSeconds(5, 'g'); // now try to expire only 1 of the
530                                   // keys in the namespace
531 $s->g = 'guava';
532 $s->p = 'peach';
533 $s->p = 'plum';
535 session_write_close(); // release session so process below can use it
536 sleep(6); // not long enough for things to expire
537 exec($script . "expireAll $id expireGuava", $result);
538 $result = $this->sortResult($result);
539 session_start(); // resume artificially suspended session
540 $this->assertTrue($result === ';p === plum',
541     "iteration over named Zend_Session namespace failed (result=$result)");
542 ]]></programlisting>
544         </example>
546     </sect2>
548 </sect1>