* @link http://www.pradosoft.com/
* @copyright Copyright © 2007 PradoSoft
* @license http://www.pradosoft.com/license/
* @version $Id: TMemCacheSession.php $
* @package System.Web
*/
/**
* TMemCacheSession class
*
* TMemCacheSession provides access for storing sessions in memcache.
* Beware, session data will be stored in memory. Old data will be flushed
* if you run out of cache memory. Configure your memcache carefully.
* Keep in mind that memcached is a cache not a database. It is fast,
* but not reliable storage.
*
*
*
*
*
*
* @author Carl G. Mathisen
* @version $Id: TMemCacheSession.php $
* @package System.Web
* @since 3.1.0
*/
class TMemCacheSession extends THttpSession
{
/**
* @var MemCache
*/
private $_memCache=null;
/**
* @var boolean if the module is initialized
*/
private $_initialized=false;
/**
* File extension of external configuration file
*/
const CONFIG_FILE_EXT='.xml';
/**
* @var array list of servers available
*/
private $_servers=array();
/**
* @var string external configuration file
*/
private $_configFile=null;
/**
* @var string
*/
private $_prefix = 'PRADO';
/**
* @var string
*/
private $_host = 'localhost';
/**
* @var integer
*/
private $_port = 11211;
/**
* Connecting to memcached. If no servers are defined in config, we will use
*the default server {@see Host} and {@see Port}, localhost and 11211 respectively.
*/
public function init($config)
{
$this->setAutoStart(true);
$this->setUseCustomStorage(true);
if(!extension_loaded('memcache'))
throw new TConfigurationException('memcache_extension_required');
if($this->_configFile!==null)
{
if(is_file($this->_configFile))
{
$dom=new TXmlDocument;
$dom->loadFromFile($this->_configFile);
$this->loadConfig($dom);
}
else
throw new TConfigurationException('memcachesession_configfile_invalid',$this->_configFile);
}
$this->loadConfig($config);
$this->_memCache = new MemCache;
if(count($this->_servers))
{
foreach($this->_servers as $server)
{
Prado::trace('Adding server '.$server['Host'].' from serverlist', 'System.Web.TMemCacheSession');
if($this->_memCache->addServer($server['Host'],$server['Port'],$server['Persistent'],
$server['Weight'],$server['Timeout'],$server['RetryInterval'])===false)
throw new TConfigurationException('memcache_connection_failed',$server['Host'],$server['Port']);
}
}
else
{
Prado::trace('Adding server '.$this->_host.' from serverlist', 'System.Web.TMemCacheSession');
if($this->_memCache->addServer($this->_host,$this->_port)===false)
throw new TConfigurationException('memcache_connection_failed',$this->_host,$this->_port);
}
$this->_initialized=true;
}
/**
* Loads configuration from an XML element
* @param TXmlElement configuration node
* @throws TConfigurationException if log route class or type is not specified
*/
private function loadConfig($xml)
{
if($xml instanceof TXmlElement)
{
foreach($xml->getElementsByTagName('server') as $serverConfig)
{
$properties=$serverConfig->getAttributes();
if(($host=$properties->remove('Host'))===null)
throw new TConfigurationException('memcachesession_serverhost_required');
if(($port=$properties->remove('Port'))===null)
throw new TConfigurationException('memcachesession_serverport_required');
if(!is_numeric($port))
throw new TConfigurationException('memcachesession_serverport_invalid');
$server = array('Host'=>$host,'Port'=>$port,'Weight'=>1,'Timeout'=>1800,'RetryInterval'=>15,'Persistent'=>true);
$checks = array(
'Weight'=>'memcachesession_serverweight_invalid',
'Timeout'=>'memcachesession_servertimeout_invalid',
'RetryInterval'=>'memcachesession_serverretryinterval_invalid'
);
foreach($checks as $property=>$exception)
{
$value=$properties->remove($property);
if($value!==null && is_numeric($value))
$server[$property]=$value;
else if($value!==null)
throw new TConfigurationException($exception);
}
$server['Persistent']= TPropertyValue::ensureBoolean($properties->remove('Persistent'));
$this->_servers[]=$server;
}
}
}
/**
* Session open handler.
* @param string session save path
* @param string session name
* @return boolean whether session is opened successfully
*/
public function _open($savePath,$sessionName)
{
return true;
}
/**
* Session close handler.
* @return boolean whether session is closed successfully
*/
public function _close()
{
return true;
}
/**
* Session read handler.
* @param string session ID
* @return string the session data
*/
public function _read($id)
{
$key = $this->calculateKey($id);
return $this->_memCache->get($key);
}
/**
* Session write handler.
* @param string session ID
* @param string session data
* @return boolean whether session write is successful
*/
public function _write($id,$data)
{
$key = $this->calculateKey($id);
$res = $this->_memCache->set($key,$data,MEMCACHE_COMPRESSED,$this->_timeOut);
return $res;
}
/**
* Session destroy handler.
* This method should be overriden if {@link setUseCustomStorage UseCustomStorage} is set true.
* @param string session ID
* @return boolean whether session is destroyed successfully
*/
public function _destroy($id)
{
$key = $this->calculateKey($id);
return $this->_memCache->delete($key);
}
/**
* Session GC (garbage collection) handler.
* Memcache has it's own garbage collection
* @param integer the number of seconds after which data will be seen as 'garbage' and cleaned up.
* @return boolean whether session is GCed successfully
*/
public function _gc($maxLifetime)
{
return true;
}
/**
* @return string host name of the memcache server
*/
public function getHost()
{
return $this->_host;
}
/**
* @param string host name of the memcache server
* @throws TInvalidOperationException if the module is already initialized
*/
public function setHost($value)
{
if($this->_initialized)
throw new TInvalidOperationException('memcache_host_unchangeable');
else
$this->_host=$value;
}
/**
* @return integer port number of the memcache server
*/
public function getPort()
{
return $this->_port;
}
/**
* @param integer port number of the memcache server
* @throws TInvalidOperationException if the module is already initialized
*/
public function setPort($value)
{
if($this->_initialized)
throw new TInvalidOperationException('memcache_port_unchangeable');
else
$this->_port=TPropertyValue::ensureInteger($value);
}
/**
* @param string prefix of our memcache data key
*/
public function setPrefix($value)
{
if($this->_initialized)
throw new TInvalidOperationException('memcache_prefix_unchangeable');
else
$this->_prefix = $value;
}
/**
* @return string prefix of our memcache data key
*/
public function getPrefix()
{
return $this->_prefix;
}
/**
* @param string memcache data key
* @return safe memcache key within 256 characters
*/
private function calculateKey($key)
{
return md5($this->_prefix.$key);
}
}
?>