Ok. Filling up the hard drive is not good.
So, I wrote some PHP code that adds DB-based session management. And you can (optionally) set the timeout in code, so eventually it could be in the manager -- no need for PHP.INI changes on hosted system.
I’ve done some testing on my MODX development site, but any feedback would be appreciated.
Instructions:
1. Add this code to the \manager\includes\config.inc.php (near the bottom) just before existing comment:
//start cms session
// SAVE SESSIONS IN DB TABLE: modx_sessoin
@include_once "session_db.inc.php";
2. Then create a new file: \manager\includes\session_db.inc.php
This file will automatically create the new modx_session table AND install the custom session handlers.
<?php
// FILE: session_db.inc.php
// This is the name for the session table.
$g_sessTable = "modx_sessions"; // for backward compatibility with etomite .6
// Retrieve the session maximum lifetime (found in php.ini)
// Or you can just set your desired session time here.
// For example, setting to 300 will give you 5 minutes (60*5).
$g_lifetime = ini_get("session.gc_maxlifetime");
// Hold reference to instance of class defined below.
$g_modxMysqlSession = null;
// Need access to DBAPI, but that can't be instantiated without an entire DocumentParser
// object because of bug FS#393. For now just make $modx global (which will be re-created later)
// during startup.
include_once $base_path.'/manager/includes/document.parser.class.inc.php';
// Use for DB access
$modx = new DocumentParser;
// Override MODX version of session starter with this better one.
// See bottom of config.inc.php. If this function (below) exists, it overrides that one.
function startCMSSession(){
$g_modxMysqlSession = new ModxMysqlSession();
}
// This class implements the PHP API for Mysql sessions.
// Currently won't work with any other DB.
class ModxMysqlSession {
// Constructor
function ModxMysqlSession()
{
global $g_sessTable; // Name of session table, from this file.
global $site_sessionname; // From config.inc.php
// Create the session table, if we don't have one.
if(!$this->tableExists($g_sessTable))
{
$query = "CREATE TABLE $g_sessTable ( SID char(255) NOT NULL, expiration INT NOT NULL, value LONGTEXT NOT NULL, PRIMARY KEY(SID), INDEX ( expiration) );";
$result = $this->query($query);
if( !$result)
{
die( "ERROR: Failed to create session table: $g_sessTable");
}
}
// Set the session name.
session_name($site_sessionname);
// Setup Mysql API for handling sessions.
// THIS MUST BE DONE AFTER SESSION_NAME, OR YOU WILL GET FATAL ERROR ON LOGOUT.
ini_set('session.save_handler', 'user');
session_set_save_handler(
array(&$this, "open"),
array(&$this, "close"),
array(&$this, "read"),
array(&$this, "write"),
array(&$this, "destroy"),
array(&$this, "garbage_collect") );
// Works around bug in some newer PHP's.
// See: http://fr.php.net/manual/en/function.session-set-save-handler.php#61223
register_shutdown_function("session_write_close");
// Start the session.
session_start();
}
// My special version of dbapi query function. Actually returns errors.
// You can use the regular DBAPI function when FS#358 is fixed.
function query($sql) {
global $modx;
if(empty($modx->db->conn)||!is_resource($modx->db->conn)) {
$modx->db->connect();
}
$tstart = $modx->getMicroTime();
if(!$result = @mysql_query($sql, $modx->db->conn)) {
// Original printed a message here that caused problems.
return $result;
} else {
$tend = $modx->getMicroTime();
$totaltime = $tend-$tstart;
$modx->queryTime = $modx->queryTime+$totaltime;
if($modx->dumpSQL) {
$modx->queryCode .= "<fieldset style='text-align:left'><legend>Query ".($this->executedQueries+1)." - ".sprintf("%2.4f s", $totaltime)."</legend>".$sql."</fieldset><br />";
}
$modx->executedQueries = $modx->executedQueries+1;
return $result;
}
}
// Check to see if Table exists in current DB.
function tableExists($tableName)
{
global $dbase;
$tables = array();
$dbase_without_ticks = str_replace( "`", "", $dbase);
$tablesResult = $this->query("SHOW TABLES in $dbase_without_ticks;");
while ($row = mysql_fetch_row($tablesResult))
{
$tables[] = $row[0];
}
return(in_array($tableName, $tables));
}
// Cleanup any out of data sessions at startup.
function open($session_path, $session_name) {
global $g_lifetime;
global $site_sessionname; // From config.inc.php
// Cleanup any old sessions.
if( $session_name != $site_sessionname ) die( "Unexpected session name: $session_name");
return $this->garbage_collect( $g_lifetime);
}
// Doesn't actually do anything since the server connection is
// persistent. Keep in mind that although this function
// doesn't do anything in my particular implementation, I
// still must define it.
function close() {
return( true);
}
// Reads the session data from the database
function read($SID) {
global $g_sessTable;
$SID = mysql_escape_string($SID);
$query = "SELECT value FROM $g_sessTable WHERE SID = '$SID' AND expiration > ". time();
$result = $this->query($query);
if($result && (mysql_num_rows($result) == 1) )
{
$row = mysql_fetch_array($result);
return $row['value'];
}
else
{
return( ''); // Must return "" here.
}
}
// This function writes the session data to the database. If that SID // already exists, then the existing data will be updated.
function write($SID, $value) {
global $g_sessTable;
GLOBAL $g_lifetime;
$SID = mysql_escape_string($SID);
$value = mysql_escape_string( $value);
$expiration = time() + $g_lifetime;
$query = "INSERT INTO $g_sessTable VALUES('$SID', '$expiration', '$value')";
$result = $this->query($query);
if (!$result)
{
$query = "UPDATE $g_sessTable SET expiration = '$expiration', value = '$value' WHERE SID = '$SID' AND expiration >". time();
$result = $this->query($query);
if( !$result)
{
return( false);
}
}
return strlen(value); /* Should be bytes written, whatever that means in a Mysql */
}
// deletes all session information having input SID (only one row)
function destroy($SID) {
global $g_sessTable;
$SID = mysql_escape_string($SID);
$query = "DELETE FROM $g_sessTable WHERE SID = '$SID'";
$result = $this->query($query);
if( $result && ($result != -1))
{
return mysql_affected_rows($result);
}
else
{
return 0;
}
}
// deletes all sessions that have expired.
function garbage_collect($maxlifetime) {
global $g_sessTable;
$maxlifetime = mysql_escape_string($maxlifetime);
$query = "DELETE FROM $g_sessTable WHERE expiration < " . (time() - $maxlifetime);
$result = $this->query($query);
return( true);
}
}
?>
If you run into problems, let me know and I will fix it up.
-- Jorge.