Zend Framework 3 (ZF3) will provide a new session management layer that will decouple it from ext/session. In doing this we will allow you to run multiple sessions at the same time in an application, utilize sessions in long-running applications, compatibility/convertibility with other applications, languages, etc.
With decoupling ext/session we will also provide a compatibility layer that will allow you to continue to utilize ext/session and the super global $_SESSION thus allowing you to continue with like behavior as you would have come to expect with generic session management in PHP.
In Zend Framework 2 (ZF2), we encountered numerous issues throughout the session management layer. Certain compatibility issues with ext/session specifically relating to session uploads. Session uploads caused corruption to the internal handing of sessions generally due to it's attempt to rewrite the session data. We also encountered several issues with PHP itself specifically regarding the usage of ArrayObject (deep unsets, references, etc) which removing will provide better long-term support.
In addition the session management layer will attempt to decouple itself by utilizing events as a means of inner-process communication which will allow for the user to take a new route and short-circuit the session if necessary.
- Config
- General configuration values for session implementation
- Container
- Remove array object; merge storage as a container; all session values are part of a container or a default container
- Containers will utilize simple key => value pairs
- Serializer
- Provide strategies as to how data is serialized and unserialized.
- Storage
- This will be removed; this is what allowed us to have ArrayObject and different means of storage; ArrayObject caused many issues and in addition makes things far more difficult in the long run. It also caused several issues with corruption due to how sessions were manipulated (ext/session that is)
- Persistence
- To replace SaveHandler, persistance will no longer follow ext/session but a compatibility layer may utilize events to short circuit.
- Validation
- Referrer
- IP / Hostname
- Upload Process - Upload progress is built into the PHP core and would not work as expected unless utilizing the compatibility mode.
- While the values may get set, it could be in the wrong session area such as a local file system by PHP defaults.
- Overall other methods exist that can chunk data and provide the user with progress on that front and overall being far more flexible than attempting to do it on the server side.
The configuration class will allow for setting specific configuration values.
** Configuration Values **
- lazy_persistence - The ability to lazily write the session; aka do not write anything unless something had changed.
- This in addition for expiration reasons may need to 'touch' the session from time to time.
- Ex: 1 - (lifetime + sessionTime / currentTime) <= .1 update or something like it
- lifetime - The value in seconds that a session can live for, ** see above for usage of this value **
- id_strategy - The strategy in how we generate out the session id, likely utilize RandomLib as a default, or utilize php to generate it
- gc_strategy - Handling probability, and lifetime for gc reasons. More than likely a class and we can read the php configuration values here which could supply the default ** note: gc_lifetime should minimally be the same as lifetime **
- serializer - factory to set the serializer preference, generally php serialization
- validation_chain - factory to set the validation chain which will likely utilize an array of values or array of Validation objects
- compatibility - to force PHP compatability
- This could cause changes to the above values for instance, serializer must be a php compatable serialize handler
- lazy_persistence does not necessarily work in ext/session and instead would have to be enforced in the persistence area to know if values had changed
The container would work much as it does today; although we'd move away from the ArrayObject or ArrayAccess method of doing things. Instead providing a getter and setter that can handle the values. This still has not been thought through 100% here but overall we want to avoid having issues with deep linking and unsetting or changing values. This means that we attempt to maintain simple key => value pairs and it is up to the user to handle the deeper nesting.
Containers would have the ability to have expiration in both immediate expiration and at a specific datetime. By removing the seconds and hops it clears up additional areas. However, hops will likely be expected by the user and to replicate it will be as easy as setting $container->expireAt(new \DateTime()) for a single hop. Hop's just do not make as much sense as it is easy to become expired by an XHR request and instead should likely be manually expired.
For instance, take a FlashMessenger. The flash messenger should create a container that would then expire once the value is read thus being the responsibility of the flash messenger to call ->expire() on the container.
The container itself could maintain the meta-data separately from the values and the container's data may be stored in a zf specific indici.
$container = $session->getContainer('foo');
$container->set('foo', 'bar');
$container->set('bar', 'baz');
var_dump($session->toArray());
array(
'foo' => array(
'foo' => 'bar',
'bar' => 'baz',
),
);
The Serializer would simply be any class implementing Serializable. We would then call the serialize() and unserialize() methods manually.
Note: You would need to implement JsonSerailizable on all objects that you would want to have properly hydrated.
class Json implement Serializable
{
protected $data;
public function __construct($data = null)
{
$this->data = $data;
}
public function serialize() {
return json_encode($this->data);
}
public function unserialize($data) {
return json_decode($data);
}
}
Persistence will attempt to closely follow SessionHandlerInterface in PHP itself. However, we will have a separate interface as the storage will be passed certain objects that are not available in PHP itself. This is why during compatibility mode that we will supply a method of proxying this information.
However, we will not utilize the same interface directly. The reason for this is the need for additional data and information.
Session validation would be changed to no longer throw exceptions and gracefully handle session errors. In many cases on a failed session validation, a new session should be generated for the user and initialized that way.
In ZF2 many users had issues with the session validators because they would throw an exception causing the application to error. Instead this should be handled gracefully as to not attempt to destroy the overall session but to supply it with the ability to regenerate an id and utilize the new session in that way.
$config = new Zend\Session\Config();
$config->setLazyPersistence(true); //
$config->setLifetime(3600);
$config->setIdStrategy('php');
$config->setGcStategy('php');
$session = new Zend\Session\Session('name', $config); // $config could be an object
$session->setSerializer('php');
$session->setValidationChain(array($validator, $v2, $v3));
$session->setCompat(true); // works with ext/session
$session->start();
$container = $session->getContainer('foo');
$container->get('var', 'default');
$container->set('var', 'foo');
$container->expire(); // expire immediate
$container->expireAt(DateTime); // expire at a specific time
$session->save();
This is a pretty cool idea. I like the additional features this could provide over ext/session. My main concern is security and how to deal with more complex problems like load balancing. How would those fit into it? Security is obviously important - if ZF3 is saying "here come use this awesome new session handler", everyone does, and then there's a scary vuln, it will be like heartbleed sadness all over again. But perhaps a smaller scale. Security of this must be thought about carefully.
I think it should be made simple enough so that the easy way is not to just go and use $_SESSION - it should cater to basic needs, as well as LB/multi-server systems, e.g. in ZF2 at the moment, you just instantiate a
new \Zend\Session\Container()
and you're off, it's very straightforward. So as long as this is possible with this concept, then I am +1.