View Source

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[{zone-template-instance:ZFDEV:Zend Proposal Zone Template With Tracker}

{zone-data:jira-key}LPSESS{zone-data}
{zone-data:jira-pid}10010{zone-data}
{zone-data:jira-recently}
{jiraissues:url=http://framework.zend.com/issues/secure/IssueNavigator.jspa?view=rss&created%3Aprevious=-1w&pid=10010&sorter/field=created&sorter/order=DESC&tempMax=25&reset=true&decorator=none|columns=Type;Key;Summary;Assignee;Priority;Status;Created}
{zone-data}
{zone-data:jira-summary}
{jiraportlet:url=http://framework.zend.com/issues/secure/RunPortlet.jspa?portletKey=com.atlassian.jira.plugin.system.portlets:project&projectid=10010&projectinfo=full}
{zone-data}
{zone-data:review-end}September 27, 2006{zone-data}

{zone-data:component-name}
Zend_Session
{zone-data}

{zone-data:proposer-list}
[~ralph]
[~gavin] (Zend-liaison)
{zone-data}

{zone-data:revision}
$Id: Zend_Session-proposal.txt,v 4.0 2006/07/12 02:00:00 ralph Exp $
{zone-data}

{zone-data:overview}
Zend_Session provides a standardized interface to common functionality found in or indigenous to site session implementations. The core of Zend_Session implements functionality centered around the storage of information within the $_SESSION variable while maintaining the idea of a "namespace". At the most basic usage level, a user is able to store and retrieve information in a session that is self-evident. Examples of the very basic to complex usage are below.


Worked code located here (subversion repository)
http://www.ralphschindler.com/subversion/Zend_Session/

{zone-data}

{zone-data:references}
http://www.php.net/session
http://shiflett.org/articles/security-corner-feb2004
http://www.ralphschindler.com/Zend_Framework_Modules/Zend_Session/
http://java.sun.com/j2ee/sdk_1.2.1/techdocs/api/javax/servlet/http/HttpSession.html
http://wiki.rubyonrails.com/rails/pages/sessions
{zone-data}

{zone-data:requirements}
*PHP5
*Zend Framework
{zone-data}

{zone-data:dependencies}
* Zend_Exception
{zone-data}

{zone-data:operation}

Zend_Session utilizes ext/session and and _SESSION supberglobal as its storage mechanism. That being the case, the internals (or the Zend_Session_Core), is implemented as a singleton/getInstance pattern and should only be interfaced via Zend_Session.

Optionally first, the user may pass options to the Zend_Session::setOptions() method. These options currently are the same options available to the ext/session (a list is here:
http://us3.php.net/session#session.configuration). To pass the options just pass the basename (the non session. part) as part of an array to setOptions. Without setting any options, Zend_Session will utilize the recommended options first, then the default php.ini settings. *Community feedback is welcome on what the best practices set of recommended options should be. *** See Use Cases #1

Next, the user may attain an instance of Zend_Session through standard instantiation. By default, all data stored in the session will go into the 'Default' namespace. This is transparent to the user. See use cases #2.

The internals of Zend_Session_Core are implemented in such a way as to promote the use of namespaces to distinguish types of variables and attaching specific functionality to them. Zend_Session_Core also has public facing static methods which when called will affect all instances of Zend_Session and the entire session as a whole. It should be thought that any instance of Zend_Session will jail the users data to a sliver of a namespace within the overall session and accessing Zend_Session_Core and its static methods will affect all aspects of the session. The static methods listed in the Core are typically more advanced features of sessions that some users may or may not find useful or feel comfortable utilizing.

A common case of usage might be for a use to separate session access by controllers. One might want to protect variables in say the 'Zend_Auth' controller. That is done by retireving an instance from the sessions getInstance and priming it with a string of the desired namespace. *** See Example #3

{zone-data}

{zone-data:class-list}

* Zend_Session (public)
* Zend_Session_Core
* Zend_Session_SaveHandlerInterface

* Zend_Messenger (functional, proposed, and example)
* Zend_Session_User (example)

{zone-data}

{zone-data:use-cases}

{code}

7. Use Cases

Note: Working code is prefixed with ZendDev to separate from development tree.

Typical Use Cases for Zend_Session


1. Out the box


$session = new Zend_Session(); // this will provide storage within the 'default' namespace.
$session->user = "ralph";


2. Out the box, namespace support


$wiki_session = new Zend_Session('wiki');
$wiki_session->message = "The page located at {$page} has been updated";


3. Out the box, namespace support, expiration by seconds support


// 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
}

4. Out the box, namespace support, expiration by hops support


// form processing controller
$form_session = new Zend_Session('signup_form');
$form_session->setExpirationHops(1, "message"); // expire only this variable

$form_session->message = "Your form has been updated";

// process form below

--

// next hop*, page view after processing of form
$form_session = new Zend_Session('signup_form');

if (isset($form_session->message))
{
$template->showUserMessage($form_session->message);
}

--
note: on the 3rd hop from initial setting, the variable message will cease to exist

5. Options/Init usage (This is the prefered way of doign things in the bootstrap file, this way
all controllers are garanteed to be using the samed session options regardless of what order they
run in)


$config = new Zend_Config(Zend_Config_Ini::load('/path/to/config.ini', 'production'));

Zend_Session::setOptions($config->session);

// in another controller somewhere
$session = new Zend_Session('some_controllers_namespace');


6. Options/Init 2 + Security


$config = new Zend_Config(Zend_Config_Ini::load('/path/to/config.ini', 'production'));

Zend_Session::setOptions($config->session);
Zend_Session::setOptions(array(
'lock_strict' => true,
'lock_to_ip' => true // other options here are false, true, 1, 2, 3, 4
// 1 - same as locking to 192.*.*.* given the users ip is 192.168.1.10
// 2 - same as locking to 192.168.*.*
// 3 - same as locking to 192.168.1.*
// 4, true - same as locking to 192.168.1.10
// false - default value
);

// and/or optionally
Zend_Session::setOptions(array(
'max_requests_per_minute' => 30
);


7. Validation Techniques


[[[ NONCE SUPPORT ]]]

//-- in one request
$session = new Zend_Session('my_namespace');

// by default, will exist for next hop only regardless of namespace usage
$nonce = $session->validationNonce->init();

$view->nonce = $nonce;

//-- another request
$session = new Zend_Session('my_namespace');

// at this point any attempt to utitlize $session variables will throw errors until
// the nonce is supplied to the object like so:

$session->validationNonce->validate($input->getRaw('nonce'));



[[[ STRING SUPPORT ]]]

//-- in one request

$session = new Zend_Session('my_namespace');

$capcha = new Zend_Validate_Capcha();

$session->validationString->setString($capcha->getString());

//-- in next page request

$session = new Zend_Session('my_namespace');

// at this point any attempt to utitlize $session variables will throw exception until
// the valid string is supplied to the object like so:

$session->validationString->validate($input->getRaw('capcha'));


8. API For Use In Practical Modules

// for regenerating session id, this is useful for something like Zend_User/Zend_ACL/Zend_Permissions
// such that when privledge changes, session id can change as well

$session->regenerateId();




Prefered Usage / Default Global Settings

Below are the options that can be passed to the Zend_Session::init() function, as an assoc-array.
The values below are the "Best Practices" values, the ones listed as null, will simply rely on
the value that is set in the users php.ini file. Perhaps we should enforce somethign more than
just "use_only_cookies"?.. Perhaps referer check set to the base host name?

static private $_default_options = array(

// from php.ini
"save_path" => null,
"name" => null,
"save_handler" => null,
"auto_start" => null,
"gc_probability" => null,
"gc_divisor" => null,
"gc_maxlifetime" => null,
"serialize_handler" => null,
"cookie_lifetime" => null,
"cookie_path" => null,
"cookie_domain" => null,
"cookie_secure" => null,
"use_cookies" => null,
"use_only_cookies" => "on",
"referer_check" => null,
"entropy_file" => null,
"entropy_length" => null,
"cache_limiter" => null,
"cache_expire" => null,
"use_trans_sid" => null,
"bug_compat_42" => null,
"bug_compat_warn" => null,
"hash_function" => null,
"hash_bits_per_character" => null,

// Zend_Session Internal settings
"lock_strict" => false, // when true, any security failures will
// throw zend_session_exception
"lock_to_ip" => false,
"lock_to_useragent" => false,

"max_requests_per_minute" => null,


);


{code}

{zone-data}

{zone-data:milestones}
Describe some intermediate state of this component in terms of design notes, additional material added to this page, and / code. Note any significant dependencies here, such as, "Milestone #3 can not be completed until feature Foo has been added to ZF component XYZ." Milestones will be required for acceptance of future proposals. They are not hard, and many times you will only need to think of the first three below.
* Milestone 1: \[DONE\] [design notes will be published here|http://framework.zend.com/wiki/x/sg]
* Milestone 1a: \[DONE\] Working code checked into users local repository, for peer review
* Milestone 2: Working prototype checked into the incubator supporting use cases #1, #2, ...
* Milestone 3: Working prototype checked into the incubator supporting use cases #3 and #4.
* Milestone 4: Unit tests exist, work, and are checked into SVN.
* Milestone 5: Initial documentation exists.

If a milestone is already done, begin the description with "\[DONE\]", like this:
* Milestone #: \[DONE\] Unit tests ...
{zone-data}

{zone-data:skeletons}
{code}


-- ZEND_SESSION --
<?

require_once 'Zend/Session/Core.php';
require_once 'Zend/Session/SaveHandlerInterface.php';

/**
* Zend_Session
*
*/
class Zend_Session
{

/**
* Session Core
*
* @var Zend_Session_Core
*/
protected $_session_core = null;

/**
* Namespace - which namespace this instance of zend-session is saving-to/getting-from
*
* @var string
*/
protected $_namespace = "Default";

/**
* SetOptions - this is a convienance method such that Zend_Session::setOptions will
* effectively be Zend_Session_Core::setOptions();
*
* @param array $user_options
*/
static public function setOptions($user_options);

/**
* SetFeature - this is a convienance method such that Zend_Session::setFeature will
* effectively be Zend_Session_Core::setFeature.
*
* @param string $name
* @param mixed $value
*/
static public function setFeature($name, $value);

/**
* Session save handler
*
* @param Zend_Session_SaveHandlerInterface $interface
*/
static public function setSaveHandler(Zend_Session_SaveHandlerInterface $interface);


/**
* Constructor - This will create an instance that saves to/gets from an
* instantiated core. An optional namespace allows for saving/getting
* to isolated sections of the session.
*
* @param string $namespace
*/
public function __construct($namespace = 'Default');


/**
* SetExpirationSeconds - expire the namespace, or specific variables after a specified
* number of seconds
*
* @param int $seconds
* @param mixed $variables
*/
public function setExpirationSeconds($seconds, $variables = null);

/**
* SetExpirationHops - expire the namespace, or specific variables after a specified
* number of page hops
*
* @param int $hops
* @param mixed $variables
* @param boolean $hop_count_on_usage_only
*/
public function setExpirationHops($hops, $variables = null, $hop_count_on_usage_only = false);

/**
* @todo Perhaps we want to lock a namespace after one instantiation?
*
*/
public function lock();

/**
* __get() - method to get a variable in this objects current namespace
*
* @param string $name
* @return mixed
*/
public function __get($name);

/**
* __set() - method to set a variable/value in this objects namespace
*
* @param string $name
* @param mixed $value
* @return true
*/
public function __set($name, $value);

/**
* __isset() - determine if a variable in this objects namespace is set
*
* @param string $name
* @return bool
*/
public function __isset($name);

/**
* __unset() - unset a variable in this objects namespace.
*
* @param string $name
* @return true
*/
public function __unset($name);
}



-- ZEND_SESSION_CORE --
<?

require_once 'Zend/Exception.php';

/**
* Zend_Session
*
* @package Zend_Session_Core
* @copyright none applied yet
* @license none applied yet
*/
class Zend_Session_Core
{

/**
* Check wether or not the session was started
*
* @var bool
*/
private $_session_started = false;

/**
* Instance of ZendDev_Session_Core
*
* @var ZendDev_Session_Core
*/
static private $_instance;

/**
* Private list of php's ini values for ext/session
* null values will default to the php.ini value, otherwise
* the value below will overwrite the default ini value, unless
* the user has set an option explicity with setOptions()
*
* @var array
*/
static private $_default_options = array(
"save_path" => null,
"name" => null,
"save_handler" => null,
"auto_start" => null,
"gc_probability" => null,
"gc_divisor" => null,
"gc_maxlifetime" => null,
"serialize_handler" => null,
"cookie_lifetime" => null,
"cookie_path" => null,
"cookie_domain" => null,
"cookie_secure" => null,
"use_cookies" => null,
"use_only_cookies" => "on",
"referer_check" => null,
"entropy_file" => null,
"entropy_length" => null,
"cache_limiter" => null,
"cache_expire" => null,
"use_trans_sid" => null,
"bug_compat_42" => null,
"bug_compat_warn" => null,
"hash_function" => null,
"hash_bits_per_character" => null
);

static private $_default_features = array(
"debug_mode" => false,
"lock_strict" => false,
"lock_to_ip" => false,
"lock_to_useragent" => false,
"max_requests_per_minute" => false,
"regenerate_id" => false,
"remember_me" => false
);

/**
* Whether options were parsed prior to instance creation
*
* @var array
*/
static private $_options_parsed = array();

/**
* Whether or not write close has been performed.
*
* @var bool
*/
private $_write_closed = false;


static private $_debug_mode = false;


/**
* SetOptions -
*
* @param array $user_options
*/
static public function setOptions(Array $user_options);

static public function setFeature($name, $value = true);

static public function featureDebugMode($value);


/**
* Session save handler
*
* @param Zend_Session_SaveHandlerInterface $interface
*/
static public function setSaveHandler(ZendDev_Session_SaveHandlerInterface $interface);

/**
* getInstance() - Enfore the Singleton, make sure we were initiated by a PluginAbstract.
*
* @param boolean $instance_must_exist
* @return ZendDev_Session_Core
*/
static public function getInstance($instance_must_exist = false);

static public function removeInstance();

/**
* Constructor
*
* @access private *not really but we would like it to be.
* @param string $namespace
*/
public function __construct();

/**
* _start() - this is our lazy load session initition. (not currently lazy)
*
*/
private function _start();

private function _processGlobalMetadata();

public function processNamespaceMetadata($namespace);

/**
* namespaceIsset() - check to see if a namespace or a variable within a namespace is set
*
* @param string $namespace
* @param string $name
* @return bool
*/
public function namespaceIsset($namespace, $name = null);

/**
* namespaceUnset() - unset a namespace or a variable within a namespace
*
* @param string $namespace
* @param string $name
* @return true
*/
public function namespaceUnset($namespace, $name = null);

/**
* namespaceSet() - set a variable within a namespace.
*
* @param string $namespace
* @param string $name
* @param mixed $value
* @return true
*/
public function namespaceSet($namespace, $name, $value);

/**
* Enter description here...
*
* @param string $namespace
* @param string $name
* @return mixed
*/
public function namespaceGet($namespace, $name = null);

public function namespaceSetExpirationSeconds($namespace, $seconds, $variables = null);

public function namespaceSetExpirationHops($namespace, $hops, $variables = null, $hop_count_on_usage_only = false);



/**
* Regenerate the session id
*
*/
public function regenerateId();

/**
* id - set to new id or return current id
*
* @param string $new_id
* @return string
*/
static public function getId();

static public function setId($id);

/**
* WriteClose - this will complete the internal data transformation on this request.
*
*/
public function writeClose();

/**
* This is here as a note of how destruction works. It is important to know that destructors
* run in no particular order. While that is the case, this object can still be referenced
* until the very last object is destroyed.
*
* @see http://www.phpbuilder.com/board/showthread.php?t=10293485
*/
final public function __destruct();

static public function shutdown();
}



{code}
{zone-data}

{zone-template-instance}]]></ac:plain-text-body></ac:macro>