After plenty of debugging it seems the problem was (embarrassingly) in my database session code, not a typical session problem. You can see my answer relating it here – Thanks
I understand this may be a duplicate of similar questions (e.g. one, two, three) but despite following what seems to be the best practices for this I’m still having problems.
When using the session_set_save_handler() to use my database session class the session data is cleared when the session begins on session2.php after being redirected from session1.php.
An overview of my observations:
- Data is saved into the database correctly in session1.php
- Data is lost on session_start() in session2.php
- Data is still in database after the redirect and before session_start() is called in session2.php
- Session ID remains the same and is stored in a cookie which is being sent back to the server in the request headers correctly
- Using PHP’s default Session handling it works okay
And note:
- exit() used after header()
- session_start() on every page before output
Have I made a silly typo? Made a daft error? Or is this a strange quirk?
Thanks in advance for any help offered.
Here’s the code (extracted into testing files while fixing this issue):
session1.php
<?php
require_once('session.php');
session_start();
$_SESSION['KEY'] = 'VALUE PHPSESSID: ' . session_id();
session_write_close();
header('Location: session2.php');
exit;
session2.php
<?php
require_once('session.php');
session_start();
// Nothing?
var_dump( $_SESSION );
session.php
<?php
define( "DB_HOST", 'localhost' );
define( "DB_USER", '******' );
define( "DB_PWD", '******' );
define( "DB_NAME", '******' );
require_once('class/DatabaseSessionHandler.php');
// Use the DatabaseSessionHandler class to handle sessions
$session_handler = new DatabaseSessionHandler;
// Set up the handler above as the default session handler
session_set_save_handler(
array($session_handler, 'open'),
array($session_handler, 'close'),
array($session_handler, 'read'),
array($session_handler, 'write'),
array($session_handler, 'destroy'),
array($session_handler, 'gc')
);
DatabaseSessionHandler.php
<?php
class DatabaseSessionHandler
{
protected $connection;
protected $session_life_time;
public function __construct()
{
// Ensure that everything is closed correctly as
// per warning on http://uk3.php.net/session_set_save_handler
register_shutdown_function( 'session_write_close' );
}
public function open( $save_path, $session_name )
{
$this->connection = new mysqli( DB_HOST, DB_USER, DB_PWD, DB_NAME );
$this->session_life_time = get_cfg_var( "session.gc_maxlifetime" );
if ( $this->connection->connect_error )
return false;
return true;
}
public function close()
{
$this->connection->close();
return true;
}
public function read( $session_id )
{
$data = '';
$statement = $this->connection->prepare( "SELECT `session_data`
FROM `session`
WHERE `session_id` = ? " );
$statement->bind_param( "s", $session_id );
$statement->execute();
$statement->bind_result( $data );
return (string) $data;
}
public function write( $session_id, $session_data )
{
$expiry_time = time() + $this->session_life_time;
$statement = $this->connection->prepare( "REPLACE INTO `session`
(`session_id`, `session_data`,
`expiry_time`)
VALUES (?, ?, ?)" );
$statement->bind_param( "ssi", $session_id, $session_data, $expiry_time );
if ( !$statement->execute() )
return false;
return true;
}
public function destroy( $session_id )
{
$statement = $this->connection->prepare( "DELETE FROM `session`
WHERE `session_id` = ?" );
$statement->bind_param( "s", $session_id );
if ( !$statement->execute() )
return false;
return true;
}
public function gc( $max_lifetime )
{
$current_time = time();
$statement = $this->connection->prepare( "DELETE FROM `session`
WHERE `expiry_time` < ?" );
$statement->bind_param( "i", $current_time );
if ( !$statement->execute() )
return false;
return true;
}
}
Well after extensive file logging and printing out variables (some of the session functions are called after the output to the browser is sent and as such you can’t output errors in the normal fashion) it turns out the issue was with my database session code.
I’d forgotten to add ‘$statement->fetch()’ after binding the MySQLi output to a variable and as such I was never really getting the data out of the database.
An embarrassing mistake but it served to drive home with the fact that if there’s something wrong with your software, more often than not it’s a problem in your code and not in the language or libraries you’re using.
Thanks to those who commented and answered, I’ve learnt a lot about PHP sessions over the past couple of days because of this.