While the basic usage examples are a perfectly acceptable way to utilize Zend_Session, there are some best
practices to consider. Consider the
Zend_Auth
example
that transparently uses Zend_Session by default to persist authentication tokens. This example shows one
approach to quickly and easily integrate Zend_Session and Zend_Auth.
If you want all requests to have a session and use Zend_Session, then start the session in the bootstrap file:
Exemple 25.6. Starting the Global Session
<?php ... require_once 'Zend/Session.php'; Zend_Session_Core::start(); ... ?>
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 Zend_Session_Core::start()
first. (More on
advanced features later).
There are four ways to start a session, when using Zend_Session. Two are wrong.
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 php_value session.auto_start 0
to your
.htaccess file (usually in your HTML document root directory).
2. Wrong: Do not use session_start()
directly (see
session_start()
). If you use session_start()
directly, and then start using Zend_Session, an
exception will be thrown by Zend_Session::start()
("session has already been started").
If you call session_start()
, after starting Zend_Session, an error of level E_NOTICE
will be generated, and the call will be ignored.
3. Correct: Use Zend_Session_Core::start()
. If you want all requests to have sessions,
then place this function call in your ZF bootstrap code. If some requests should have sessions, but
others should not, then set the strict
option to true (see
setOptions()
) in your userland bootstrap, and then call Zend_Session_Core::start()
in your
bootstrap, before the first call to new Zend_Session()
. Sessions have some overhead.
The strict
option prevents new Zend_Session()
from automatically starting
Zend_Session_Core. 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, before an explicit call to
Zend_Session_Core::start()
. Do not use this option in ZF core code, because only
userland developers should make this design choice. Similarly, all "library" developers should
carefully consider the impact of using setOptions() on users of their library code, since these
options have global side-effects (as do the underlying options for ext/session).
4. Correct: Just use new Zend_Session()
, and the session will be automatically started
within Zend_Session_Core.
Option #4 is more difficult to use than #3, because you are responsible for ensuring that the first
new Zend_Session()
happens before any output has been sent
by PHP to the client, if you are using the default, cookie-based sessions (recommended).
Session namespaces can be locked, to prevent further alterations to the data in that namespace. Use
lock()
to make the namespace read-only, unLock()
to make a read-only namespace
read-write, and isLocked()
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 the object. Similarly, locking Zend_Session namespaces does not prevent the use of symbol
table aliases to the same data (see
PHP references
).
Exemple 25.7. Locking Session Namespaces
<?php // assuming: $my_session = new Zend_Session('my_session'); // marking session as read only locked $my_session->lock(); // unlocking read-only lock if ($my_session->isLocked()) { $my_session->unLock(); } ?>
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.
Exemple 25.8. Locking Sessions in Views
<?php class FooModule_View extends Zend_View { public function show($name) { if (!isset($this->session)) { $this->session = Zend::registry('FooModule'); } if ($this->session->isLocked()) { return parent::render($name); } $this->session->lock(); $return = parent::render($name); $this->session->unLock(); return $return; } } ?>
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. That is done by retrieving an instance from the sessions getInstance and priming it with a string of the desired namespace.
Exemple 25.9. Namespaced Sessions for Controllers with Automatic Expiration
<?php require_once 'Zend/Session.php'; // question view controller $test_session = new Zend_Session('test'); $test_session->setExpirationSeconds(300, "accept_answer"); // expire only this variable $test_session->accept_answer = true; -- // answer processing controller $test_session = new Zend_Session('test'); if ($test_session->accept_answer === true) { // within time } else { // not within time } ?>
We recommend using session locking (see above) instead of the feature below, which places extra management burden on the developer to pass the Zend_Session instance into whatever functions and objects need access.
When constructing the first instance of Zend_Session attached to a specific namespace, you can also instruct
Zend_Session to not make any more instances for that namespace. Thus, any future attempts to construct a
Zend_Session 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 $session = new
Zend_Session('aNamespace');
, after the first intance has been created, as follows in the example
below:
Exemple 25.10. Limiting to Single Instances
<?php require_once 'Zend/Session.php'; $authSessionAccessor1 = new Zend_Session('Zend_Auth'); $authSessionAccessor2 = new Zend_Session('Zend_Auth', Zend_Session::SINGLE_INSTANCE); $authSessionAccessor1->foo = 'bar'; assert($authSessionAccessor2->foo, 'bar'); // passes doSomething($options, $authSessionAccessor2); // pass the accessor to wherever it is needed . . . $aSessionObject = new Zend_Session('Zend_Auth'); // this will throw an error ?>
The second parameter in the constructor above will tell Zend_Session that any future Zend_Session's that are
instantiated with the 'Zend_Auth' namespace are not allowed and will thus result in an exception being
thrown. Since new Zend_Session('Zend_Auth')
will not be allowed after the code above has been
executed, the developer becomes responsible for storing the instance object
($componentAuthState
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.