Skip to content

Instantly share code, notes, and snippets.

@mwillbanks
Last active August 29, 2015 14:02
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mwillbanks/72def29ddb0f1dc88a09 to your computer and use it in GitHub Desktop.
Save mwillbanks/72def29ddb0f1dc88a09 to your computer and use it in GitHub Desktop.
Some ideas around sessions

Sessions

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.

Highlights

  • 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

Limitations of not using ext/session

  • 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.

Configuration

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

Container

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.

An example

$container = $session->getContainer('foo');
$container->set('foo', 'bar');
$container->set('bar', 'baz');

var_dump($session->toArray());

array(
    'foo' => array(
    	'foo' => 'bar',
    	'bar' => 'baz',
    ),
);

Serializer

The Serializer would simply be any class implementing Serializable. We would then call the serialize() and unserialize() methods manually.

An Example

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

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.

Validation

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.

Example

$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();
@Ocramius
Copy link

Ocramius commented Jun 7, 2014

I really really really like this. This makes us completely independent from the core sessions and their horrors.

Additionally, building sessions that can be reused across different requests in the same long running process is now possible!

I really like it, though I don't have a clear overview of the pitfalls so far.

@asgrim
Copy link

asgrim commented Jun 7, 2014

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.

@Ocramius
Copy link

Ocramius commented Jun 7, 2014

Security of this must be thought about carefully.

Security was never taken lightly in zendframework, so we should be able to keep up with expectations.

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.

I think we can still keep that API via some sort of container that just proxies access to the current session (not the session manager anymore). What is important is that we are able to swap out the current session at runtime, getting away from some "singleton-ish" binding.

@adamculp
Copy link

adamculp commented Jun 7, 2014

Interesting. As long as beginners would be able to rely sane defaults, for simpler usage as they learn.

@yasselavila
Copy link

👍

@mwillbanks
Copy link
Author

@asgrim
We would be relying on several of the same things from a session ID perspective that PHP does as a root (this is practically the only thing you are given for free). Additionally the validators are the items to lock down the session more by choice (fixation, highjacking, etc) - all of the session techniques for security have a trade off.

@adamculp
Well, if I find a yellow elephant, I'll make it as simple as possible! Really; it will be almost as simple as $_SESSION. The goal is to create that separation but then also allow for a compatibility layer to allow it to completely interoperate with ext/session but provide the compatibility.

@danizord
Copy link

danizord commented Jun 9, 2014

Great ideas! However, I just dislike the $session->setCompat(true);, I think it should be handled in separate classes.

@mwillbanks
Copy link
Author

@danizord how do you think you would handle it that way out of curiosity?

In theory we would change how the session class would work likely by utilizing the event manager (hooking into specific methods for start and close); then the saving would register a compatibility persistence layer that would basically proxy to our objects for persistence and short circuit.

I'm not certain how we'd do that with a separate class unless you are thinking it would be like:

$session = new Zend\Session\SessionCompat();

or something to that degree; my preference would be to utilize events to handle the differences which gives people more flexibility in the long run.

@juriansluiman
Copy link

@mwillbanks I am missing the considerations why to switch away from the underlying $_SESSION "backend"? If you are kind-of replicating the same behaviour, why not use a part of a ecosystem which is used and developed by thousands more developers than ZF3 can ever reach?

@Ocramius
Copy link

Ocramius commented Jun 9, 2014

@juriansluiman I personally need that for long running processes. Being able to spawn own sessions per ID is a great deal.

Additionally, BC breaks in the session layer have been very annoying for us, and shipping our own solution that can wire into ext/session seems much more flexible and maintainable.

@mwillbanks
Copy link
Author

@juriansluiman

To a degree, one of them is so that we can run multiple sessions. The second part of it is that there are several compatibility considerations that have to be made. We become highly limited to what we can do when we fully incorporate ext/session. I attempted to update the gist a bit as to provide some additional information.

We've also had issues with ext/session especially with session upload progress; it has caused several corruption issues. In addition to this; the session management component would have been better served architecturally within ZF by utilizing the event manager and allowing for short-circuiting, etc. In addition to this; by removing that behavior we can provide our own serialization (using php by default) and it will overall make it easier for interoperability with other languages and systems.

Overall; the compatibility layer will work far better than ZF2 sessions ever did in the way that this would be architected. I think that the general use case will be people utilizing sessions via compatibility mode which will provide the most support for third party applications in the PHP world.

@mwillbanks
Copy link
Author

@Ocramius

Most of these were not BC breaks in the ext/session component itself; but rather in the ArrayObject implementation. The main issue we've had with ext/session has been with the session upload progress which continually corrupts the session but that is due to our internal handling by using ArrayObject and the serialization.

@jaapio
Copy link

jaapio commented Jun 10, 2014

Sounds good, especially the serializer part, which will solve the strange ext/session serialization of protected and private properties. If you want to persist an object using the current session container in a database session in a text field you will get some strange results.

Tnx for this one.

@weierophinney
Copy link

@mwillbanks How do you envision garbage collection working? Would it emulate how it is done by default in PHP? Or would we be recommending usage of a scheduler that would handle it? I'd like some more details on this aspect.

@mwillbanks
Copy link
Author

@weierophinney I would say that I would envision this that we would recommend a scheduler for instance, debian has it set on a cron when you install packages thus if you use a custom session handler you need to override the basic php configuration anyhow.

Since the default of this would still utilize ext/session we would leave php up to the garbage collection. Ultimately on a high performance site you do not want the gc process to happen on x number of requests or thing of that nature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment