Ripped out the MySQL session handling
Write heavy operations like sessions fare much better in Redis or (sic) Memcache[d] so I've dropped support for MySQL sessions entirely.
This commit is contained in:
parent
097911a667
commit
f1ecc27029
1 changed files with 43 additions and 248 deletions
|
@ -24,95 +24,24 @@
|
||||||
* the datasource will default to the value in
|
* the datasource will default to the value in
|
||||||
* $config['pickles']['datasource'] and if the table will default to
|
* $config['pickles']['datasource'] and if the table will default to
|
||||||
* "sessions". The format is as follows:
|
* "sessions". The format is as follows:
|
||||||
*
|
|
||||||
* $config = array(
|
|
||||||
* 'pickles' => array(
|
|
||||||
* 'session' => array(
|
|
||||||
* 'datasource' => 'mysql',
|
|
||||||
* 'table' => 'sessions',
|
|
||||||
* )
|
|
||||||
* )
|
|
||||||
* );
|
|
||||||
*
|
|
||||||
* In addition to the configuration variables, a table in your database
|
|
||||||
* must be created. The [MySQL] table schema is as follows:
|
|
||||||
*
|
|
||||||
* CREATE TABLE sessions (
|
|
||||||
* id varchar(32) COLLATE utf8_unicode_ci NOT NULL,
|
|
||||||
* session text COLLATE utf8_unicode_ci NOT NULL,
|
|
||||||
* expires_at datetime NOT NULL,
|
|
||||||
* PRIMARY KEY (id),
|
|
||||||
* INDEX (expires_at)
|
|
||||||
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
|
||||||
*
|
|
||||||
* Note: The reason for not using a model class was to avoid a naming
|
|
||||||
* conflict between the Session model and the Session class itself. This
|
|
||||||
* will eventually be resolved when I abandon full 5.x support and migrate
|
|
||||||
* to 5.3+ (assuming that ever happens).
|
|
||||||
*/
|
*/
|
||||||
class Session extends Object
|
class Session extends Object
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Handler
|
|
||||||
*
|
|
||||||
* What the session is being handled by.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $handler = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accessed At
|
|
||||||
*
|
|
||||||
* The UNIX timestamp of when the page was accessed.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
private $accessed_at = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Time to Live
|
|
||||||
*
|
|
||||||
* The number of seconds the session should remain active. Corresponds
|
|
||||||
* to the INI variable session.gc_maxlifetime
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
private $time_to_live = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Datasource
|
|
||||||
*
|
|
||||||
* Name of the datasource, defaults to whatever the default datasource
|
|
||||||
* is defined to in config.php
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $datasource = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Table
|
|
||||||
*
|
|
||||||
* Name of the database table in the aforementioned datasource that
|
|
||||||
* holds the session data. The expected schema is defined above.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $table = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* All of our set up logic for the session in contained here. This
|
* All of our set up logic for the session in contained here. This class is
|
||||||
* object is initially instantiated from pickles.php and the session
|
* initially instantiated from pickles.php. Non-file handlers need to be
|
||||||
* callbacks are established here. All variables are driven from
|
* configured in the site's config. MySQL support was dropped in favor of
|
||||||
* php.ini and/or the site config. Once configured, the session is
|
* in memory stores or simply relying on file based sessions. Why? Because
|
||||||
* started automatically.
|
* using MySQL for sessions is very write intensive and having done it in
|
||||||
|
* the past I don't recommend it. If you run a single server, files are
|
||||||
|
* good enough if your volume is lower. Memcache[d] is fine if you don't
|
||||||
|
* mind logging all of your users off your site when you restart the
|
||||||
|
* service and/or you run out of memory for the process. Redis is the best
|
||||||
|
* choice as it can be configured to be persistent and lives in memory.
|
||||||
|
* This is assuming you don't just want to roll your own sessions, which is
|
||||||
|
* pretty damn easy as well.
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
@ -121,64 +50,60 @@ class Session extends Object
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
// Sets up our configuration variables
|
// Sets up our configuration variables
|
||||||
if (isset($this->config->pickles['session']))
|
|
||||||
{
|
|
||||||
$session = $this->config->pickles['session'];
|
|
||||||
$version = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->config->pickles['sessions']))
|
if (isset($this->config->pickles['sessions']))
|
||||||
{
|
{
|
||||||
$session = $this->config->pickles['sessions'];
|
$session = $this->config->pickles['sessions'];
|
||||||
$version = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$datasources = $this->config->datasources;
|
$datasources = $this->config->datasources;
|
||||||
|
|
||||||
$this->handler = 'files';
|
$handler = 'files';
|
||||||
$datasource = false;
|
$datasource = false;
|
||||||
$table = 'sessions';
|
|
||||||
|
|
||||||
if (isset($datasources[$session]))
|
if (isset($datasources[$session]))
|
||||||
{
|
{
|
||||||
$datasource = $datasources[$session];
|
$datasource = $datasources[$session];
|
||||||
$this->handler = $datasource['type'];
|
$handler = $datasource['type'];
|
||||||
|
|
||||||
if (isset($datasource['hostname'], $datasource['port']))
|
if ($handler != 'files')
|
||||||
{
|
{
|
||||||
$host = 'tcp://' . $datasource['hostname'] . ':' . $datasource['port'];
|
if (isset($datasource['hostname'], $datasource['port']))
|
||||||
|
{
|
||||||
|
$host = ($handler != 'memcached' ? 'tcp://' : '')
|
||||||
|
. $datasource['hostname'] . ':' . $datasource['port'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception('You must provide both the hostname and port for the datasource.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($this->handler)
|
switch ($handler)
|
||||||
{
|
{
|
||||||
case 'memcache':
|
case 'memcache':
|
||||||
ini_set('session.save_handler', 'memcache');
|
ini_set('session.save_handler', 'memcache');
|
||||||
ini_set('session.save_path', $host . '?persistent=1&weight=1&timeout=1&retry_interval=15');
|
ini_set('session.save_path', $host . '?persistent=1&weight=1&timeout=1&retry_interval=15');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// @todo memcached
|
case 'memcached':
|
||||||
|
ini_set('session.save_handler', 'memcached');
|
||||||
case 'mysql':
|
ini_set('session.save_path', $host);
|
||||||
// Sets our access time and time to live
|
|
||||||
$this->accessed_at = time();
|
|
||||||
$this->time_to_live = ini_get('session.gc_maxlifetime');
|
|
||||||
|
|
||||||
$this->datasource = $datasource;
|
|
||||||
$this->table = $table;
|
|
||||||
|
|
||||||
// Gets a database instance
|
|
||||||
$this->db = Database::getInstance($this->datasource);
|
|
||||||
|
|
||||||
// Initializes the session
|
|
||||||
$this->initialize();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'redis':
|
case 'redis':
|
||||||
// Keep in mind that the database value is ignored by phpredis
|
$save_path = $host . '?weight=1';
|
||||||
$save_path = $host . '?weight=1'
|
|
||||||
. (isset($datasource['database']) ? '&database=' . $datasource['database'] : '')
|
// Database ignored by phpredis when this was coded
|
||||||
. (isset($datasource['prefix']) ? '&prefix=' . $datasource['prefix'] : '');
|
if (isset($datasource['database']))
|
||||||
|
{
|
||||||
|
$save_path .= '&database=' . $datasource['database'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($datasource['prefix']))
|
||||||
|
{
|
||||||
|
$save_path .= '&prefix=' . $datasource['prefix'];
|
||||||
|
}
|
||||||
|
|
||||||
ini_set('session.save_handler', 'redis');
|
ini_set('session.save_handler', 'redis');
|
||||||
ini_set('session.save_path', $save_path);
|
ini_set('session.save_path', $save_path);
|
||||||
|
@ -190,6 +115,7 @@ class Session extends Object
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't start sessions for people without a user agent and bots.
|
||||||
if (isset($_SERVER['HTTP_USER_AGENT'])
|
if (isset($_SERVER['HTTP_USER_AGENT'])
|
||||||
&& !String::isEmpty($_SERVER['HTTP_USER_AGENT'])
|
&& !String::isEmpty($_SERVER['HTTP_USER_AGENT'])
|
||||||
&& !preg_match('/(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)/i', $_SERVER['HTTP_USER_AGENT']))
|
&& !preg_match('/(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)/i', $_SERVER['HTTP_USER_AGENT']))
|
||||||
|
@ -198,137 +124,6 @@ class Session extends Object
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Destructor
|
|
||||||
*
|
|
||||||
* Runs garbage collection and closes the session. I'm not sure if the
|
|
||||||
* garbage collection should stay as it could be accomplished via
|
|
||||||
* php.ini variables. The session_write_close() is present to combat a
|
|
||||||
* chicken and egg scenario in earlier versions of PHP 5.
|
|
||||||
*/
|
|
||||||
public function __destruct()
|
|
||||||
{
|
|
||||||
if ($this->handler == 'mysql')
|
|
||||||
{
|
|
||||||
$this->gc($this->time_to_live);
|
|
||||||
session_write_close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the Session
|
|
||||||
*
|
|
||||||
* This method exists to combat the fact that calling session_destroy()
|
|
||||||
* also clears out the save handler. Upon destorying a session this
|
|
||||||
* method is called again so the save handler is all set.
|
|
||||||
*/
|
|
||||||
public function initialize()
|
|
||||||
{
|
|
||||||
// Sets up the session handler
|
|
||||||
session_set_save_handler(
|
|
||||||
array($this, 'open'),
|
|
||||||
array($this, 'close'),
|
|
||||||
array($this, 'read'),
|
|
||||||
array($this, 'write'),
|
|
||||||
array($this, 'destroy'),
|
|
||||||
array($this, 'gc')
|
|
||||||
);
|
|
||||||
|
|
||||||
register_shutdown_function('session_write_close');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the Session
|
|
||||||
*
|
|
||||||
* Since the session is in the database, opens the database connection.
|
|
||||||
* This step isn't really necessary as the Database object is smart
|
|
||||||
* enough to open itself up upon execute.
|
|
||||||
*/
|
|
||||||
public function open()
|
|
||||||
{
|
|
||||||
session_regenerate_id();
|
|
||||||
|
|
||||||
return $this->db->open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the Session
|
|
||||||
*
|
|
||||||
* Same as above, but in reverse.
|
|
||||||
*/
|
|
||||||
public function close()
|
|
||||||
{
|
|
||||||
return $this->db->close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the Session
|
|
||||||
*
|
|
||||||
* Checks the database for the session ID and returns the session data.
|
|
||||||
*
|
|
||||||
* @param string $id session ID
|
|
||||||
* @return string serialized session data
|
|
||||||
*/
|
|
||||||
public function read($id)
|
|
||||||
{
|
|
||||||
$sql = 'SELECT session FROM `' . $this->table . '` WHERE id = ?;';
|
|
||||||
|
|
||||||
$session = $this->db->fetch($sql, array($id));
|
|
||||||
|
|
||||||
return isset($session[0]['session']) ? $session[0]['session'] : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the Session
|
|
||||||
*
|
|
||||||
* When there's changes to the session, writes the data to the
|
|
||||||
* database.
|
|
||||||
*
|
|
||||||
* @param string $id session ID
|
|
||||||
* @param string $session serialized session data
|
|
||||||
* @return boolean whether the query executed correctly
|
|
||||||
*/
|
|
||||||
public function write($id, $session)
|
|
||||||
{
|
|
||||||
$sql = 'REPLACE INTO `' . $this->table . '` VALUES (?, ? ,?);';
|
|
||||||
|
|
||||||
$parameters = array($id, $session, date('Y-m-d H:i:s', strtotime('+' . $this->time_to_live . ' seconds')));
|
|
||||||
|
|
||||||
return $this->db->execute($sql, $parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys the Session
|
|
||||||
*
|
|
||||||
* Deletes the session from the database.
|
|
||||||
*
|
|
||||||
* @param string $id session ID
|
|
||||||
* @return boolean whether the query executed correctly
|
|
||||||
*/
|
|
||||||
public function destroy($id)
|
|
||||||
{
|
|
||||||
$sql = 'DELETE FROM `' . $this->table . '` WHERE id = ?;';
|
|
||||||
|
|
||||||
return $this->db->execute($sql, array($id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Garbage Collector
|
|
||||||
*
|
|
||||||
* This is who you call when you got trash to be taken out.
|
|
||||||
*
|
|
||||||
* @param integer $time_to_live number of seconds a session is active
|
|
||||||
* @return boolean whether the query executed correctly
|
|
||||||
*/
|
|
||||||
public function gc($time_to_live)
|
|
||||||
{
|
|
||||||
$sql = 'DELETE FROM `' . $this->table . '` WHERE expires_at < ?;';
|
|
||||||
|
|
||||||
$parameters = array(date('Y-m-d H:i:s', $this->accessed_at - $time_to_live));
|
|
||||||
|
|
||||||
return $this->db->execute($sql, $parameters);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue