PHP Standalone Session Database Storage Operations Class Sharing
- 2021-06-29 10:33:25
- OfStack
Code directly:
class DbSession
{
const TYPE_INT = 1;
const TYPE_STR = 2;
/**
* Database configration
*
* @var array
*/
private $_config = array(
' host' => '127.0.0.1 ' ,
' port' => 3306,
' username' => ' root',
' password' => ' root',
' dbname' => ' db_mylab',
' tablename' => ' t_sessions',
' cookie_prefix' => ' mylab_',
' cookiepath' => ' /',
' cookiedomain' => " ,
' cookie_timeout' => 900
);
/**
* Table fields type array
*
* @var array
*/
private $_db_fields = array(
' crc32sid' => self::TYPE_INT,
' sessionhash' => self::TYPE_STR,
' idhash' => self::TYPE_STR,
' userid' => self::TYPE_INT,
' ipaddress' => self::TYPE_STR,
' lastactivity' => self::TYPE_STR,
' location' => self::TYPE_STR,
' loggedin' => self::TYPE_INT,
' heartbeat' => self::TYPE_STR
);
/**
* db obj
*
* @var mysqli object
*/
private $_mysqli = null;
/**
* Weather the session was created or existed previously
*
* @var bool
*/
private $_created = false;
/**
* Array of changes.
*
* @var array
*/
private $_changes = array();
/**
* @var bool
*/
private $_db_inited = false;
/**
* session host
*
* @var string
*/
private $_session_host = " ;
/**
* session idhash
*
* @var string
*/
private $_session_idhash = " ;
private $_dbsessionhash = " ;
private $_vars = array();
public function __construct()
{
$this->_dbsessionhash = addslashes($this->get_cookie( ' sessionhash'));
$this->_session_host = substr($_SERVER[ ' REMOTE_ADDR'], 0, 15);
#This should *never* change during a session
$this->_session_idhash = md5($_SERVER[ ' HTTP_USER_AGENT'] . self::fetch_substr_ip(self::fetch_alt_ip()) );
$this->_init_config();
$this->init_db();
$gotsession = false;
if ($this->_dbsessionhash)
{
$sql = '
SELECT *
FROM ' . $this->_config[ ' tablename'] . '
WHERE crc32sid = ' . sprintf( ' %u', crc32($this->_dbsessionhash)) . '
AND sessionhash = ' ' . $this->_dbsessionhash . ' '
AND idhash = ' ' . $this->_session_idhash . ' '
AND heartbeat > ' ' . date( ' Y-m-d H:i:s' ,TIMENOW � $this->_config[ ' cookie_timeout']) . ' ' ' ;
//echo $sql;exit;
$result = $this->_mysqli->query($sql);
$session = $result->fetch_array(MYSQLI_ASSOC);
if ($session AND ($this->fetch_substr_ip($session[ ' ipaddress']) == $this->fetch_substr_ip($this->_session_host)))
{
$gotsession = true;
$this->_vars = $session;
$this->_created = false;
}
}
if ($gotsession == false)
{
$this->_vars = $this->fetch_session();
$this->_created = true;
$gotsession = true;
}
if ($this->_created == false)
{
$this->set( ' lastactivity', date( ' Y-m-d H:i:s', TIMENOW));
$this->set( ' location', $_SERVER[ ' REQUEST_URI']);
}
}
/**
* Builds an array that can be used to build a query to insert/update the session
*
* @return array Array of column name => prepared value
*/
private function _build_query_array()
{
$return = array();
foreach ($this->_db_fields AS $fieldname => $cleantype)
{
switch ($cleantype)
{
case self::TYPE_INT:
$cleaned = is_numeric($this->_vars["$fieldname"]) ? $this->_vars["$fieldname"] : intval($this->_vars["$fieldname"]);
break;
case self::TYPE_STR:
default:
$cleaned = "'" . addslashes($this->_vars["$fieldname"]) . "'";
}
$return["$fieldname"] = $cleaned;
}
return $return;
}
/**
* Sets a session variable and updates the change list.
*
* @param string Name of session variable to update
* @param string Value to update it with
*/
public function set($key, $value)
{
if ($this->_vars["$key"] != $value)
{
$this->_vars["$key"] = $value;
$this->_changes["$key"] = true;
}
}
public function get($key)
{
return $this->_vars["$key"];
}
public function unsetchangedvar($var)
{
if (isset($this->_changes["$var"]))
{
unset($this->_changes["$var"]);
}
}
/**
* Fetches a valid sessionhash value, not necessarily the one tied to this session.
*
* @return string 32-character sessionhash
*/
static function fetch_sessionhash()
{
return hash( ' md5 ' , TIMENOW . rand(1, 100000) . uniqid() );
}
private function _init_config()
{
$registry = Zend_Registry::getInstance();
$config = $registry->get( ' config');
$this->_config[ ' host'] = $config->database->params->host;
$this->_config[ ' port'] = $config->database->params->port;
$this->_config[ ' username'] = $config->database->params->username;
$this->_config[ ' password'] = $config->database->params->password;
$this->_config[ ' dbname'] = $config->database->params->dbname;
$this->_config[ ' tablename'] = $config->database->session->tablename;
}
/**
* initialize database connection
*/
public function init_db()
{
if ($this->_db_inited)
{
return true;
}
//mysqli_report(MYSQLI_REPORT_OFF);
$this->_mysqli = new mysqli(
$this->_config[ ' host'],
$this->_config[ ' username'],
$this->_config[ ' password'],
$this->_config[ ' dbname'],
$this->_config[ ' port']
);
/* check connection */
if (mysqli_connect_errno())
{
// printf("Connect failed: %sn", mysqli_connect_error());
// echo ' in ' , __FILE__, ' on line ' , __LINE__;
echo "{ success: false, errors: { reason: ' Connect failed: " . addslashes( mysqli_connect_error() ) . "' }}";
exit();
}
$this->_mysqli->query( ' set names latin1 ' );
$this->_db_inited = true;
return true;
}
/**
* Fetches an alternate IP address of the current visitor, attempting to detect proxies etc.
*
* @return string
*/
static function fetch_alt_ip()
{
$alt_ip = $_SERVER[ ' REMOTE_ADDR'];
if (isset($_SERVER[ ' HTTP_CLIENT_IP']))
{
$alt_ip = $_SERVER[ ' HTTP_CLIENT_IP'];
}
else if (isset($_SERVER[ ' HTTP_FROM']))
{
$alt_ip = $_SERVER[ ' HTTP_FROM'];
}
return $alt_ip;
}
/**
* Returns the IP address with the specified number of octets removed
*
* @param string IP address
*
* @return string truncated IP address
*/
static function fetch_substr_ip($ip, $length = null)
{
return implode( ' .', array_slice(explode( ' .', $ip), 0, 4 � $length));
}
/**
* Fetches a default session. Used when creating a new session.
*
* @param integer User ID the session should be for
*
* @return array Array of session variables
*/
public function fetch_session($userid = 0)
{
$sessionhash = self::fetch_sessionhash();
$this->set_cookie( ' sessionhash', $sessionhash);
return array(
' crc32sid' => sprintf( ' %u', crc32($sessionhash)),
' sessionhash' => $sessionhash,
' idhash' => $this->_session_idhash,
' userid' => $userid,
' ipaddress' => $this->_session_host,
' lastactivity' => date( ' Y-m-d H:i:s', TIMENOW),
' location' => $_SERVER[ ' REQUEST_URI'],
' loggedin' => $userid ? 1 : 0,
' heartbeat' => date( ' Y-m-d H:i:s', TIMENOW)
);
}
public function get_cookie($cookiename)
{
$full_cookiename = $this->_config[ ' cookie_prefix'] . $cookiename;
if (isset($_COOKIE[$full_cookiename]))
{
return $_COOKIE[$full_cookiename];
}
else
{
return false;
}
}
public function set_cookie($name, $value = " , $permanent = 1, $allowsecure = true)
{
if ($permanent)
{
$expire = TIMENOW + 60 * 60 * 24 * 365;
}
else
{
$expire = 0;
}
if ($_SERVER[ ' SERVER_PORT'] == '443 ' )
{
// we're using SSL
$secure = 1;
}
else
{
$secure = 0;
}
// check for SSL
$secure = ((REQ_PROTOCOL === ' https' AND $allowsecure) ? true : false);
$name = $this->_config[ ' cookie_prefix'] . $name;
$filename = ' N/A';
$linenum = 0;
if (!headers_sent($filename, $linenum))
{ // consider showing an error message if there not sent using above variables?
if ($value == " AND strlen($this->_config[ ' cookiepath']) > 1 AND strpos($this->_config[ ' cookiepath'], ' /') !== false)
{
// this will attempt to unset the cookie at each directory up the path.
// ie, cookiepath = /test/abc/. These will be unset: /, /test, /test/, /test/abc, /test/abc/
// This should hopefully prevent cookie conflicts when the cookie path is changed.
$dirarray = explode( ' /', preg_replace( ' #/+$#', " , $this->_config[ ' cookiepath']));
$alldirs = " ;
foreach ($dirarray AS $thisdir)
{
$alldirs .= "$thisdir";
if (!empty($thisdir))
{ // try unsetting without the / at the end
setcookie($name, $value, $expire, $alldirs, $this->_config[ ' cookiedomain'], $secure);
}
$alldirs .= "/";
setcookie($name, $value, $expire, $alldirs, $this->_config[ ' cookiedomain'], $secure);
}
}
else
{
setcookie($name, $value, $expire, $this->_config[ ' cookiepath'], $this->_config[ ' cookiedomain'], $secure);
}
}
else if (!DEBUG)
{
echo "can't set cookies";
}
}
private function _save()
{
$cleaned = $this->_build_query_array();
if ($this->_created)
{
//var_dump($cleaned);
# insert query
$this->_mysqli->query( '
INSERT IGNORE INTO ' . $this->_config[ ' tablename'] . '
( ' . implode( ' ,', array_keys($cleaned)) . ' )
VALUES
( ' . implode( ' ,', $cleaned). ' )
' );
}
else
{
# update query
$update = array();
foreach ($cleaned AS $key => $value)
{
if (!empty($this->_changes["$key"]))
{
$update[] = "$key = $value";
}
}
if (sizeof($update) > 0)
{
$sql = ' UPDATE ' . $this->_config[ ' tablename'] . '
SET ' . implode( ' , ' , $update) . '
WHERE crc32sid = ' . sprintf( ' %u', crc32($this->_dbsessionhash)) . '
AND sessionhash = ' ' . $this->_dbsessionhash . ' ' ' ;
//echo $sql;
$this->_mysqli->query($sql);
}
}
}
public function getOnlineUserNum()
{
$sql = '
SELECT count(*) as cnt
FROM ' . $this->_config[ ' tablename'] . '
WHERE loggedin = 1
AND heartbeat > ' ' . date( ' Y-m-d H:i:s' ,TIMENOW � $this->_config[ ' cookie_timeout']) . ' ' ' ;
$result = $this->_mysqli->query($sql);
$row = $result->fetch_array(MYSQLI_ASSOC);
return $row[ ' cnt'];
}
private function _gc()
{
$rand_num = rand(); # randow integer between 0 and getrandmax()
if ($rand_num < 100)
{
$sql = ' DELETE FROM ' . $this->_config[ ' tablename'] . '
WHERE heartbeat < ' ' . date( ' Y-m-d H:i:s' ,TIMENOW � $this->_config[ ' cookie_timeout']) . ' ' ' ;
$this->_mysqli->query($sql);
}
}
public function __destruct()
{
$this->_save();
$this->_gc();
$this->_mysqli->close();
}
}