13 Jul 2009, 5:40 p.m.

Zend_Session Overrides php.ini Settings

This post concerns some curious behaviour in Zend_Session. We spent about a day of developer time tracking this down, and I'm still a bit puzzled about it. It doesn't appear anywhere obvious in the documentation, and I haven't found a great deal on the web about it, so maybe this will save somebody some time, if nothing else.

First, some background on PHP sessions and what we were trying to achieve. We rely pretty heavily on PHP sessions, which by default rely on cookies. There are still some question marks over how well mobile handsets support cookies, but not to worry, session.use_trans_sid to the rescue.

session.use_trans_sid is a php.ini setting. With it enabled, if cookies are not supported by the user's browser, PHP automatically decorates the links in your URLs and form actions so that they contain a session ID as part of the query string. We switched it on and, as if by magic - which is how I assume they do it, quite frankly - the session ID was in the URLs. Unfortunately, each click was generating a new session ID, and so in practice there was no session.

Cue an afternoon of head-scratching.

We finally pinned the problem down to Zend_Session. We're using the Zend_Session component of the Zend Framework to wrap PHP's built in session management, which centres around the $_SESSION superglobal array.

And it turns out that Zend_Session silently overrides session-related php.ini settings, whether you like it or not. The offending code is here:

$defaultOptionValue) { if (isset(self::$_defaultOptions[$defaultOptionName])) { ini_set("session.$defaultOptionName", $defaultOptionValue); } }

The actual options that Zend_Session uses are defined a little earlier:

null, 'name' => null, /* this should be set to a unique value for each application */ 'save_handler' => null, //'auto_start' => null, /* intentionally excluded (see manual) */ 'gc_probability' => null, 'gc_divisor' => null, 'gc_maxlifetime' => null, 'serialize_handler' => null, 'cookie_lifetime' => null, 'cookie_path' => null, 'cookie_domain' => null, 'cookie_secure' => null, 'cookie_httponly' => 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 );

Note that use_only_cookies is on, and use_trans_sid is null, meaning it gets overridden to its default value, which is off. So in short, you can't rely on php.ini settings being honoured if you use Zend_Session.

The workaround is to call Zend_Session::setOptions() within your application's code, for example:

'off', 'use_trans_sid' => 'on'); Zend_Session::setOptions($options); $session = new Zend_Session_Namespace('my_session_namespace');

I don't know why the developers feel the need to override carefully chosen php.ini settings, let alone that they have a right to do so. In general terms, calling ini_set() from within a library feels very wrong to me. Most of us put a lot of time and thought into configuring our production PHP environments, so it's counterintuitive to me that this should happen.

If anyone can actually justify this behaviour, I'd be really interested to hear the reasoning. It could at least be made explicit in the documentation.

Posted by Simon at 01:53:00 PM
13 Jul 2009, 6:46 p.m.

Ciaran McNulty

It certainly seems to be an interesting choice - ignore PHP's mechanism for configuration and essentially reproduce it with a new option mechanism inside of Zend_Session.

I had a look through the SVN and it seems to have been added as part of a larger restructuring of the Session mechanism so I'm not sure where to find the motivations behind it.

12 Mar 2010, 11:52 a.m.

Ciaran McNulty

To resurrect an old post...

The preferred answer to this in ZF seems to be to use Zend_Application_Resource_Session during your bootstrapping and specify the options there.

For an app that used Zend_Application that would be as simple as adding the following to the application.ini:

resources.session.use_only_cookies = Off
resources.session.use_trans_sid = On

I've not tested that of course but I can now see the point of ZF setting these things per-application.