1 <sect1 id="zend.session.advancedusage">
3 <title>Расширенное использование<!--Advanced Usage--></title>
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.
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.
23 <sect2 id="zend.session.startingasession">
25 <title>Старт сессии<!--Starting a Session--></title>
28 Если вы хотите, чтобы все запросы имели сессии и использовали
29 сессии Zend Framework, то стартуйте сессию в файле загрузки:
31 If you want all requests to have a session and use Zend Framework sessions, then start the session in the
37 <title>Старт общей сессии<!--Starting the Global Session--></title>
38 <programlisting language="php">
41 require_once 'Zend/Session.php';
42 Zend_Session::start();
44 ?>]]></programlisting>
48 Стартуя сессию в файле загрузки, вы исключаете вероятность того, что
49 старт сессии произойдет после того, как заголовки будут
50 отправлены броузеру, что вызвовет исключение и, возможно,
51 отображение испорченной страницы посетителю сайта. Некоторые
52 расширенные возможности Zend_Session требуют вызова
53 <code>Zend_Session_Core::start()</code> в начале (больше о
54 расширенных возможностях будет написано позднее).
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).
64 Есть четыре способа стартовать сессию, используя Zend_Session. Два
65 из них - неправильные.
66 <!-- There are four ways to start a session, when using Zend_Session. Two are wrong.
70 <itemizedlist mark='opencircle'>
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 (обычно находится в корневой директории для
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).
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 и проигнорирован вызов функции.
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.
116 Используйте <code>Zend_Session::start()</code>. Если
117 необходимо, чтобы все запросы имели и использовали сессии,
118 то поместите вызов этой функции в коде загрузки близко к
119 точке входа и без условной логики. При этом присутствуют
120 некоторые издержки за счет сессий. Если для одних запросов
121 нужны сессии, а для других - нет, то:
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
129 <itemizedlist mark='opencircle'>
132 Установите опцию <code>strict</code> в true (см.
133 <link linkend="zend.session.startingasession"><code>Zend_Session::setOptions()</code></link>) в коде загрузки.
135 Unconditionally, set the <code>strict</code> option to true (see
137 linkend="zend.session.startingasession"><code>Zend_Session::setOptions()</code>
139 ) in your userland bootstrap.
145 Вызывайте <code>Zend_Session::start()</code>
146 только при тех запросах, для которых нужны сессии,
147 и до того, как будет произведен первый вызов
148 <code>new Zend_Session_Namespace()</code>.
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>.
158 <code>new Zend_Session_Namespace()</code> как
159 обычно и там, где это нужно, но при этом необходимо
161 <code>Zend_Session::start()</code> был вызван ранее.
163 Use <code>new Zend_Session_Namespace()</code> normally, where needed, but make sure
164 <code>Zend_Session::start()</code> has been called previously.
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 область действия (как и лежащие в основе опции расширения
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).
203 4. Правильно: Просто используйте
204 <code>new Zend_Session_Namespace()</code> где необходимо, и
205 сессия будет автоматически запущена в Zend_Session. Это
206 наиболее простой вариант использования, подходящий для
207 большинства случаев. Но необходимо будет следить за тем,
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 быть улучшена производительность. Например, в
219 "<code>output_buffering = 65535</code>" включает буферизацию
220 вывода с размером буфера 64K.
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
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
240 <sect2 id="zend.session.locking">
242 <title>Блокировка пространств имен<!--Locking Session Namespaces--></title>
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>).
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
269 <ulink url="http://www.php.net/references">PHP references</ulink>
275 <title>Блокировка пространств имен<!--Locking Session Namespaces--></title>
276 <programlisting language="php">
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();
288 ?>]]></programlisting>
292 Есть некоторые идеи по поводу того, как организовывать модели в
293 парадигме MVC для Веб, включая создание моделей представления для
294 использования видами (views). Иногда имеющиеся данные, являются ли
295 они частью вашей доменной модели или нет, являются подходящими для
296 этой задачи. Для того, чтобы предотвратить изменение таких данных,
297 используйте блокировку пространств имен сессий до того, как
298 предоставить видам доступ к этим подмножествам вашей модели
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.
309 <title>Блокировка сессий в видах<!--Locking Sessions in Views--></title>
310 <programlisting language="php">
312 class FooModule_View extends Zend_View
314 public function show($name)
316 if (!isset($this->mySessionNamespace)) {
317 $this->mySessionNamespace = Zend::registry('FooModule');
320 if ($this->mySessionNamespace->isLocked()) {
321 return parent::render($name);
324 $this->mySessionNamespace->lock();
325 $return = parent::render($name);
326 $this->mySessionNamespace->unLock();
331 ?>]]></programlisting>
336 <sect2 id="zend.session.expiration">
338 <title>Время жизни пространства имен<!--Namespace Expiration--></title>
341 Время жизни может быть ограничено как у пространства имен в целом,
342 так и у отдельных ключей. Общие случаи использования
343 включают в себя передачу временной информации между запросами
344 и повышение защищенности от определенных угроз безопасности
345 посредством устранения доступа к потенциально чувствительной
346 информации по прошествии некоторого времени после
347 аутентификации. Истечение времени жизни может быть основано на
348 количестве секунд или на концепции "прыжков" (hops), в которой
349 "прыжком" считается каждый успешный запрос, в котором активируется
350 пространство имен через, как минимум, один
351 <varname>$space = new Zend_Session_Namespace('myspace');</varname>.
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>.
367 <title>Примеры установки времени жизни<!--Expiration Examples--></title>
368 <programlisting language="php">
370 $s = new Zend_Session_Namespace('expireAll');
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>
389 При работе с данными, время жизни которых истекает в текущем запросе,
390 будьте внимательны при их извлечении. Несмотря на то, что данные
391 возвращаются по ссылке, изменение этих данных не приведет к их
392 сохранению после текущего запроса. Для "сброса" времени истечения
393 извлеките данные во временные переменные, уничтожьте эти данные в
394 пространстве имен и затем установите соответствующий ключ снова.
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.
408 <sect2 id="zend.session.controllers">
410 <title>Инкапсуляция сессий и контроллеры<!--Session Encapsulation and Controllers--></title>
413 Пространства имен могут также использоваться для разделения доступа
414 контроллеров к сессиям, чтобы защитить переменные от повреждения.
415 Например, контроллер 'Zend_Auth' может хранить свои постоянные
416 данные сессии отдельно от всех остальных контроллеров.
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
425 <title>Сессии с пространствами имен для контроллеров с автоматическим истечением времени<!--Namespaced Sessions for Controllers with Automatic Expiration--></title>
426 <programlisting language="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) {
446 ?>]]></programlisting>
451 <sect2 id="zend.session.limitinginstances">
453 <title>Ограничение количества экземпляров Zend_Session_Namespace до одного на каждое пространство имен<!--Limiting Instances of Zend_Session to One Per Namespace--></title>
456 Мы рекомендуем использовать блокировку сессии (см. выше) вместо этой
457 функциональной возможности, которая накладывает дополнительное бремя
458 на разработчика, состоящее в передаче экземпляров
459 Zend_Session_Namespace во все функции и объекты, нуждающихся в
460 использовании этих пространств имен.
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.
469 Когда создается первый экземпляр Zend_Session_Namespace, связанный с
470 определенным пространством имен, вы можете дать команду
471 Zend_Session_Namespace больше не создавать объекты для этого
472 пространства имен. Таким образом, в дальнейшем попытка создать
473 экземпляр Zend_Session_Namespace для
474 того же пространства имен вызовет генерацию исключения. Это
475 поведение является опциональным и не принято по умолчанию, но
476 остается доступным для тех, кто предпочитает передавать по коду
477 единственный объект для каждого пространства имен. Это повышает
478 защиту пространства имен от изменений компонентами, которые не
479 должны делать этого, поскольку тогда они не будут иметь свободного
480 доступа к пространствам имен. Тем не менее, ограничение пространства
481 имен до одного экземпляра модет привести к большему объему кода или
482 к его усложнению, поскольку он отменяет возможность использования
484 <varname>$aNamespace = new Zend_Session_Namespace('aNamespace');</varname>
485 после того, как был создан первый экземпляр. Это продемонстрировано
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:
501 <title>Ограничение до единичных экземпляров<!--Limiting to Single Instances--></title>
502 <programlisting language="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);
513 $aNamespaceObject = new Zend_Session_Namespace('Zend_Auth'); // это вызовет ошибку
514 ?>]]></programlisting>
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 передавать его другим методам, которым нужен доступ к данному
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.
544 <sect2 id="zend.session.modifyingarray">
546 <title>Работа с массивами в пространствах имен<!--Working with Arrays in Namespaces--></title>
549 Изменение массива внутри пространства имен невозможно. Простейшим
550 решением является сохранение массивов после того, как все желаемые
551 значения были установлены. <ulink url="http://framework.zend.com/issues/browse/ZF-800">ZF-800</ulink>
552 подтверждает известный баг, затрагивающий многие PHP-приложения,
553 использующие "магические" методы и массивы.
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.
564 <title>Известные проблемы с массивами<!--Known problem with arrays--></title>
565 <programlisting language="php">
567 $sessionNamespace = new Zend_Session_Namespace('Foo');
568 $sessionNamespace->array = array();
569 $sessionNamespace->array['testKey'] = 1; // Не работает в версиях ниже PHP 5.2.1
570 ?>]]></programlisting>
574 Если вам нужно изменить массив после того, как добавили его в
575 пространство имен, извлеките массив, произведите необходимые
576 изменения и сохраните его под тем же ключом в пространстве имен.
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.
584 <title>Обходной путь: извлечение, изменение и сохранение<!--Workaround: fetch, modify, save--></title>
585 <programlisting language="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>
596 Можно также сохранить массив, содержащий ссылку на желаемый массив
597 и косвенно работать с ним.
599 Alternatively, store an array containing a reference to the desired array, and then access it indirectly.
604 <title>Обходной путь: сохранение массива, содержащего ссылку<!--Workaround: store array containing reference--></title>
605 <programlisting language="php">
607 $myNamespace = new Zend_Session_Namespace('mySpace');
609 // работает даже с версиями PHP, содержащими баг
611 $myNamespace->someArray = array( & $a ) ;
613 ?>]]></programlisting>
618 <sect2 id="zend.session.auth">
620 <title>Использование сессий вместе с аутентификацией<!--Using Sessions with Authentication--></title>
623 Если ваш адаптер аутентификации для <code>Zend_Auth</code>
624 возвращает результат, в котором идетификатором авторизации является
625 объект (не рекомендуется) вместо массива, то выполняйте проверку
626 класса идентификатора авторизации до того, как стартовать сессию.
627 Вместо этого мы рекомендуем хранить идентификаторы авторизации,
628 вычисленные в адаптере авторизации, под хорошо известным ключом в
629 пространстве имен сессии. Например, по умолчанию
630 <code>Zend_Auth</code> размещает идентификаторы под ключом 'storage'
631 пространства имен 'Zend_Auth'.
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.
642 Если вы приказали <code>Zend_Auth</code> не сохранять метку сессии в
643 сессиях, то можете вручную сохранять ID авторизации под хорошо
644 известным ключом в любом пространстве имен сессии.
645 Часто приложения имеют свои
646 требования к тому, где хранить "мандат" (учетная запись с
647 праметрами доступа пользователя) и идентификатор авторизации.
648 Приложения часто устанавливают соответствие идентификаторов
649 аутентификации (например, имена пользователей) и идентификаторов
650 авторизации (например, присвоенное уникальное целое число) во время
651 аутентификации, которая должна производится внутри метода
652 <code>authenticate()</code> адаптера аутентификации Zend_Auth.
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.
664 <title>Пример: Простой доступ к ID авторизации<!--Example: Simplified access of authorization ids--></title>
665 <programlisting language="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();
677 $namespace->attempts++;
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>
696 Идентификаторы авторизации, хранящиеся на клиентской стороне, могут
697 использоваться в атаках на поднятие привилегий, если им доверяет
698 серверная сторона и если они, например, не дублируются на серверной
699 стороне (например, в данных сессии) и затем сверяются с
700 идентификатором авторизации, предоставленным клентом для
701 действующией сессии. Мы различаем понятия "идентификаторов
702 аутентификации" (например, имена пользователей) и "идентификаторов
703 авторизации" (например, ID пользователя #101 в таблице БД для
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).
715 Последнее часто используется для повышения производительности -
716 например, для выборки из пула серверов, кеширующих данные сессии,
717 чтобы решить проблему "курицы и яйца". Часто
718 возникают дебаты о том, использовать ли настоящий ID авторизации в
719 куках или некую замену, которая помогает установить соответствие
720 с настоящим ID авторизации (или сессии сервера(ов), хранящего
721 сессию/профиль пользователя и т.д.), в то время как некоторые
722 архитекторы системной безопасности предпочитают избегать
723 публикования истинных значений первичных ключей, пытаясь достичь
724 некоторого дополнительного уровня защиты в случае наличия
725 уязвимостей к SQL-инъекциям.
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.
739 <sect2 id="zend.session.testing">
741 <title>Использование сессий с юнит-тестами<!--Using Sessions with Unit Tests--></title>
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 "отмены" для облегчения установки/демонтажа в юнит-тестировании.
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.
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 родительскому процессу возможность закрыть сессию до использования
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
799 <title>Использование PHPUnit для тестирования кода, написанного с использованием Zend_Session*<!--Using PHPUnit to test code written using Zend_Session*--></title>
800 <programlisting language="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');
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
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>