One of my favourite things about PHP 5 is how nifty the object-oriented features are. I have been able to put together surprisingly robust web applications using simple class hierarchies and abstract classes, features that only took off in PHP with the version 5.0 release. One of the other things I have loved is using the the various built-in object-oriented classes provided by the runtime, most notably the mysqli and related functionality.
One extremely common task I complete is to use database storage for session data. When you are running multiple web servers and individual HTTP requests might go to different machines, trying to come up with a scheme to synchronise session data files between the individual servers becomes prohibitive. Far better a solution is to simply put these data in the database server along with everything else (see Figure 1) – your application servers hold only the code needed to generate the pages from the database.
In the past, I have written code as follows to store the session data after the script has finished executing:
$s_conn = NULL; // this is set to be a mysqli object in the session_open function.
function session_open($save_path, $session_name)
{
global $s_conn;
$s_conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_DB);
return TRUE;
}
// there are a bunch of other read, close, clean up functions etc.
function session_write_data($id, $session_data)
{
global $s_conn;
$x = $s_conn->real_escape_string($id);
$d = $s_conn->real_escape_string($session_data);
$s_conn->query('SET AUTOCOMMIT=0');
$q = "DELETE FROM SessionData WHERE session_id='$x'";
$s_conn->query($q);
$q = "INSERT INTO SessionData VALUES('$x', NOW(), '$d')";
$s_conn->query($q);
$s_conn->commit();
return TRUE;
}
Unfortunately, the combination of all of these nifty features has taken a turn for the worse in PHP 5.0.5 and greater (including PHP 5.1). When you execute the above code in these versions of PHP, you will now get the rather odd error:
Error: couldn't fetch mysqli.
What happened? Where did mysqli go, seeing as it was working earlier in the script, and in all previous versions of PHP 5?
The short answer is that the PHP team changed how objects are cleaned up and destroyed when PHP shuts down. All objects are now cleaned up before the session is closed on exit. Thus, if you use any objects in your session shutdown code, you are in trouble.
Are you simply out of luck then? Can you absolutely not use mysql in session_write_data as shown above? Fortunately, the situation is not so dire. There is a rather unpleasant hack that you can add to your code to make things continue to work. In our session handling code, we create a subclass of the mysqli class that, when instructed to destroy itself, insures that the session is shut down early and the data are written to the database.
We then instantiate this subclass of mysqli instead of using the regular old class.
class php505bugMysqli extends mysqli
{
public function __destruct(){
@session_write_close();
}
}
function session_open($save_path, $session_name)
{
global $s_conn;
$s_conn = new php505bugMysqli(DB_HOST, DB_USER, DB_PASS, DB_DB);
return TRUE;
}
If you are looking at this code and thinking “yeeeech”, you should be. It is hacky and unpleasant. It is, unfortunately, also the only way to make this work in current versions of PHP 5. Much noise has been appearing in various PHP bug reports on bugs.php.net, but the Zend folks have, thus far, been resolutely insisting this is not a serious breakage and arguing it is a documentation bug.
Here’s to hoping that they come to their senses sometime soon, or at least come up with a less awful solution to this problem. It flies directly in the face of all the advances PHP has made in the last year.
Got any other solutions or workarounds for this problem? Add a comment.
We were working on a major web application, and suddently, after upgrading to a new version of PHP, everything went to hell.
Changing APIs or functionality is inevitable and probably a good thing. Doing so without informing poeple is, however, bad practice in my book.
Now, I finally stumble upon this. Because I am REALLY terrible at sessions, I don't understand your solution to the problem as it relates to the example in the book. I would love to see a solution posted in the Errata to your book. Unfortunately, I can't even find an Errata section on your publishers website. Is there one?
Any suggestions would be greatly appreciated.
Hey Justin!
This isn't really an error in the book, but you're correct - it would be worth mentioning to readers of the book, as it IS quite annoying. I will create an updated errata article for the book and put it on this blog site.
Thanks.
I totally understand the book was not actually in error. Thanks for offering to put up an Errata section. I scoured all over the internet trying to find something about how to solve this. It was VERY hard to find this site too! Hopefully, other readers of your book will be able to use the errata section to add comments about the code, offer suggestions, etc.
Justin
Custom Session Handling in PHP5 script and my hosting people will be migrating to php5 from php4 very soon.
Can you please give me a hand in implementing it in the
Zend Custom Session Handling script thatt I am using now?
http://www.zend.com/zend/spotlight/code-gallery-wade8.php?article=code-gallery-wade8&kind=sl&id=4791&open=1&anc=0&view=1
I am not sure what needs to be added where and what needs to be changed!
Thank you
I've just run into this problem as I use objects for my database connection and session functions. The database object was getting destroyed before the session _write() and _close() functions were called (which relied on the database object).
I worked around it by defining this at the top of my script:
register_shutdown_function('session_write_close');
My session code is based on this:
http://www.nateklaiber.com/blog/2006/05/10/custom-php-session-handler
Hope that helps someone


