0

I've been trying to save PHP session data in a MySQL database, but can't get it to work.

For a simple example, here's code that should increment a counter with each visit. I've seen other examples, etc. but can someone please tell me why this code isn't working? (Apache/2.2.11 (Win32) DAV/2 mod_ssl/2.2.11 OpenSSL/0.9.8i PHP/5.2.9 MySQL client version: 5.0.51a)

Here is the mysql database table:

CREATE TABLE IF NOT EXISTS `sessions` (
  `session_ID` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
  `session_data` mediumblob NOT NULL,
  `access` int(10) unsigned NOT NULL,
  PRIMARY KEY (`session_ID`),
  KEY `access` (`access`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

And the PHP code (just plug in your db credentials):

<?PHP

function mysession_open()
{

    global $sdbc;   // Session Database Connection

    if ($sdbc) {
        return true;
    } else {
        return false;
    }

}

function mysession_close()
{

    global $sdbc;

    return mysqli_close($sdbc);

}

function mysession_read($session_id)
{
    global $sdbc;

    $session_id = mysqli_real_escape_string($sdbc, $session_id);

    $sql_sel    = "SELECT session_data FROM sessions WHERE session_id = '$session_id'";
    $data_sel   = mysqli_query($sdbc, $sql_sel);
    $row_sel     = mysqli_fetch_array($data_sel);

    if (isset($row_sel['session_data'])) {
        return $row_sel['session_data'];
    } else {
        return '';
    }

}

function mysession_write($session_id, $session_data)
{
    global $sdbc;

    $access = time();

    $session_id    = mysqli_real_escape_string($sdbc, $session_id);
    $access        = mysqli_real_escape_string($sdbc, $access);
    $session_data   = mysqli_real_escape_string($sdbc, $session_data);

    $sql_write  =   "REPLACE INTO sessions (session_ID, session_data, access) " .
                    "VALUES ('$session_id', '$session_data', '$access')";

    return mysqli_query($sdbc, $sql_write);

}

function mysession_destroy($session_id)
{
    global $sdbc;

    $session_id = mysqli_real_escape_string($sdbc, $session_id);

    return mysqli_query($sdbc, $sql_del);
}


function mysession_gc($max)
{
    global $sdbc;

    $old = time() - $max;
    $old = mysqli_real_escape_string($sdbc, $old);

    $sql_old = "DELETE FROM sessions WHERE access < '$old'";

    return mysqli_query($sdbc, $sql_old);

}

global $sdbc;
$sdbc = mysqli_connect('localhost', '...', '...', '...') or die('Could not connect to SDBC');

session_set_save_handler('mysession_open','mysession_close','mysession_read','mysession_write','mysession_destroy','mysession_gc');
session_start();

if (isset($_SESSION['counter'])) {
    echo "counter is already set and it is " . $_SESSION['counter'] . '<br />';
    $_SESSION['counter']++;
} else {
    echo "counter is not set.  setting to 1<br />";
    $_SESSION['counter'] = 1;
}

echo "<br />Dumping SESSION data:<br />";
var_dump($_SESSION);

session_write_close();

?>

Thanks in advance for your help.

If you comment out the session_set_save_handler line of code, it works fine (it increments). But using the save handler it does not.

TMG
  • 500
  • 2
  • 6
  • 13
  • Using the save handler it does not increment. I think the error occurs when the function tries to write to the db ('REPLACE ...') because mysqli_query will return false from a valid db object handle, but PHP will not send that debug info to the console. – TMG Oct 28 '10 at 16:52

2 Answers2

2

None of your query calls have any error checking. Instead of blindly assuming the database portion works, do some basic error checking at each stage, e.g:

function mysession_write($session_id, $session_data) {
    global $sdbc;

    [...snip...]

    $stmt = mysqli_query($sdbc, $sql_write);
    if ($stmt === FALSE) {
       error_log("Failed to write session $session_id: " . mysqli_error($sdbc);
    }
    return($stmt);
}

There's only way way for a query to succeed, but zillions of ways to fail.

Marc B
  • 356,200
  • 43
  • 426
  • 500
  • Thanks; I figured it out. DB user didn't have DELETE permission. Actually, I had all kinds of debug code but you can't use error_log (or anything else) as you have it because it won't go to the stdout: (per php.net: "The "write" handler is not executed until after the output stream is closed. Thus, output from debugging statements in the "write" handler will never be seen in the browser...write to a file instead.") So this works:if (!$result) { error_log("Failed to write session $session_id: " . mysqli_error($sdbc) . "\n", 3, 'errors.txt'); } – TMG Oct 28 '10 at 20:49
  • I usually have my php installs configured to write out its own error log (`error_log` ini setting) and keep a window open with `tail -f` on the log file while debugging. – Marc B Oct 28 '10 at 21:36
0

From the manual:
"Warning As of PHP 5.0.5 the write and close handlers are called after object destruction and therefore cannot use objects or throw exceptions. The object destructors can however use sessions.

It is possible to call session_write_close() from the destructor to solve this chicken and egg problem. "

Mxyk
  • 10,678
  • 16
  • 57
  • 76
jasf
  • 1
  • Please pay attention to the date of the original question. This question was answered nearly a year ago with an accepted answer. – AJ. May 23 '11 at 16:36