| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847 |
- <sect1 id="zend.session.advancedusage">
- <title>Расширенное использование<!--Advanced Usage--></title>
- <para>
- Хотя базовое использование является совершенно допустимым вариантом
- использования сессий Zend Framework, стоит рассмотреть другие
- возможности их использования. См.
- <link linkend="zend.auth.introduction.using"> пример на
- <code>Zend_Auth</code></link>, который по умолчанию неявно
- использует Zend_Session_Namespace для сохранения меток аутентификации.
- Этот пример показывает один из способов быстрой и легкой интеграции
- Zend_Session_Namespace и Zend_Auth.
- <!--
- While the basic usage examples are a perfectly acceptable way to utilize Zend Framework sessions, there are some
- best practices to consider. Consider the
- <link linkend="zend.auth.introduction.using"><code>Zend_Auth</code> example</link>
- that transparently uses Zend_Session_Namespace by default to persist authentication tokens. This example shows
- one approach to quickly and easily integrate Zend_Session_Namespace and Zend_Auth.
- -->
- </para>
- <sect2 id="zend.session.startingasession">
- <title>Старт сессии<!--Starting a Session--></title>
- <para>
- Если вы хотите, чтобы все запросы имели сессии и использовали
- сессии Zend Framework, то стартуйте сессию в файле загрузки:
- <!--
- If you want all requests to have a session and use Zend Framework sessions, then start the session in the
- bootstrap file:
- -->
- </para>
- <example>
- <title>Старт общей сессии<!--Starting the Global Session--></title>
- <programlisting language="php">
- <![CDATA[<?php
- ...
- require_once 'Zend/Session.php';
- Zend_Session::start();
- ...
- ?>]]></programlisting>
- </example>
- <para>
- Стартуя сессию в файле загрузки, вы исключаете вероятность того, что
- старт сессии произойдет после того, как заголовки будут
- отправлены броузеру, что вызвовет исключение и, возможно,
- отображение испорченной страницы посетителю сайта. Некоторые
- расширенные возможности Zend_Session требуют вызова
- <code>Zend_Session_Core::start()</code> в начале (больше о
- расширенных возможностях будет написано позднее).
- <!--
- By starting the session in the bootstrap file, you avoid the possibility that your session might be started
- after headers have been sent to the browser, which results in an exception, and possibly a broken page for
- website viewers. Various advanced features require <code>Zend_Session_Core::start()</code> first. (More on
- advanced features later).
- -->
- </para>
- <para>
- Есть четыре способа стартовать сессию, используя Zend_Session. Два
- из них - неправильные.
- <!-- There are four ways to start a session, when using Zend_Session. Two are wrong.
- -->
- </para>
- <itemizedlist mark='opencircle'>
- <listitem>
- <para>
- 1. Неправильно: Устанавливать опцию session.auto_start в
- php.ini или .htaccess (http://www.php.net/manual/en/ref.session.php#ini.session.auto-start).
- Если вы не имеете возможность отключить эту опцию в php.ini,
- то, если используется mod_php (или его эквивалент) и в
- php.ini уже установлена эта опция, добавьте строку
- <code>php_value session.auto_start 0</code> в ваш файл
- .htaccess (обычно находится в корневой директории для
- HTML-документов).
- <!--
- 1. Wrong: Do not set PHP's session.auto_start ini setting in either php.ini or .htaccess
- (http://www.php.net/manual/en/ref.session.php#ini.session.auto-start). If you do not have the
- ability to disable this setting in php.ini, you are using mod_php (or equivalent), and the setting
- is already enabled in php.ini, then add <code>php_value session.auto_start 0</code> to your
- .htaccess file (usually in your HTML document root directory).
- -->
- </para>
- </listitem>
- <listitem>
- <para>
- 2. Неправильно: Непосредственно использовать функцию
- <ulink url="http://www.php.net/session_start"><code>session_start()</code></ulink>.
- Если вы вызываете <code>session_start()</code> напрямую и
- начинаете использование Zend_Session_Namespace, то при
- вызове метода <code>Zend_Session::start()</code> будет
- сгенерировано исключение ("session has already been
- started"). Если вы вызываете <code>session_start()</code>
- после использования Zend_Session_Namespace или явного вызова
- <code>Zend_Session::start()</code>, то будет сгенерирована
- ошибка уровня E_NOTICE и проигнорирован вызов функции.
- <!--
- 2. Wrong: Do not use PHP's <code>
- <ulink url="http://www.php.net/session_start">session_start()</ulink>
- </code> function directly. If you use <code>session_start()</code> directly, and then start using
- Zend_Session_Namespace, an exception will be thrown by <code>Zend_Session::start()</code> ("session
- has already been started"). If you call <code>session_start()</code>, after using
- Zend_Session_Namespace or starting <code>Zend_Session::start()</code> explicitly, an error of level
- E_NOTICE will be generated, and the call will be ignored.
- -->
- </para>
- </listitem>
- <listitem>
- <para>
- 3. Правильно:
- Используйте <code>Zend_Session::start()</code>. Если
- необходимо, чтобы все запросы имели и использовали сессии,
- то поместите вызов этой функции в коде загрузки близко к
- точке входа и без условной логики. При этом присутствуют
- некоторые издержки за счет сессий. Если для одних запросов
- нужны сессии, а для других - нет, то:
- <!--
- 3. Correct: Use <code>Zend_Session::start()</code>. If you want all requests to have and use
- sessions, then place this function call early and unconditionally in your ZF bootstrap code.
- Sessions have some overhead. If some requests need sessions, but other requests will not need to use
- sessions, then:
- -->
- </para>
- <itemizedlist mark='opencircle'>
- <listitem>
- <para>
- Установите опцию <code>strict</code> в true (см.
- <link linkend="zend.session.startingasession"><code>Zend_Session::setOptions()</code></link>) в коде загрузки.
- <!--
- Unconditionally, set the <code>strict</code> option to true (see
- <link
- linkend="zend.session.startingasession"><code>Zend_Session::setOptions()</code>
- </link>
- ) in your userland bootstrap.
- -->
- </para>
- </listitem>
- <listitem>
- <para>
- Вызывайте <code>Zend_Session::start()</code>
- только при тех запросах, для которых нужны сессии,
- и до того, как будет произведен первый вызов
- <code>new Zend_Session_Namespace()</code>.
- <!--
- Call <code>Zend_Session::start()</code>, only for requests that need to use sessions, before
- the first call to <code>new Zend_Session_Namespace()</code>.
- -->
- </para>
- </listitem>
- <listitem>
- <para>
- Используйте
- <code>new Zend_Session_Namespace()</code> как
- обычно и там, где это нужно, но при этом необходимо
- убедиться, что
- <code>Zend_Session::start()</code> был вызван ранее.
- <!--
- Use <code>new Zend_Session_Namespace()</code> normally, where needed, but make sure
- <code>Zend_Session::start()</code> has been called previously.
- -->
- </para>
- </listitem>
- </itemizedlist>
- <para>
- Опция <code>strict</code> предотвращает автоматический
- старт сессии с использованием
- <code>Zend_Session::start()</code> при вызове
- <code>new Zend_Session_Namespace()</code>. Эта опция
- помогает разработчикам пользовательских областей приложений
- ZF следовать принятому при проектировании решению не
- использовать сессии для определенных запросов, т.к. при
- установке этой опции и последующем инстанцировании
- Zend_Session_Namespace до явного вызова
- <code>Zend_Session::start()</code> будет сгенерировано
- исключение. Не используйте эту опцию в коде библиотек ZF,
- поскольку проектные решения должны принимать только
- разработчики пользовательской области. Аналогичным образом,
- все разработчики "библиотек" должны осторожно подходить
- к использованию <code>Zend_Session::setOptions()</code>
- в коде их библиотек, поскольку эти опции имеют глобальную
- область действия (как и лежащие в основе опции расширения
- ext/session).
- <!--
- The <code>strict</code> option prevents <code>new Zend_Session_Namespace()</code> from automatically
- starting the session using <code>Zend_Session::start()</code>. Thus, this option helps developers of
- userland ZF applications enforce a design decision to avoid using sessions for certain requests,
- since an error will be thrown when using this option and instantiating Zend_Session_Namespace,
- before an explicit call to <code>Zend_Session::start()</code>. Do not use this option in ZF core
- library code, because only userland developers should make this design choice. Similarly, all
- "library" developers should carefully consider the impact of using
- <code>Zend_Session::setOptions()</code> on users of their library code, since these options have
- global side-effects (as do the underlying options for ext/session).
- -->
- </para>
- </listitem>
- <listitem>
- <para>
- 4. Правильно: Просто используйте
- <code>new Zend_Session_Namespace()</code> где необходимо, и
- сессия будет автоматически запущена в Zend_Session. Это
- наиболее простой вариант использования, подходящий для
- большинства случаев. Но необходимо будет следить за тем,
- чтобы первый вызов
- <code>new Zend_Session_Namespace()()</code> всегда
- происходил <emphasis>до того</emphasis>, как
- выходные данные будут отправлены клиенту (т.е. до того, как
- агенту пользователя будут отправлены HTTP-заголовки),
- если используются основанные на куках сессии
- (очень рекомендуется). Использование
- <ulink url="http://php.net/outcontrol">буферизации
- вывода</ulink> может быть удачным решением, при этом может
- быть улучшена производительность. Например, в
- <code>php.ini</code>
- "<code>output_buffering = 65535</code>" включает буферизацию
- вывода с размером буфера 64K.
- <!--
- 4. Correct: Just use <code>new Zend_Session_Namespace()</code> whenever needed, and the session will
- be automatically started within Zend_Session. This offers extremely simple usage that works well in
- most situations. However, you then become responsible for ensuring that the first <code>new
- Zend_Session_Namespace()</code> happens <emphasis>before</emphasis> any output (i.e.
- <ulink url="http://www.php.net/headers_sent">HTTP headers</ulink>
- ) has been sent by PHP to the client, if you are using the default, cookie-based sessions (strongly
- recommended). Using
- <ulink url="http://php.net/outcontrol">output buffering</ulink>
- often is sufficient to prevent this issue and may help improve performance. For example, in
- <code>php.ini</code>, "<code>output_buffering = 65535</code>" enables output buffering with a 64K
- buffer.
- -->
- </para>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="zend.session.locking">
- <title>Блокировка пространств имен<!--Locking Session Namespaces--></title>
- <para>
- Можно применять блокировку к пространствам имен для предотвращения
- изменения данных в этом пространстве имен. Используйте
- метод <code>Zend_Session_Namespace::lock()</code> для того, чтобы
- сделать определенное пространство имен доступным только для чтения,
- <code>unLock()</code> - чтобы сделать пространство имен доступным
- для чтения и изменений, а <code>isLocked()</code> для проверки того,
- не было ли пространство имен заблокировано ранее. Блокировка не
- сохраняется от одного запроса к другому. Блокировка пространства
- имен не действует на методы установки (setter methods) в объектах,
- сохраненных в пространстве имен, но предотвращает использование
- методов установки пространства имен сессии для удаления или замены
- объектов, сохраненных непосредственно в пространстве имен.
- Также блокирование пространств имен Zend_Session_Namespace не
- препятствует использованию ссылок на те же данные
- (см. <ulink url="http://www.php.net/references">PHP references</ulink>).
- <!--
- Session namespaces can be locked, to prevent further alterations to the data in that namespace. Use
- <code>Zend_Session_Namespace's lock()</code> to make a specific namespace read-only, <code>unLock()</code>
- to make a read-only namespace read-write, and <code>isLocked()</code> to test if a namespace has been
- previously locked. Locks are transient and do not persist from one request to the next. Locking the
- namespace has no effect on setter methods of objects stored in the namespace, but does prevent the use of
- the namespace's setter method to remove or replace objects stored directly in the namespace. Similarly,
- locking Zend_Session_Namespace namespaces does not prevent the use of symbol table aliases to the same data
- (see
- <ulink url="http://www.php.net/references">PHP references</ulink>
- ).
- -->
- </para>
- <example>
- <title>Блокировка пространств имен<!--Locking Session Namespaces--></title>
- <programlisting language="php">
- <![CDATA[<?php
- // assuming:
- $userProfileNamespace = new Zend_Session_Namespace('userProfileNamespace');
- // marking session as read only locked
- $userProfileNamespace->lock();
- // unlocking read-only lock
- if ($userProfileNamespace->isLocked()) {
- $userProfileNamespace->unLock();
- }
- ?>]]></programlisting>
- </example>
- <para>
- Есть некоторые идеи по поводу того, как организовывать модели в
- парадигме MVC для Веб, включая создание моделей представления для
- использования видами (views). Иногда имеющиеся данные, являются ли
- они частью вашей доменной модели или нет, являются подходящими для
- этой задачи. Для того, чтобы предотвратить изменение таких данных,
- используйте блокировку пространств имен сессий до того, как
- предоставить видам доступ к этим подмножествам вашей модели
- представления.
- <!--
- There are numerous ideas for how to manage models in MVC paradigms for the Web, including creating
- presentation models for use by views. Sometimes existing data, whether part of your domain model or not, is
- adequate for the task. To discourage views from applying any processing logic to alter such data, consider
- locking session namespaces before permitting views to access this subset of your "presentation" model.
- -->
- </para>
- <example>
- <title>Блокировка сессий в видах<!--Locking Sessions in Views--></title>
- <programlisting language="php">
- <![CDATA[<?php
- class FooModule_View extends Zend_View
- {
- public function show($name)
- {
- if (!isset($this->mySessionNamespace)) {
- $this->mySessionNamespace = Zend::registry('FooModule');
- }
- if ($this->mySessionNamespace->isLocked()) {
- return parent::render($name);
- }
- $this->mySessionNamespace->lock();
- $return = parent::render($name);
- $this->mySessionNamespace->unLock();
- return $return;
- }
- }
- ?>]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.session.expiration">
- <title>Время жизни пространства имен<!--Namespace Expiration--></title>
- <para>
- Время жизни может быть ограничено как у пространства имен в целом,
- так и у отдельных ключей. Общие случаи использования
- включают в себя передачу временной информации между запросами
- и повышение защищенности от определенных угроз безопасности
- посредством устранения доступа к потенциально чувствительной
- информации по прошествии некоторого времени после
- аутентификации. Истечение времени жизни может быть основано на
- количестве секунд или на концепции "прыжков" (hops), в которой
- "прыжком" считается каждый успешный запрос, в котором активируется
- пространство имен через, как минимум, один
- <varname>$space = new Zend_Session_Namespace('myspace');</varname>.
- <!--
- Limits can be placed on the longevity of both namespaces and
- individual keys in namespaces. Common use cases
- include passing temporary information between requests, and
- reducing exposure to certain security risks by
- removing access to potentially sensitive information some time
- after authentication occurred. Expiration can
- be based on elapsed seconds, or based on the concept of "hops",
- where a hop occurs for each successive
- request that activates the namespace via at least one
- <varname>$space = new Zend_Session_Namespace('myspace');</varname>.
- -->
- </para>
- <example>
- <title>Примеры установки времени жизни<!--Expiration Examples--></title>
- <programlisting language="php">
- <![CDATA[<?php
- $s = new Zend_Session_Namespace('expireAll');
- $s->a = 'apple';
- $s->p = 'pear';
- $s->o = 'orange';
- // Время жизни установлено только для ключа "a" (5 секунд)
- $s->setExpirationSeconds(5, 'a');
- // Время жизни всего пространства имен - 5 "прыжков"
- $s->setExpirationHops(5);
- $s->setExpirationSeconds(60);
- // Пространство имен "expireAll" будет помечено как с истекшим временем жизни
- // при первом запросе, произведенном после того, как прошло 60 секунд,
- // или после 5 "прыжков" - в зависимости от того, что произошло раньше
- ?>]]></programlisting>
- </example>
- <para>
- При работе с данными, время жизни которых истекает в текущем запросе,
- будьте внимательны при их извлечении. Несмотря на то, что данные
- возвращаются по ссылке, изменение этих данных не приведет к их
- сохранению после текущего запроса. Для "сброса" времени истечения
- извлеките данные во временные переменные, уничтожьте эти данные в
- пространстве имен и затем установите соответствующий ключ снова.
- <!--
- When working with data expiring from the session in the current
- request, care should be used when retrieving
- it. Although the data is returned by reference, modifying the data
- will not make expiring data persist past
- the current request. In order to "reset" the expiration time, fetch
- the data into temporary variables, use
- the namespace to unset it, and then set the appropriate keys again.
- -->
- </para>
- </sect2>
- <sect2 id="zend.session.controllers">
- <title>Инкапсуляция сессий и контроллеры<!--Session Encapsulation and Controllers--></title>
- <para>
- Пространства имен могут также использоваться для разделения доступа
- контроллеров к сессиям, чтобы защитить переменные от повреждения.
- Например, контроллер 'Zend_Auth' может хранить свои постоянные
- данные сессии отдельно от всех остальных контроллеров.
- <!--
- Namespaces can also be used to separate session access by controllers to protect variables from
- contamination. For example, the 'Zend_Auth' controller might keep its session state data separate from all
- other controllers.
- -->
- </para>
- <example>
- <title>Сессии с пространствами имен для контроллеров с автоматическим истечением времени<!--Namespaced Sessions for Controllers with Automatic Expiration--></title>
- <programlisting language="php">
- <![CDATA[<?php
- require_once 'Zend/Session.php';
- // контроллер для вывода вопроса
- $testSpace = new Zend_Session_Namespace('testSpace');
- // установка времени жизни только для этой переменной
- $testSpace->setExpirationSeconds(300, "accept_answer");
- $testSpace->accept_answer = true;
- --
- // контроллер для обработки ответа на вопрос
- $testSpace = new Zend_Session_Namespace('testSpace');
- if ($testSpace->accept_answer === true) {
- // время не истекло
- }
- else {
- // время истекло
- }
- ?>]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.session.limitinginstances">
- <title>Ограничение количества экземпляров Zend_Session_Namespace до одного на каждое пространство имен<!--Limiting Instances of Zend_Session to One Per Namespace--></title>
- <para>
- Мы рекомендуем использовать блокировку сессии (см. выше) вместо этой
- функциональной возможности, которая накладывает дополнительное бремя
- на разработчика, состоящее в передаче экземпляров
- Zend_Session_Namespace во все функции и объекты, нуждающихся в
- использовании этих пространств имен.
- <!--
- We recommend using session locking (see above) instead of the feature below, which places extra management
- burden on the developer to pass any Zend_Session_Namespace instances into whatever functions and objects
- need access to each namespace.
- -->
- </para>
- <para>
- Когда создается первый экземпляр Zend_Session_Namespace, связанный с
- определенным пространством имен, вы можете дать команду
- Zend_Session_Namespace больше не создавать объекты для этого
- пространства имен. Таким образом, в дальнейшем попытка создать
- экземпляр Zend_Session_Namespace для
- того же пространства имен вызовет генерацию исключения. Это
- поведение является опциональным и не принято по умолчанию, но
- остается доступным для тех, кто предпочитает передавать по коду
- единственный объект для каждого пространства имен. Это повышает
- защиту пространства имен от изменений компонентами, которые не
- должны делать этого, поскольку тогда они не будут иметь свободного
- доступа к пространствам имен. Тем не менее, ограничение пространства
- имен до одного экземпляра модет привести к большему объему кода или
- к его усложнению, поскольку он отменяет возможность использования
- директив вида
- <varname>$aNamespace = new Zend_Session_Namespace('aNamespace');</varname>
- после того, как был создан первый экземпляр. Это продемонстрировано
- в примере ниже:
- <!--
- When constructing the first instance of Zend_Session_Namespace attached to a specific namespace, you can
- also instruct Zend_Session_Namespace to not make any more instances for that namespace. Thus, any future
- attempts to construct a Zend_Session_Namespace instance having the same namespace will throw an error. Such
- behavior is optional, and not the default behavior, but remains available to those who prefer to pass around
- a single instance object for each namespace. This increases protection from changes by components that
- should not modify a particular session namespace, because they won't have easy access. However, limiting a
- namespace to a single instance may lead to more code or more complex code, as it removes access to the
- convient <varname>$aNamespace = new Zend_Session_Namespace('aNamespace');</varname>, after the first intance has
- been created, as follows in the example below:
- -->
- </para>
- <example>
- <title>Ограничение до единичных экземпляров<!--Limiting to Single Instances--></title>
- <programlisting language="php">
- <![CDATA[<?php
- require_once 'Zend/Session.php';
- $authSpaceAccessor1 = new Zend_Session_Namespace('Zend_Auth');
- $authSpaceAccessor2 = new Zend_Session_Namespace('Zend_Auth', Zend_Session_Namespace::SINGLE_INSTANCE);
- $authSpaceAccessor1->foo = 'bar';
- assert($authSpaceAccessor2->foo, 'bar');
- doSomething($options, $authSpaceAccessor2);
- .
- .
- .
- $aNamespaceObject = new Zend_Session_Namespace('Zend_Auth'); // это вызовет ошибку
- ?>]]></programlisting>
- </example>
- <para>
- Второй параметр в конструкторе выше говорит Zend_Session, что
- в будущем создание любых других экземпляров Zend_Session_Namespace с
- пространством имен 'Zend_Auth' не допустимо. Поскольку
- директиву <code>new Zend_Session_Namespace('Zend_Auth')</code>
- нельзя использовать после того, как будет выполнен приведенный выше
- код, то разработчику нужно будет где-либо сохранять объект
- (<varname>$authSpaceAccessor2</varname> в
- примере выше), если в дальнейшем при обработке того же запроса
- необходим доступ к этому пространству имен сессии.
- Например, вы можете сохранять экземпляр в статической переменной или
- передавать его другим методам, которым нужен доступ к данному
- пространству имен.
- <!--
- The second parameter in the constructor above will tell Zend_Session_Namespace that any future
- Zend_Session's that are instantiated with the 'Zend_Auth' namespace are not allowed, and will thus cause an
- exception. Since <code>new Zend_Session_Namespace('Zend_Auth')</code> will not be allowed after the code
- above has been executed, the developer becomes responsible for storing the instance object
- (<varname>$authSpaceAccessor2</varname> in the example above) somewhere, if access to this session namespace is
- needed at a later time during the same request. For example, a developer may store the instance in a static
- variable, or pass it to other methods that might need access to this session namespace. Session locking (see
- above) provides a more convenient, and less burdensome approach to limiting access to namespaces.
- -->
- </para>
- </sect2>
- <sect2 id="zend.session.modifyingarray">
- <title>Работа с массивами в пространствах имен<!--Working with Arrays in Namespaces--></title>
- <para>
- Изменение массива внутри пространства имен невозможно. Простейшим
- решением является сохранение массивов после того, как все желаемые
- значения были установлены. <ulink url="http://framework.zend.com/issues/browse/ZF-800">ZF-800</ulink>
- подтверждает известный баг, затрагивающий многие PHP-приложения,
- использующие "магические" методы и массивы.
- <!--
- Modifying an array inside a namespace does not work. The simplest solution is to store arrays after all
- desired values have been set.
- <ulink url="http://framework.zend.com/issues/browse/ZF-800">ZF-800</ulink>
- documents a known issue affecting many PHP applications using magic methods and arrays.
- -->
- </para>
- <example>
- <title>Известные проблемы с массивами<!--Known problem with arrays--></title>
- <programlisting language="php">
- <![CDATA[<?php
- $sessionNamespace = new Zend_Session_Namespace('Foo');
- $sessionNamespace->array = array();
- $sessionNamespace->array['testKey'] = 1; // Не работает в версиях ниже PHP 5.2.1
- ?>]]></programlisting>
- </example>
- <para>
- Если вам нужно изменить массив после того, как добавили его в
- пространство имен, извлеките массив, произведите необходимые
- изменения и сохраните его под тем же ключом в пространстве имен.
- <!--
- If you need to modify the array after assigning it to a session namespace key, fetch the array, then
- modify it and save the array back to the session namespace.
- -->
- </para>
- <example>
- <title>Обходной путь: извлечение, изменение и сохранение<!--Workaround: fetch, modify, save--></title>
- <programlisting language="php">
- <![CDATA[<?php
- $sessionNamespace = new Zend_Session_Namespace('Foo');
- $sessionNamespace->array = array('tree' => 'apple');
- $tmp = $sessionNamespace->array;
- $tmp['fruit'] = 'peach';
- $sessionNamespace->array = $tmp;
- ?>]]></programlisting>
- </example>
- <para>
- Можно также сохранить массив, содержащий ссылку на желаемый массив
- и косвенно работать с ним.
- <!--
- Alternatively, store an array containing a reference to the desired array, and then access it indirectly.
- -->
- </para>
- <example>
- <title>Обходной путь: сохранение массива, содержащего ссылку<!--Workaround: store array containing reference--></title>
- <programlisting language="php">
- <![CDATA[<?php
- $myNamespace = new Zend_Session_Namespace('mySpace');
- // работает даже с версиями PHP, содержащими баг
- $a = array(1,2,3);
- $myNamespace->someArray = array( & $a ) ;
- $a['foo'] = 'bar';
- ?>]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.session.auth">
- <title>Использование сессий вместе с аутентификацией<!--Using Sessions with Authentication--></title>
- <para>
- Если ваш адаптер аутентификации для <code>Zend_Auth</code>
- возвращает результат, в котором идетификатором авторизации является
- объект (не рекомендуется) вместо массива, то выполняйте проверку
- класса идентификатора авторизации до того, как стартовать сессию.
- Вместо этого мы рекомендуем хранить идентификаторы авторизации,
- вычисленные в адаптере авторизации, под хорошо известным ключом в
- пространстве имен сессии. Например, по умолчанию
- <code>Zend_Auth</code> размещает идентификаторы под ключом 'storage'
- пространства имен 'Zend_Auth'.
- <!--
- If your authentication adapter for <code>Zend_Auth</code> returns a result where the authorization identity
- is an object (not recommended), instead of an array, then make sure to require your authorization identity
- class definition before starting the session. Instead, we recommend storing the authorization ids computed
- within an authentication adapter inside a well-known key in a session namespace. For example, the default
- behavior of <code>Zend_Auth</code> places this in the 'storage' key of the 'Zend_Auth' namespace.
- -->
- </para>
- <para>
- Если вы приказали <code>Zend_Auth</code> не сохранять метку сессии в
- сессиях, то можете вручную сохранять ID авторизации под хорошо
- известным ключом в любом пространстве имен сессии.
- Часто приложения имеют свои
- требования к тому, где хранить "мандат" (учетная запись с
- праметрами доступа пользователя) и идентификатор авторизации.
- Приложения часто устанавливают соответствие идентификаторов
- аутентификации (например, имена пользователей) и идентификаторов
- авторизации (например, присвоенное уникальное целое число) во время
- аутентификации, которая должна производится внутри метода
- <code>authenticate()</code> адаптера аутентификации Zend_Auth.
- <!--
- If you tell <code>Zend_Auth</code> to not persist authentication tokens in sessions, then you can manually
- store the authorization id in the session namespace, in a well-known location in a session namespace of your
- choice. Often, applications have specific needs about where to store credentials used (if any) and
- "authorization" identity. Applications often map authentication identities (e.g. usernames) to authorization
- identities (e.g. a uniquely assigned integer) during authentication, which would occur in the Zend_Auth
- authentication adapter's <code>authenticate()</code> method.
- -->
- </para>
- <example>
- <title>Пример: Простой доступ к ID авторизации<!--Example: Simplified access of authorization ids--></title>
- <programlisting language="php">
- <![CDATA[<?php
- // pre-authentication request
- require_once 'Zend/Auth/Adapter/Digest.php';
- $adapter = new Zend_Auth_Adapter_Digest($filename, $realm, $username, $password);
- $result = $adapter->authenticate();
- require_once 'Zend/Session/Namespace.php';
- $namespace = new Zend_Session_Namespace('Zend_Auth');
- if ($result->isValid()) {
- $namespace->authorizationId = $result->getIdentity();
- $namespace->date = time();
- } else {
- $namespace->attempts++;
- }
- // subsequent requests
- require_once 'Zend/Session.php';
- Zend_Session::start();
- $namespace = new Zend_Session_Namespace('Zend_Auth');
- echo "Valid: ", (empty($namespace->authorizationId) ? 'No' : 'Yes'), "\n"';
- echo "Authorization / user Id: ", (empty($namespace->authorizationId)
- ? 'none' : print_r($namespace->authorizationId, true)), "\n"';
- echo "Authentication attempts: ", (empty($namespace->attempts)
- ? '0' : $namespace->attempts), "\n"';
- echo "Authenticated on: ",
- (empty($namespace->date) ? 'No' : date(DATE_ATOM, $namespace->date), "\n"';
- ?>]]></programlisting>
- </example>
- <para>
- Идентификаторы авторизации, хранящиеся на клиентской стороне, могут
- использоваться в атаках на поднятие привилегий, если им доверяет
- серверная сторона и если они, например, не дублируются на серверной
- стороне (например, в данных сессии) и затем сверяются с
- идентификатором авторизации, предоставленным клентом для
- действующией сессии. Мы различаем понятия "идентификаторов
- аутентификации" (например, имена пользователей) и "идентификаторов
- авторизации" (например, ID пользователя #101 в таблице БД для
- пользователей).
- <!--
- Authorization ids stored client-side are subject to privilege escalation vulnerabilities, if these ids are
- used and trusted by the server, unless, for example, the id is duplicated on the server-side (e.g. in the
- session) and then cross-checked with the authorization id claimed by the client for the in-effect session.
- We are differentiating between "authentication ids" (e.g. usernames) and "authorization ids" (e.g. user id
- #101 in the users DB table).
- -->
- </para>
- <para>
- Последнее часто используется для повышения производительности -
- например, для выборки из пула серверов, кеширующих данные сессии,
- чтобы решить проблему "курицы и яйца". Часто
- возникают дебаты о том, использовать ли настоящий ID авторизации в
- куках или некую замену, которая помогает установить соответствие
- с настоящим ID авторизации (или сессии сервера(ов), хранящего
- сессию/профиль пользователя и т.д.), в то время как некоторые
- архитекторы системной безопасности предпочитают избегать
- публикования истинных значений первичных ключей, пытаясь достичь
- некоторого дополнительного уровня защиты в случае наличия
- уязвимостей к SQL-инъекциям.
- <!--
- The latter is not uncommon for performance reasons, such as helping select from a pool of servers caching
- session information to help solve chicken-and-egg problems. Often debates ensue about whether to use the
- real authorization id in the cookie, or some substitute that aids in mapping to the real authorization id
- (or session or server(s) holding the user's session/profile, etc.), as some system security architects wish
- to prevent true "DB primary keys" from escaping into the wild. These architects try and obtain some level of
- protection by obfuscation in the event of a SQL injection vulnerability in their system. Not everyone uses
- auto-increment strategies for authorization ids.
- -->
- </para>
- </sect2>
- <sect2 id="zend.session.testing">
- <title>Использование сессий с юнит-тестами<!--Using Sessions with Unit Tests--></title>
- <para>
- Zend Framework использует PHPUnit для своего тестирования. Многие
- разработчики расширяют существующие наборы
- юнит-тестов для покрытия кода в своих приложениях.
- Если при выполнении юнит-тестирований после завершения сессии были
- использованы любые связанные с записью методы, то генерируется
- исключение "<emphasis>Zend_Session is currently marked
- as read-only</emphasis>" ("Zend_Session помечен как доступный только
- для чтения"). Тем не менее, юнит-тесты, использующие Zend_Session,
- требуют особого внимания в разработке, поскольку закрытие
- (<code>Zend_Session::writeClose()</code>) или уничтожение сессии
- (<code>Zend_Session::destroy()</code>) не дает впоследствии
- устанавливать или сбрасывать ключи в любом объекте
- Zend_Session_Namespace. Это поведение является прямым следствием
- использования лежащего в основе расширения ext/session,
- функций <code>session_destroy()</code> и
- <code>session_write_close()</code>, которые не имеют механизма
- "отмены" для облегчения установки/демонтажа в юнит-тестировании.
- <!--
- Zend Framework relies on PHPUnit to facilitate testing of itself. Many developers extend the existing
- suite of unit tests to cover the code in their applications. The exception
- "<emphasis>Zend_Session is currently marked as read-only</emphasis>" is thrown while
- performing unit tests, if any write-related methods are used after ending the session. However, unit tests
- using Zend_Session require extra attention, because closing (<code>Zend_Session::writeClose()</code>), or
- destroying a session (<code>Zend_Session::destroy()</code>) prevents any further setting or unsetting of
- keys in any Zend_Session_Namespace. This behavior is a direct result of the underlying ext/session mechanism
- and PHP's <code>session_destroy()</code> and <code>session_write_close()</code>, which has no "undo"
- mechanism to facilitate setup/teardown with unit tests.
- -->
- </para>
- <para>
- Чтобы обойти это, см. юнит-тест
- <code>testSetExpirationSeconds()</code> в
- <code>tests/Zend/Session/SessionTest.php</code> и
- <code>SessionTestHelper.php</code>, которые используют
- <code>exec()</code> для запуска отдельного процесса. Новый процесс
- более точно имитирует второй, последующий, запрос из броузера.
- Отдельный процесс начинается с "чистой" сессии, так же, как при
- выполнении любого PHP-скрипта для веб-запроса. Кроме этого,
- любые изменения в <varname>$_SESSION[]</varname>, произведенные при вызове
- процесса, становятся доступными и в дочернем процессе, что дает
- родительскому процессу возможность закрыть сессию до использования
- <code>exec()</code>.
- <!--
- To work around this, see the unit test <code>testSetExpirationSeconds()</code> in
- <code>tests/Zend/Session/SessionTest.php and SessionTestHelper.php</code>, which make use of PHP's
- <code>exec()</code> to launch a separate process. The new process more accurately simulates a second,
- successive request from a browser. The separate process begins with a "clean" session, just like any PHP
- script execution for a web request. Also, any changes to <varname>$_SESSION[]</varname> made in the calling
- process become available to the child process, provided the parent closed the session before using
- <code>exec()</code>
- -->
- </para>
- <example>
- <title>Использование PHPUnit для тестирования кода, написанного с использованием Zend_Session*<!--Using PHPUnit to test code written using Zend_Session*--></title>
- <programlisting language="php">
- <![CDATA[<?php
- // testing setExpirationSeconds()
- require 'tests/Zend/Session/SessionTestHelper.php'; // also see SessionTest.php in trunk/
- $script = 'SessionTestHelper.php';
- $s = new Zend_Session_Namespace('space');
- $s->a = 'apple';
- $s->o = 'orange';
- $s->setExpirationSeconds(5);
- Zend_Session::regenerateId();
- $id = Zend_Session::getId();
- session_write_close(); // release session so process below can use it
- sleep(4); // not long enough for things to expire
- exec($script . "expireAll $id expireAll", $result);
- $result = $this->sortResult($result);
- $expect = ';a === apple;o === orange;p === pear';
- $this->assertTrue($result === $expect,
- "iteration over default Zend_Session namespace failed; expecting result === '$expect', but got '$result'");
- sleep(2); // long enough for things to expire (total of 6 seconds waiting, but expires in 5)
- exec($script . "expireAll $id expireAll", $result);
- $result = array_pop($result);
- $this->assertTrue($result === '',
- "iteration over default Zend_Session namespace failed; expecting result === '', but got '$result')");
- session_start(); // resume artificially suspended session
- // We could split this into a separate test, but actually, if anything leftover from above
- // contaminates the tests below, that is also a bug that we want to know about.
- $s = new Zend_Session_Namespace('expireGuava');
- $s->setExpirationSeconds(5, 'g'); // now try to expire only 1 of the keys in the namespace
- $s->g = 'guava';
- $s->p = 'peach';
- $s->p = 'plum';
- session_write_close(); // release session so process below can use it
- sleep(6); // not long enough for things to expire
- exec($script . "expireAll $id expireGuava", $result);
- $result = $this->sortResult($result);
- session_start(); // resume artificially suspended session
- $this->assertTrue($result === ';p === plum',
- "iteration over named Zend_Session namespace failed (result=$result)");
- ?>]]></programlisting>
- </example>
- </sect2>
- </sect1>
|