Update: solved
I finally figured out my problem (I think). I'm pretty sure the issue is that closures cannot be serialized, which means they cannot be stored in the session. The next issue then is that PHP was not returning a very useful error, and was breaking in an unexpected way, instead of just telling me that I couldn't serialize a closure.
I store my session data in a mysql database. I've had that part of my application in place and working well for quite a while. Today I tried to store a closure (i.e. anonymous function) in the session and that broke my otherwise very well behaved sessions.
My session management is handled by an object, which automatically calls session_write_close() when PHP attempts to destroy the object. I did this so that because otherwise, by the time PHP tries to close the session, my database connection (a mysqli object) has already been destroyed.
I take over the session handling like this:
// set the session save handler
session_set_save_handler(
array( $this, '_open' ),
array( $this, '_close' ),
array( $this, '_read' ),
array( $this, '_write' ),
array( $this, '_destroy' ),
array( $this, '_clean' )
);
which is pretty standard. The part that handles session closing is this:
public function __destruct()
{
// this variable will only be destroyed when the script is closing
// at this point it is safe to close the session
// if we wait for php to close the session then we will
// have lost the database connection, so we do it now
session_write_close();
}
// write session data
public function _write( $sid, $data )
{
// run query to write to database
$now = NOW;
$stmt = $this->mysqli->prepare( "REPLACE INTO $this->table (sid,time,data) VALUES (?,?,?)" );
$stmt->bind_param( 'sis', $sid, $now, $data );
// execute
$success = $stmt->execute();
// close
$stmt->close();
// and return
return $success;
}
// close session store
public function _close()
{
// close the database connection
$this->mysqli->close();
return true;
}
A couple print functions reveal that normally this works just as you would think: the __destruct() function is called, which calls session_write_close(), which is immediately followed up by calls to _write() and _close(). However, the moment I store a closure to the session:
$test = function($name)
{
print "Hello $name";
};
$_SESSION['test'] = $test;
Everything breaks. __destruct() is called as before, but execution never reaches the _write() or _close() functions. Instead I get these messages:
Warning: session_write_close() [function.session-write-close]: Failed to write session data (user). Please verify that the current setting of session.save_path is correct (/var/lib/php/session) in /var/www/vhosts/ambida.com/httpdocs/includes/core/session_handler.php on line 48
Fatal error: Exception thrown without a stack frame in Unknown on line 0
Which really makes no sense. It looks like it has reverted back to the default session handler, which would of course fail because the tmp file was never opened (since my function took over opening the session). I don't understand why storing a closure in the session would cause such a revert to happen, or why this is breaking in general. Any help would be appreciated.