[ZF-10089] Zend_Log
[zend.git] / documentation / manual / ru / module_specs / Zend_Session-AdvancedUsage.xml
blob68dabfd4b2b00c43bf7a575c5430e2aefa50c3bd
1 <sect1 id="zend.session.advancedusage">
3     <title>Расширенное использование<!--Advanced Usage--></title>
5     <para>
6         Хотя базовое использование является совершенно допустимым вариантом
7         использования сессий Zend Framework, стоит рассмотреть другие
8         возможности их использования. См.
9         <link linkend="zend.auth.introduction.using"> пример на
10         <code>Zend_Auth</code></link>, который по умолчанию неявно
11         использует Zend_Session_Namespace для сохранения меток аутентификации.
12         Этот пример показывает один из способов быстрой и легкой интеграции
13         Zend_Session_Namespace и Zend_Auth.
14 <!--
15         While the basic usage examples are a perfectly acceptable way to utilize Zend Framework sessions, there are some
16         best practices to consider. Consider the
17         <link linkend="zend.auth.introduction.using"><code>Zend_Auth</code> example</link>
18         that transparently uses Zend_Session_Namespace by default to persist authentication tokens. This example shows
19         one approach to quickly and easily integrate Zend_Session_Namespace and Zend_Auth.
20 -->
21     </para>
23     <sect2 id="zend.session.startingasession">
25         <title>Старт сессии<!--Starting a Session--></title>
27         <para>
28             Если вы хотите, чтобы все запросы имели сессии и использовали
29             сессии Zend Framework, то стартуйте сессию в файле загрузки:
30 <!--
31             If you want all requests to have a session and use Zend Framework sessions, then start the session in the
32             bootstrap file:
33 -->
34         </para>
36         <example>
37             <title>Старт общей сессии<!--Starting the Global Session--></title>
38 <programlisting language="php">
39 <![CDATA[<?php
40 ...
41 require_once 'Zend/Session.php';
42 Zend_Session::start();
43 ...
44 ?>]]></programlisting>
45         </example>
47         <para>
48             Стартуя сессию в файле загрузки, вы исключаете вероятность того, что
49             старт сессии произойдет после того, как заголовки будут
50             отправлены броузеру, что вызвовет исключение и, возможно,
51             отображение испорченной страницы посетителю сайта. Некоторые
52             расширенные возможности Zend_Session требуют вызова
53             <code>Zend_Session_Core::start()</code> в начале (больше о
54             расширенных возможностях будет написано позднее).
55 <!--
56             By starting the session in the bootstrap file, you avoid the possibility that your session might be started
57             after headers have been sent to the browser, which results in an exception, and possibly a broken page for
58             website viewers. Various advanced features require <code>Zend_Session_Core::start()</code> first. (More on
59             advanced features later).
60 -->
61         </para>
63         <para>
64             Есть четыре способа стартовать сессию, используя Zend_Session. Два
65             из них - неправильные.
66 <!--            There are four ways to start a session, when using Zend_Session. Two are wrong.
67 -->
68         </para>
70         <itemizedlist mark='opencircle'>
71             <listitem>
72                 <para>
73                     1. Неправильно: Устанавливать опцию session.auto_start в
74                     php.ini или .htaccess (http://www.php.net/manual/en/ref.session.php#ini.session.auto-start).
75                     Если вы не имеете возможность отключить эту опцию в php.ini,
76                     то, если используется mod_php (или его эквивалент) и в
77                     php.ini уже установлена эта опция, добавьте строку
78                     <code>php_value session.auto_start 0</code> в ваш файл
79                     .htaccess (обычно находится в корневой директории для
80                     HTML-документов).
81 <!--
82                     1. Wrong: Do not set PHP's session.auto_start ini setting in either php.ini or .htaccess
83                     (http://www.php.net/manual/en/ref.session.php#ini.session.auto-start). If you do not have the
84                     ability to disable this setting in php.ini, you are using mod_php (or equivalent), and the setting
85                     is already enabled in php.ini, then add <code>php_value session.auto_start 0</code> to your
86                     .htaccess file (usually in your HTML document root directory).
87 -->
88                 </para>
89             </listitem>
90             <listitem>
91                 <para>
92                     2. Неправильно: Непосредственно использовать функцию
93                     <ulink url="http://www.php.net/session_start"><code>session_start()</code></ulink>.
94                     Если вы вызываете <code>session_start()</code> напрямую и
95                     начинаете использование Zend_Session_Namespace, то при
96                     вызове метода <code>Zend_Session::start()</code> будет
97                     сгенерировано исключение ("session has already been
98                     started"). Если вы вызываете <code>session_start()</code>
99                     после использования Zend_Session_Namespace или явного вызова
100                     <code>Zend_Session::start()</code>, то будет сгенерирована
101                     ошибка уровня E_NOTICE и проигнорирован вызов функции.
102 <!--
103                     2. Wrong: Do not use PHP's <code>
104                     <ulink url="http://www.php.net/session_start">session_start()</ulink>
105                     </code> function directly. If you use <code>session_start()</code> directly, and then start using
106                     Zend_Session_Namespace, an exception will be thrown by <code>Zend_Session::start()</code> ("session
107                     has already been started"). If you call <code>session_start()</code>, after using
108                     Zend_Session_Namespace or starting <code>Zend_Session::start()</code> explicitly, an error of level
109                     E_NOTICE will be generated, and the call will be ignored.
111                 </para>
112             </listitem>
113             <listitem>
114                 <para>
115                     3. Правильно:
116                     Используйте <code>Zend_Session::start()</code>. Если
117                     необходимо, чтобы все запросы имели и использовали сессии,
118                     то поместите вызов этой функции в коде загрузки близко к
119                     точке входа и без условной логики. При этом присутствуют
120                     некоторые издержки за счет сессий. Если для одних запросов
121                     нужны сессии, а для других - нет, то:
122 <!--
123                     3. Correct: Use <code>Zend_Session::start()</code>. If you want all requests to have and use
124                     sessions, then place this function call early and unconditionally in your ZF bootstrap code.
125                     Sessions have some overhead. If some requests need sessions, but other requests will not need to use
126                     sessions, then:
128                 </para>
129                 <itemizedlist mark='opencircle'>
130                     <listitem>
131                         <para>
132                             Установите опцию <code>strict</code> в true (см.
133                             <link linkend="zend.session.startingasession"><code>Zend_Session::setOptions()</code></link>) в коде загрузки.
134 <!--
135                             Unconditionally, set the <code>strict</code> option to true (see
136                             <link
137                         linkend="zend.session.startingasession"><code>Zend_Session::setOptions()</code>
138                             </link>
139                             ) in your userland bootstrap.
141                         </para>
142                     </listitem>
143                     <listitem>
144                         <para>
145                             Вызывайте <code>Zend_Session::start()</code>
146                             только при тех запросах, для которых нужны сессии,
147                             и до того, как будет произведен первый вызов
148                             <code>new Zend_Session_Namespace()</code>.
149 <!--
150                             Call <code>Zend_Session::start()</code>, only for requests that need to use sessions, before
151                             the first call to <code>new Zend_Session_Namespace()</code>.
153                         </para>
154                     </listitem>
155                     <listitem>
156                         <para>
157                             Используйте
158                             <code>new Zend_Session_Namespace()</code> как
159                             обычно и там, где это нужно, но при этом необходимо
160                             убедиться, что
161                             <code>Zend_Session::start()</code> был вызван ранее.
162 <!--
163                             Use <code>new Zend_Session_Namespace()</code> normally, where needed, but make sure
164                             <code>Zend_Session::start()</code> has been called previously.
166                         </para>
167                     </listitem>
168                 </itemizedlist>
169                 <para>
170                     Опция <code>strict</code> предотвращает автоматический
171                     старт сессии с использованием
172                     <code>Zend_Session::start()</code> при вызове
173                     <code>new Zend_Session_Namespace()</code>. Эта опция
174                     помогает разработчикам пользовательских областей приложений
175                     ZF следовать принятому при проектировании решению не
176                     использовать сессии для определенных запросов, т.к. при
177                     установке этой опции и последующем инстанцировании
178                     Zend_Session_Namespace до явного вызова
179                     <code>Zend_Session::start()</code> будет сгенерировано
180                     исключение. Не используйте эту опцию в коде библиотек ZF,
181                     поскольку проектные решения должны принимать только
182                     разработчики пользовательской области. Аналогичным образом,
183                     все разработчики "библиотек" должны осторожно подходить
184                     к использованию <code>Zend_Session::setOptions()</code>
185                     в коде их библиотек, поскольку эти опции имеют глобальную
186                     область действия (как и лежащие в основе опции расширения
187                     ext/session).
188 <!--
189                     The <code>strict</code> option prevents <code>new Zend_Session_Namespace()</code> from automatically
190                     starting the session using <code>Zend_Session::start()</code>. Thus, this option helps developers of
191                     userland ZF applications enforce a design decision to avoid using sessions for certain requests,
192                     since an error will be thrown when using this option and instantiating Zend_Session_Namespace,
193                     before an explicit call to <code>Zend_Session::start()</code>. Do not use this option in ZF core
194                     library code, because only userland developers should make this design choice. Similarly, all
195                     "library" developers should carefully consider the impact of using
196                     <code>Zend_Session::setOptions()</code> on users of their library code, since these options have
197                     global side-effects (as do the underlying options for ext/session).
199                 </para>
200             </listitem>
201             <listitem>
202                 <para>
203                     4. Правильно: Просто используйте
204                     <code>new Zend_Session_Namespace()</code> где необходимо, и
205                     сессия будет автоматически запущена в Zend_Session. Это
206                     наиболее простой вариант использования, подходящий для
207                     большинства случаев. Но необходимо будет следить за тем,
208                     чтобы первый вызов
209                     <code>new Zend_Session_Namespace()()</code> всегда
210                     происходил <emphasis>до того</emphasis>, как
211                     выходные данные будут отправлены клиенту (т.е. до того, как
212                     агенту пользователя будут отправлены HTTP-заголовки),
213                     если используются основанные на куках сессии
214                     (очень рекомендуется). Использование
215                     <ulink url="http://php.net/outcontrol">буферизации
216                     вывода</ulink> может быть удачным решением, при этом может
217                     быть улучшена производительность. Например, в
218                     <code>php.ini</code>
219                     "<code>output_buffering = 65535</code>" включает буферизацию
220                     вывода с размером буфера 64K.
221 <!--
222                     4. Correct: Just use <code>new Zend_Session_Namespace()</code> whenever needed, and the session will
223                     be automatically started within Zend_Session. This offers extremely simple usage that works well in
224                     most situations. However, you then become responsible for ensuring that the first <code>new
225                     Zend_Session_Namespace()</code> happens <emphasis>before</emphasis> any output (i.e.
226                     <ulink url="http://www.php.net/headers_sent">HTTP headers</ulink>
227                     ) has been sent by PHP to the client, if you are using the default, cookie-based sessions (strongly
228                     recommended). Using
229                     <ulink url="http://php.net/outcontrol">output buffering</ulink>
230                     often is sufficient to prevent this issue and may help improve performance. For example, in
231                     <code>php.ini</code>, "<code>output_buffering = 65535</code>" enables output buffering with a 64K
232                     buffer.
234                 </para>
235             </listitem>
236         </itemizedlist>
238     </sect2>
240     <sect2 id="zend.session.locking">
242         <title>Блокировка пространств имен<!--Locking Session Namespaces--></title>
244         <para>
245             Можно применять блокировку к пространствам имен для предотвращения
246             изменения данных в этом пространстве имен. Используйте
247             метод <code>Zend_Session_Namespace::lock()</code> для того, чтобы
248             сделать определенное пространство имен доступным только для чтения,
249             <code>unLock()</code> - чтобы сделать пространство имен доступным
250             для чтения и изменений, а <code>isLocked()</code> для проверки того,
251             не было ли пространство имен заблокировано ранее. Блокировка не
252             сохраняется от одного запроса к другому. Блокировка пространства
253             имен не действует на методы установки (setter methods) в объектах,
254             сохраненных в пространстве имен, но предотвращает использование
255             методов установки пространства имен сессии для удаления или замены
256             объектов, сохраненных непосредственно в пространстве имен.
257             Также блокирование пространств имен Zend_Session_Namespace не
258             препятствует использованию ссылок на те же данные
259             (см. <ulink url="http://www.php.net/references">PHP references</ulink>).
260 <!--
261             Session namespaces can be locked, to prevent further alterations to the data in that namespace. Use
262             <code>Zend_Session_Namespace's lock()</code> to make a specific namespace read-only, <code>unLock()</code>
263             to make a read-only namespace read-write, and <code>isLocked()</code> to test if a namespace has been
264             previously locked. Locks are transient and do not persist from one request to the next. Locking the
265             namespace has no effect on setter methods of objects stored in the namespace, but does prevent the use of
266             the namespace's setter method to remove or replace objects stored directly in the namespace. Similarly,
267             locking Zend_Session_Namespace namespaces does not prevent the use of symbol table aliases to the same data
268             (see
269             <ulink url="http://www.php.net/references">PHP references</ulink>
270             ).
272         </para>
274         <example>
275             <title>Блокировка пространств имен<!--Locking Session Namespaces--></title>
276 <programlisting language="php">
277 <![CDATA[<?php
278     // assuming:
279     $userProfileNamespace = new Zend_Session_Namespace('userProfileNamespace');
281     // marking session as read only locked
282     $userProfileNamespace->lock();
284     // unlocking read-only lock
285     if ($userProfileNamespace->isLocked()) {
286         $userProfileNamespace->unLock();
287     }
288 ?>]]></programlisting>
289         </example>
291         <para>
292             Есть некоторые идеи по поводу того, как организовывать модели в
293             парадигме MVC для Веб, включая создание моделей представления для
294             использования видами (views). Иногда имеющиеся данные, являются ли
295             они частью вашей доменной модели или нет, являются подходящими для
296             этой задачи. Для того, чтобы предотвратить изменение таких данных,
297             используйте блокировку пространств имен сессий до того, как
298             предоставить видам доступ к этим подмножествам вашей модели
299             представления.
300 <!--
301             There are numerous ideas for how to manage models in MVC paradigms for the Web, including creating
302             presentation models for use by views. Sometimes existing data, whether part of your domain model or not, is
303             adequate for the task. To discourage views from applying any processing logic to alter such data, consider
304             locking session namespaces before permitting views to access this subset of your "presentation" model.
306         </para>
308         <example>
309             <title>Блокировка сессий в видах<!--Locking Sessions in Views--></title>
310 <programlisting language="php">
311 <![CDATA[<?php
312 class FooModule_View extends Zend_View
314     public function show($name)
315     {
316         if (!isset($this->mySessionNamespace)) {
317             $this->mySessionNamespace = Zend::registry('FooModule');
318         }
320         if ($this->mySessionNamespace->isLocked()) {
321             return parent::render($name);
322         }
324         $this->mySessionNamespace->lock();
325         $return = parent::render($name);
326         $this->mySessionNamespace->unLock();
328         return $return;
329     }
331 ?>]]></programlisting>
332         </example>
334     </sect2>
336     <sect2 id="zend.session.expiration">
338         <title>Время жизни пространства имен<!--Namespace Expiration--></title>
340         <para>
341             Время жизни может быть ограничено как у пространства имен в целом,
342             так и у отдельных ключей. Общие случаи использования
343             включают в себя передачу временной информации между запросами
344             и повышение защищенности от определенных угроз безопасности
345             посредством устранения доступа к потенциально чувствительной
346             информации по прошествии некоторого времени после
347             аутентификации. Истечение времени жизни может быть основано на
348             количестве секунд или на концепции "прыжков" (hops), в которой
349             "прыжком" считается каждый успешный запрос, в котором активируется
350             пространство имен через, как минимум, один
351             <varname>$space = new Zend_Session_Namespace('myspace');</varname>.
352 <!--
353             Limits can be placed on the longevity of both namespaces and
354             individual keys in namespaces. Common use cases
355             include passing temporary information between requests, and
356             reducing exposure to certain security risks by
357             removing access to potentially sensitive information some time
358             after authentication occurred. Expiration can
359             be based on elapsed seconds, or based on the concept of "hops",
360             where a hop occurs for each successive
361             request that activates the namespace via at least one
362             <varname>$space = new Zend_Session_Namespace('myspace');</varname>.
364         </para>
366         <example>
367             <title>Примеры установки времени жизни<!--Expiration Examples--></title>
368 <programlisting language="php">
369 <![CDATA[<?php
370 $s = new Zend_Session_Namespace('expireAll');
371 $s->a = 'apple';
372 $s->p = 'pear';
373 $s->o = 'orange';
375 // Время жизни установлено только для ключа "a" (5 секунд)
376 $s->setExpirationSeconds(5, 'a');
378 // Время жизни всего пространства имен - 5 "прыжков"
379 $s->setExpirationHops(5);
381 $s->setExpirationSeconds(60);
382 // Пространство имен "expireAll" будет помечено как с истекшим временем жизни
383 // при первом запросе, произведенном после того, как прошло 60 секунд,
384 // или после 5 "прыжков" - в зависимости от того, что произошло раньше
385 ?>]]></programlisting>
386         </example>
388         <para>
389             При работе с данными, время жизни которых истекает в текущем запросе,
390             будьте внимательны при их извлечении. Несмотря на то, что данные
391             возвращаются по ссылке, изменение этих данных не приведет к их
392             сохранению после текущего запроса. Для "сброса" времени истечения
393             извлеките данные во временные переменные, уничтожьте эти данные в
394             пространстве имен и затем установите соответствующий ключ снова.
395 <!--
396             When working with data expiring from the session in the current
397             request, care should be used when retrieving
398             it. Although the data is returned by reference, modifying the data
399             will not make expiring data persist past
400             the current request. In order to "reset" the expiration time, fetch
401             the data into temporary variables, use
402             the namespace to unset it, and then set the appropriate keys again.
404         </para>
406     </sect2>
408     <sect2 id="zend.session.controllers">
410         <title>Инкапсуляция сессий и контроллеры<!--Session Encapsulation and Controllers--></title>
412         <para>
413             Пространства имен могут также использоваться для разделения доступа
414             контроллеров к сессиям, чтобы защитить переменные от повреждения.
415             Например, контроллер 'Zend_Auth' может хранить свои постоянные
416             данные сессии отдельно от всех остальных контроллеров.
417 <!--
418             Namespaces can also be used to separate session access by controllers to protect variables from
419             contamination. For example, the 'Zend_Auth' controller might keep its session state data separate from all
420             other controllers.
422         </para>
424         <example>
425             <title>Сессии с пространствами имен для контроллеров с автоматическим истечением времени<!--Namespaced Sessions for Controllers with Automatic Expiration--></title>
426 <programlisting language="php">
427 <![CDATA[<?php
428 require_once 'Zend/Session.php';
429 // контроллер для вывода вопроса
430 $testSpace = new Zend_Session_Namespace('testSpace');
431 // установка времени жизни только для этой переменной
432 $testSpace->setExpirationSeconds(300, "accept_answer");
433 $testSpace->accept_answer = true;
437 // контроллер для обработки ответа на вопрос
438 $testSpace = new Zend_Session_Namespace('testSpace');
440 if ($testSpace->accept_answer === true) {
441     // время не истекло
443 else {
444     // время истекло
446 ?>]]></programlisting>
447         </example>
449     </sect2>
451     <sect2 id="zend.session.limitinginstances">
453         <title>Ограничение количества экземпляров Zend_Session_Namespace до одного на каждое пространство имен<!--Limiting Instances of Zend_Session to One Per Namespace--></title>
455         <para>
456             Мы рекомендуем использовать блокировку сессии (см. выше) вместо этой
457             функциональной возможности, которая накладывает дополнительное бремя
458             на разработчика, состоящее в передаче экземпляров
459             Zend_Session_Namespace во все функции и объекты, нуждающихся в
460             использовании этих пространств имен.
461 <!--
462             We recommend using session locking (see above) instead of the feature below, which places extra management
463             burden on the developer to pass any Zend_Session_Namespace instances into whatever functions and objects
464             need access to each namespace.
466         </para>
468         <para>
469             Когда создается первый экземпляр Zend_Session_Namespace, связанный с
470             определенным пространством имен, вы можете дать команду
471             Zend_Session_Namespace больше не создавать объекты для этого
472             пространства имен. Таким образом, в дальнейшем попытка создать
473             экземпляр Zend_Session_Namespace для
474             того же пространства имен вызовет генерацию исключения. Это
475             поведение является опциональным и не принято по умолчанию, но
476             остается доступным для тех, кто предпочитает передавать по коду
477             единственный объект для каждого пространства имен. Это повышает
478             защиту пространства имен от изменений компонентами, которые не
479             должны делать этого, поскольку тогда они не будут иметь свободного
480             доступа к пространствам имен. Тем не менее, ограничение пространства
481             имен до одного экземпляра модет привести к большему объему кода или
482             к его усложнению, поскольку он отменяет возможность использования
483             директив вида
484             <varname>$aNamespace = new Zend_Session_Namespace('aNamespace');</varname>
485             после того, как был создан первый экземпляр. Это продемонстрировано
486             в примере ниже:
487 <!--
488             When constructing the first instance of Zend_Session_Namespace attached to a specific namespace, you can
489             also instruct Zend_Session_Namespace to not make any more instances for that namespace. Thus, any future
490             attempts to construct a Zend_Session_Namespace instance having the same namespace will throw an error. Such
491             behavior is optional, and not the default behavior, but remains available to those who prefer to pass around
492             a single instance object for each namespace. This increases protection from changes by components that
493             should not modify a particular session namespace, because they won't have easy access. However, limiting a
494             namespace to a single instance may lead to more code or more complex code, as it removes access to the
495             convient <varname>$aNamespace = new Zend_Session_Namespace('aNamespace');</varname>, after the first intance has
496             been created, as follows in the example below:
498         </para>
500         <example>
501             <title>Ограничение до единичных экземпляров<!--Limiting to Single Instances--></title>
502 <programlisting language="php">
503 <![CDATA[<?php
504     require_once 'Zend/Session.php';
505     $authSpaceAccessor1 = new Zend_Session_Namespace('Zend_Auth');
506     $authSpaceAccessor2 = new Zend_Session_Namespace('Zend_Auth', Zend_Session_Namespace::SINGLE_INSTANCE);
507     $authSpaceAccessor1->foo = 'bar';
508     assert($authSpaceAccessor2->foo, 'bar');
509     doSomething($options, $authSpaceAccessor2);
510     .
511     .
512     .
513     $aNamespaceObject = new Zend_Session_Namespace('Zend_Auth'); // это вызовет ошибку
514 ?>]]></programlisting>
515         </example>
517         <para>
518             Второй параметр в конструкторе выше говорит Zend_Session, что
519             в будущем создание любых других экземпляров Zend_Session_Namespace с
520             пространством имен 'Zend_Auth' не допустимо. Поскольку
521             директиву <code>new Zend_Session_Namespace('Zend_Auth')</code>
522             нельзя использовать после того, как будет выполнен приведенный выше
523             код, то разработчику нужно будет где-либо сохранять объект
524             (<varname>$authSpaceAccessor2</varname> в
525             примере выше), если в дальнейшем при обработке того же запроса
526             необходим доступ к этому пространству имен сессии.
527             Например, вы можете сохранять экземпляр в статической переменной или
528             передавать его другим методам, которым нужен доступ к данному
529             пространству имен.
530 <!--
531             The second parameter in the constructor above will tell Zend_Session_Namespace that any future
532             Zend_Session's that are instantiated with the 'Zend_Auth' namespace are not allowed, and will thus cause an
533             exception. Since <code>new Zend_Session_Namespace('Zend_Auth')</code> will not be allowed after the code
534             above has been executed, the developer becomes responsible for storing the instance object
535             (<varname>$authSpaceAccessor2</varname> in the example above) somewhere, if access to this session namespace is
536             needed at a later time during the same request. For example, a developer may store the instance in a static
537             variable, or pass it to other methods that might need access to this session namespace. Session locking (see
538             above) provides a more convenient, and less burdensome approach to limiting access to namespaces.
540         </para>
542     </sect2>
544     <sect2 id="zend.session.modifyingarray">
546         <title>Работа с массивами в пространствах имен<!--Working with Arrays in Namespaces--></title>
548         <para>
549             Изменение массива внутри пространства имен невозможно. Простейшим
550             решением является сохранение массивов после того, как все желаемые
551             значения были установлены. <ulink url="http://framework.zend.com/issues/browse/ZF-800">ZF-800</ulink>
552             подтверждает известный баг, затрагивающий многие PHP-приложения,
553             использующие "магические" методы и массивы.
555 <!--
556             Modifying an array inside a namespace does not work. The simplest solution is to store arrays after all
557             desired values have been set.
558             <ulink url="http://framework.zend.com/issues/browse/ZF-800">ZF-800</ulink>
559             documents a known issue affecting many PHP applications using magic methods and arrays.
561         </para>
563         <example>
564             <title>Известные проблемы с массивами<!--Known problem with arrays--></title>
565 <programlisting language="php">
566 <![CDATA[<?php
567     $sessionNamespace = new Zend_Session_Namespace('Foo');
568     $sessionNamespace->array = array();
569     $sessionNamespace->array['testKey'] = 1; // Не работает в версиях ниже PHP 5.2.1
570 ?>]]></programlisting>
571         </example>
573         <para>
574             Если вам нужно изменить массив после того, как добавили его в
575             пространство имен, извлеките массив, произведите необходимые
576             изменения и сохраните его под тем же ключом в пространстве имен.
577 <!--
578             If you need to modify the array after assigning it to a session namespace key, fetch the array, then
579             modify it and save the array back to the session namespace.
581         </para>
583         <example>
584             <title>Обходной путь: извлечение, изменение и сохранение<!--Workaround: fetch, modify, save--></title>
585 <programlisting language="php">
586 <![CDATA[<?php
587     $sessionNamespace = new Zend_Session_Namespace('Foo');
588     $sessionNamespace->array = array('tree' => 'apple');
589     $tmp = $sessionNamespace->array;
590     $tmp['fruit'] = 'peach';
591     $sessionNamespace->array = $tmp;
592 ?>]]></programlisting>
593         </example>
595         <para>
596             Можно также сохранить массив, содержащий ссылку на желаемый массив
597             и косвенно работать с ним.
598 <!--
599             Alternatively, store an array containing a reference to the desired array, and then access it indirectly.
601         </para>
603         <example>
604             <title>Обходной путь: сохранение массива, содержащего ссылку<!--Workaround: store array containing reference--></title>
605 <programlisting language="php">
606 <![CDATA[<?php
607     $myNamespace = new Zend_Session_Namespace('mySpace');
609     // работает даже с версиями PHP, содержащими баг
610     $a = array(1,2,3);
611     $myNamespace->someArray = array( & $a ) ;
612     $a['foo'] = 'bar';
613 ?>]]></programlisting>
614         </example>
616     </sect2>
618     <sect2 id="zend.session.auth">
620         <title>Использование сессий вместе с аутентификацией<!--Using Sessions with Authentication--></title>
622         <para>
623             Если ваш адаптер аутентификации для <code>Zend_Auth</code>
624             возвращает результат, в котором идетификатором авторизации является
625             объект (не рекомендуется) вместо массива, то выполняйте проверку
626             класса идентификатора авторизации до того, как стартовать сессию.
627             Вместо этого мы рекомендуем хранить идентификаторы авторизации,
628             вычисленные в адаптере авторизации, под хорошо известным ключом в
629             пространстве имен сессии. Например, по умолчанию
630             <code>Zend_Auth</code> размещает идентификаторы под ключом 'storage'
631             пространства имен 'Zend_Auth'.
632 <!--
633             If your authentication adapter for <code>Zend_Auth</code> returns a result where the authorization identity
634             is an object (not recommended), instead of an array, then make sure to require your authorization identity
635             class definition before starting the session. Instead, we recommend storing the authorization ids computed
636             within an authentication adapter inside a well-known key in a session namespace. For example, the default
637             behavior of <code>Zend_Auth</code> places this in the 'storage' key of the 'Zend_Auth' namespace.
639         </para>
641         <para>
642             Если вы приказали <code>Zend_Auth</code> не сохранять метку сессии в
643             сессиях, то можете вручную сохранять ID авторизации под хорошо
644             известным ключом в любом пространстве имен сессии.
645             Часто приложения имеют свои
646             требования к тому, где хранить "мандат" (учетная запись с
647             праметрами доступа пользователя) и идентификатор авторизации.
648             Приложения часто устанавливают соответствие идентификаторов
649             аутентификации (например, имена пользователей) и идентификаторов
650             авторизации (например, присвоенное уникальное целое число) во время
651             аутентификации, которая должна производится внутри метода
652             <code>authenticate()</code> адаптера аутентификации Zend_Auth.
653 <!--
654             If you tell <code>Zend_Auth</code> to not persist authentication tokens in sessions, then you can manually
655             store the authorization id in the session namespace, in a well-known location in a session namespace of your
656             choice. Often, applications have specific needs about where to store credentials used (if any) and
657             "authorization" identity. Applications often map authentication identities (e.g. usernames) to authorization
658             identities (e.g. a uniquely assigned integer) during authentication, which would occur in the Zend_Auth
659             authentication adapter's <code>authenticate()</code> method.
661         </para>
663         <example>
664             <title>Пример: Простой доступ к ID авторизации<!--Example: Simplified access of authorization ids--></title>
665 <programlisting language="php">
666 <![CDATA[<?php
667     // pre-authentication request
668     require_once 'Zend/Auth/Adapter/Digest.php';
669     $adapter = new Zend_Auth_Adapter_Digest($filename, $realm, $username, $password);
670     $result = $adapter->authenticate();
671     require_once 'Zend/Session/Namespace.php';
672     $namespace = new Zend_Session_Namespace('Zend_Auth');
673     if ($result->isValid()) {
674         $namespace->authorizationId = $result->getIdentity();
675         $namespace->date = time();
676     } else {
677         $namespace->attempts++;
678     }
680     // subsequent requests
681     require_once 'Zend/Session.php';
682     Zend_Session::start();
683     $namespace = new Zend_Session_Namespace('Zend_Auth');
685     echo "Valid: ", (empty($namespace->authorizationId) ? 'No' : 'Yes'), "\n"';
686     echo "Authorization / user Id: ", (empty($namespace->authorizationId)
687         ? 'none' : print_r($namespace->authorizationId, true)), "\n"';
688     echo "Authentication attempts: ", (empty($namespace->attempts)
689         ? '0' : $namespace->attempts), "\n"';
690     echo "Authenticated on: ",
691         (empty($namespace->date) ? 'No' : date(DATE_ATOM, $namespace->date), "\n"';
692 ?>]]></programlisting>
693         </example>
695         <para>
696             Идентификаторы авторизации, хранящиеся на клиентской стороне, могут
697             использоваться в атаках на поднятие привилегий, если им доверяет
698             серверная сторона и если они, например, не дублируются на серверной
699             стороне (например, в данных сессии) и затем сверяются с
700             идентификатором авторизации, предоставленным клентом для
701             действующией сессии. Мы различаем понятия "идентификаторов
702             аутентификации" (например, имена пользователей) и "идентификаторов
703             авторизации" (например, ID пользователя #101 в таблице БД для
704             пользователей).
705 <!--
706             Authorization ids stored client-side are subject to privilege escalation vulnerabilities, if these ids are
707             used and trusted by the server, unless, for example, the id is duplicated on the server-side (e.g. in the
708             session) and then cross-checked with the authorization id claimed by the client for the in-effect session.
709             We are differentiating between "authentication ids" (e.g. usernames) and "authorization ids" (e.g. user id
710             #101 in the users DB table).
712         </para>
714         <para>
715             Последнее часто используется для повышения производительности -
716             например, для выборки из пула серверов, кеширующих данные сессии,
717             чтобы решить проблему "курицы и яйца". Часто
718             возникают дебаты о том, использовать ли настоящий ID авторизации в
719             куках или некую замену, которая помогает установить соответствие
720             с настоящим ID авторизации (или сессии сервера(ов), хранящего
721             сессию/профиль пользователя и т.д.), в то время как некоторые
722             архитекторы системной безопасности предпочитают избегать
723             публикования истинных значений первичных ключей, пытаясь достичь
724             некоторого дополнительного уровня защиты в случае наличия
725             уязвимостей к SQL-инъекциям.
726 <!--
727             The latter is not uncommon for performance reasons, such as helping select from a pool of servers caching
728             session information to help solve chicken-and-egg problems. Often debates ensue about whether to use the
729             real authorization id in the cookie, or some substitute that aids in mapping to the real authorization id
730             (or session or server(s) holding the user's session/profile, etc.), as some system security architects wish
731             to prevent true "DB primary keys" from escaping into the wild. These architects try and obtain some level of
732             protection by obfuscation in the event of a SQL injection vulnerability in their system. Not everyone uses
733             auto-increment strategies for authorization ids.
735         </para>
737     </sect2>
739     <sect2 id="zend.session.testing">
741         <title>Использование сессий с юнит-тестами<!--Using Sessions with Unit Tests--></title>
743         <para>
744             Zend Framework использует PHPUnit для своего тестирования. Многие
745             разработчики расширяют существующие наборы
746             юнит-тестов для покрытия кода в своих приложениях.
747             Если при выполнении юнит-тестирований после завершения сессии были
748             использованы любые связанные с записью методы, то генерируется
749             исключение "<emphasis>Zend_Session is currently marked
750             as read-only</emphasis>" ("Zend_Session помечен как доступный только
751             для чтения"). Тем не менее, юнит-тесты, использующие Zend_Session,
752             требуют особого внимания в разработке, поскольку закрытие
753             (<code>Zend_Session::writeClose()</code>) или уничтожение сессии
754             (<code>Zend_Session::destroy()</code>) не дает впоследствии
755             устанавливать или сбрасывать ключи в любом объекте
756             Zend_Session_Namespace. Это поведение является прямым следствием
757             использования лежащего в основе расширения ext/session,
758             функций <code>session_destroy()</code> и
759             <code>session_write_close()</code>, которые не имеют механизма
760             "отмены" для облегчения установки/демонтажа в юнит-тестировании.
761 <!--
762             Zend Framework relies on PHPUnit to facilitate testing of itself. Many developers extend the existing
763             suite of unit tests to cover the code in their applications. The exception
764             "<emphasis>Zend_Session is currently marked as read-only</emphasis>" is thrown while
765             performing unit tests, if any write-related methods are used after ending the session. However, unit tests
766             using Zend_Session require extra attention, because closing (<code>Zend_Session::writeClose()</code>), or
767             destroying a session (<code>Zend_Session::destroy()</code>) prevents any further setting or unsetting of
768             keys in any Zend_Session_Namespace. This behavior is a direct result of the underlying ext/session mechanism
769             and PHP's <code>session_destroy()</code> and <code>session_write_close()</code>, which has no "undo"
770             mechanism to facilitate setup/teardown with unit tests.
772         </para>
774         <para>
775             Чтобы обойти это, см. юнит-тест
776             <code>testSetExpirationSeconds()</code> в
777             <code>tests/Zend/Session/SessionTest.php</code> и
778             <code>SessionTestHelper.php</code>, которые используют
779             <code>exec()</code> для запуска отдельного процесса. Новый процесс
780             более точно имитирует второй, последующий, запрос из броузера.
781             Отдельный процесс начинается с "чистой" сессии, так же, как при
782             выполнении любого PHP-скрипта для веб-запроса. Кроме этого,
783             любые изменения в <varname>$_SESSION[]</varname>, произведенные при вызове
784             процесса, становятся доступными и в дочернем процессе, что дает
785             родительскому процессу возможность закрыть сессию до использования
786             <code>exec()</code>.
787 <!--
788             To work around this, see the unit test <code>testSetExpirationSeconds()</code> in
789             <code>tests/Zend/Session/SessionTest.php and SessionTestHelper.php</code>, which make use of PHP's
790             <code>exec()</code> to launch a separate process. The new process more accurately simulates a second,
791             successive request from a browser. The separate process begins with a "clean" session, just like any PHP
792             script execution for a web request. Also, any changes to <varname>$_SESSION[]</varname> made in the calling
793             process become available to the child process, provided the parent closed the session before using
794             <code>exec()</code>
796         </para>
798         <example>
799             <title>Использование PHPUnit для тестирования кода, написанного с использованием Zend_Session*<!--Using PHPUnit to test code written using Zend_Session*--></title>
800 <programlisting language="php">
801 <![CDATA[<?php
802         // testing setExpirationSeconds()
803         require 'tests/Zend/Session/SessionTestHelper.php'; // also see SessionTest.php in trunk/
804         $script = 'SessionTestHelper.php';
805         $s = new Zend_Session_Namespace('space');
806         $s->a = 'apple';
807         $s->o = 'orange';
808         $s->setExpirationSeconds(5);
810         Zend_Session::regenerateId();
811         $id = Zend_Session::getId();
812         session_write_close(); // release session so process below can use it
813         sleep(4); // not long enough for things to expire
814         exec($script . "expireAll $id expireAll", $result);
815         $result = $this->sortResult($result);
816         $expect = ';a === apple;o === orange;p === pear';
817         $this->assertTrue($result === $expect,
818             "iteration over default Zend_Session namespace failed; expecting result === '$expect', but got '$result'");
820         sleep(2); // long enough for things to expire (total of 6 seconds waiting, but expires in 5)
821         exec($script . "expireAll $id expireAll", $result);
822         $result = array_pop($result);
823         $this->assertTrue($result === '',
824             "iteration over default Zend_Session namespace failed; expecting result === '', but got '$result')");
825         session_start(); // resume artificially suspended session
827         // We could split this into a separate test, but actually, if anything leftover from above
828         // contaminates the tests below, that is also a bug that we want to know about.
829         $s = new Zend_Session_Namespace('expireGuava');
830         $s->setExpirationSeconds(5, 'g'); // now try to expire only 1 of the keys in the namespace
831         $s->g = 'guava';
832         $s->p = 'peach';
833         $s->p = 'plum';
835         session_write_close(); // release session so process below can use it
836         sleep(6); // not long enough for things to expire
837         exec($script . "expireAll $id expireGuava", $result);
838         $result = $this->sortResult($result);
839         session_start(); // resume artificially suspended session
840         $this->assertTrue($result === ';p === plum',
841             "iteration over named Zend_Session namespace failed (result=$result)");
842 ?>]]></programlisting>
843         </example>
845     </sect2>
847 </sect1>