2

I'm noticing some slightly unexpected behaviour using __destruct to write data using Zend_Session_Namespace in PHP 5.3:

public function __destruct(){
    $this->getSession()->data = $this->data;
}
// ....
private function getSession()
{
    if (! self::$zendSession) {
        // this next line is fine because the object is a singleton
        self::$zendSession = new Zend_Session_Namespace(self::SESSION_NAMESPACE);
    }
    return(self::$zendSession);
}

The destructor is getting called, but the data isn't getting written. However, if I implicitly call the destruct after the object has had all the necessary operations done on it, and force destruction before script termination, the data is written fine, but I'd rather not do this.

I imagine it has something to do with this bug: http://bugs.php.net/29032 (bug seems a bit old to me), and on the face of it the fix looks good (register __destruct as shut-down function, which is called before $_SESSION is unavailable), but wouldn't the destructor get called twice (once through the register_shutdown_function and once automatically?

Surely writing the odd bit of object data to session on shutdown is a thing that has been solved? What do people do when this is required?

(OSX 10.6.6, Apache 2.2.15 (Unix), PHP 5.3.3, Zend Framework 1.7.2)

Charles
  • 50,943
  • 13
  • 104
  • 142
sennett
  • 8,014
  • 9
  • 46
  • 69
  • There are additional complications involving `zend_controller_action->_redirect()`, but I didn't want to make the post too awkward to understand. Essentially, I noticed that if there is a chain of requests, using `zend_controller_action->_redirect()` to forward to the next one causes the destructor to exit on the line where it references `Zend_Session_Namespace`. For the last request in the chain (where `zend_controller_action->_redirect()` is *not* called, the destructor completes normally, and the data is written to session. – sennett Feb 24 '11 at 09:30

1 Answers1

2

This is a chicken and egg problem. If the session termination was left until all objects had been destroyed, then it would be impossible to use an object for the session save handler.

The register_shutdown_function workaround is fine. To avoid running the code twice, you should move your session termination code into a different function which you register. Manually calling __destruct() on an object which still exists seems a bit ugly anyway ;)

Long Ears
  • 4,886
  • 1
  • 21
  • 16
  • Thanks very much. I'll register a function (not __destruct) that writes the session data as a shutdown function. I get what you're saying about chicken and egg. I tried instantiating a new Zend_Session_Namespace object rather than pulling the instance from the class variable, which I guess would cause the destructor to be invoked at a later on in the shutdown routine? This didn't make any difference, so I imagine that the behaviour stems from $_SESSION itself. Thanks for the help - would vote up but not enough rep :) – sennett Feb 24 '11 at 10:13
  • Unfortunately exactly the same happened with the shutdown function as well. I think this is less a problem with using the destructor in this way (as it works fine when a new request is not initiated as described above), and more a problem with the redirect, and the way Zend initiates the new HTTP request, and the way PHP handles it (currently looking into this - http://www.php.net/manual/en/features.connection-handling.php). This means that, whilst the answer was correct, I asked the wrong question! – sennett Feb 24 '11 at 10:49
  • @sennett action->_redirect() uses the [Redirector helper](http://framework.zend.com/svn/framework/standard/trunk/library/Zend/Controller/Action/Helper/Redirector.php) which defaults to closing the session first. You can call `setCloseSessionOnExit(false)` on the helper to prevent this. – Long Ears Feb 24 '11 at 10:55
  • Thanks mate that's what I was after - I didn't expect that behaviour from Zend. Why does it do that? For other people: I was using an old version of Zend Framework. I used `$this->_redirector->setGotoUrl($forwardTo,array('exit' => false));` from the controller, instead of `setCloseSessionOnExit(false)`. – sennett Feb 25 '11 at 14:31