325 lines
10 KiB
PHP
325 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* Zend Framework
|
|
*
|
|
* LICENSE
|
|
*
|
|
* This source file is subject to the new BSD license that is bundled
|
|
* with this package in the file LICENSE.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* http://framework.zend.com/license/new-bsd
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@zend.com so we can send you a copy immediately.
|
|
*
|
|
* @category Zend
|
|
* @package Zend_Service_WindowsAzure
|
|
* @subpackage Session
|
|
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
* @version $Id$
|
|
*/
|
|
|
|
/**
|
|
* @category Zend
|
|
* @package Zend_Service_WindowsAzure
|
|
* @subpackage Session
|
|
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
*/
|
|
class Zend_Service_WindowsAzure_SessionHandler
|
|
{
|
|
/**
|
|
* Maximal property size in table storage.
|
|
*
|
|
* @var int
|
|
* @see http://msdn.microsoft.com/en-us/library/dd179338.aspx
|
|
*/
|
|
const MAX_TS_PROPERTY_SIZE = 65536;
|
|
|
|
/** Storage backend type */
|
|
const STORAGE_TYPE_TABLE = 'table';
|
|
const STORAGE_TYPE_BLOB = 'blob';
|
|
|
|
/**
|
|
* Storage back-end
|
|
*
|
|
* @var Zend_Service_WindowsAzure_Storage_Table|Zend_Service_WindowsAzure_Storage_Blob
|
|
*/
|
|
protected $_storage;
|
|
|
|
/**
|
|
* Storage backend type
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_storageType;
|
|
|
|
/**
|
|
* Session container name
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_sessionContainer;
|
|
|
|
/**
|
|
* Session container partition
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_sessionContainerPartition;
|
|
|
|
/**
|
|
* Creates a new Zend_Service_WindowsAzure_SessionHandler instance
|
|
*
|
|
* @param Zend_Service_WindowsAzure_Storage_Table|Zend_Service_WindowsAzure_Storage_Blob $storage Storage back-end, can be table storage and blob storage
|
|
* @param string $sessionContainer Session container name
|
|
* @param string $sessionContainerPartition Session container partition
|
|
*/
|
|
public function __construct(Zend_Service_WindowsAzure_Storage $storage, $sessionContainer = 'phpsessions', $sessionContainerPartition = 'sessions')
|
|
{
|
|
// Validate $storage
|
|
if (!($storage instanceof Zend_Service_WindowsAzure_Storage_Table || $storage instanceof Zend_Service_WindowsAzure_Storage_Blob)) {
|
|
require_once 'Zend/Service/WindowsAzure/Exception.php';
|
|
throw new Zend_Service_WindowsAzure_Exception('Invalid storage back-end given. Storage back-end should be of type Zend_Service_WindowsAzure_Storage_Table or Zend_Service_WindowsAzure_Storage_Blob.');
|
|
}
|
|
|
|
// Validate other parameters
|
|
if ($sessionContainer == '' || $sessionContainerPartition == '') {
|
|
require_once 'Zend/Service/WindowsAzure/Exception.php';
|
|
throw new Zend_Service_WindowsAzure_Exception('Session container and session partition should be specified.');
|
|
}
|
|
|
|
// Determine storage type
|
|
$storageType = self::STORAGE_TYPE_TABLE;
|
|
if ($storage instanceof Zend_Service_WindowsAzure_Storage_Blob) {
|
|
$storageType = self::STORAGE_TYPE_BLOB;
|
|
}
|
|
|
|
// Set properties
|
|
$this->_storage = $storage;
|
|
$this->_storageType = $storageType;
|
|
$this->_sessionContainer = $sessionContainer;
|
|
$this->_sessionContainerPartition = $sessionContainerPartition;
|
|
}
|
|
|
|
/**
|
|
* Registers the current session handler as PHP's session handler
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function register()
|
|
{
|
|
return session_set_save_handler(array($this, 'open'),
|
|
array($this, 'close'),
|
|
array($this, 'read'),
|
|
array($this, 'write'),
|
|
array($this, 'destroy'),
|
|
array($this, 'gc')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Open the session store
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function open()
|
|
{
|
|
// Make sure storage container exists
|
|
if ($this->_storageType == self::STORAGE_TYPE_TABLE) {
|
|
$this->_storage->createTableIfNotExists($this->_sessionContainer);
|
|
} else if ($this->_storageType == self::STORAGE_TYPE_BLOB) {
|
|
$this->_storage->createContainerIfNotExists($this->_sessionContainer);
|
|
}
|
|
|
|
// Ok!
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Close the session store
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function close()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Read a specific session
|
|
*
|
|
* @param int $id Session Id
|
|
* @return string
|
|
*/
|
|
public function read($id)
|
|
{
|
|
// Read data
|
|
if ($this->_storageType == self::STORAGE_TYPE_TABLE) {
|
|
// In table storage
|
|
try
|
|
{
|
|
$sessionRecord = $this->_storage->retrieveEntityById(
|
|
$this->_sessionContainer,
|
|
$this->_sessionContainerPartition,
|
|
$id
|
|
);
|
|
return unserialize(base64_decode($sessionRecord->serializedData));
|
|
}
|
|
catch (Zend_Service_WindowsAzure_Exception $ex)
|
|
{
|
|
return '';
|
|
}
|
|
} else if ($this->_storageType == self::STORAGE_TYPE_BLOB) {
|
|
// In blob storage
|
|
try
|
|
{
|
|
$data = $this->_storage->getBlobData(
|
|
$this->_sessionContainer,
|
|
$this->_sessionContainerPartition . '/' . $id
|
|
);
|
|
return unserialize(base64_decode($data));
|
|
}
|
|
catch (Zend_Service_WindowsAzure_Exception $ex)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write a specific session
|
|
*
|
|
* @param int $id Session Id
|
|
* @param string $serializedData Serialized PHP object
|
|
* @throws Exception
|
|
*/
|
|
public function write($id, $serializedData)
|
|
{
|
|
// Encode data
|
|
$serializedData = base64_encode(serialize($serializedData));
|
|
if (strlen($serializedData) >= self::MAX_TS_PROPERTY_SIZE && $this->_storageType == self::STORAGE_TYPE_TABLE) {
|
|
throw new Zend_Service_WindowsAzure_Exception('Session data exceeds the maximum allowed size of ' . self::MAX_TS_PROPERTY_SIZE . ' bytes that can be stored using table storage. Consider switching to a blob storage back-end or try reducing session data size.');
|
|
}
|
|
|
|
// Store data
|
|
if ($this->_storageType == self::STORAGE_TYPE_TABLE) {
|
|
// In table storage
|
|
$sessionRecord = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($this->_sessionContainerPartition, $id);
|
|
$sessionRecord->sessionExpires = time();
|
|
$sessionRecord->serializedData = $serializedData;
|
|
|
|
$sessionRecord->setAzurePropertyType('sessionExpires', 'Edm.Int32');
|
|
|
|
try
|
|
{
|
|
$this->_storage->updateEntity($this->_sessionContainer, $sessionRecord);
|
|
}
|
|
catch (Zend_Service_WindowsAzure_Exception $unknownRecord)
|
|
{
|
|
$this->_storage->insertEntity($this->_sessionContainer, $sessionRecord);
|
|
}
|
|
} else if ($this->_storageType == self::STORAGE_TYPE_BLOB) {
|
|
// In blob storage
|
|
$this->_storage->putBlobData(
|
|
$this->_sessionContainer,
|
|
$this->_sessionContainerPartition . '/' . $id,
|
|
$serializedData,
|
|
array('sessionexpires' => time())
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Destroy a specific session
|
|
*
|
|
* @param int $id Session Id
|
|
* @return boolean
|
|
*/
|
|
public function destroy($id)
|
|
{
|
|
// Destroy data
|
|
if ($this->_storageType == self::STORAGE_TYPE_TABLE) {
|
|
// In table storage
|
|
try
|
|
{
|
|
$sessionRecord = $this->_storage->retrieveEntityById(
|
|
$this->_sessionContainer,
|
|
$this->_sessionContainerPartition,
|
|
$id
|
|
);
|
|
$this->_storage->deleteEntity($this->_sessionContainer, $sessionRecord);
|
|
|
|
return true;
|
|
}
|
|
catch (Zend_Service_WindowsAzure_Exception $ex)
|
|
{
|
|
return false;
|
|
}
|
|
} else if ($this->_storageType == self::STORAGE_TYPE_BLOB) {
|
|
// In blob storage
|
|
try
|
|
{
|
|
$this->_storage->deleteBlob(
|
|
$this->_sessionContainer,
|
|
$this->_sessionContainerPartition . '/' . $id
|
|
);
|
|
|
|
return true;
|
|
}
|
|
catch (Zend_Service_WindowsAzure_Exception $ex)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Garbage collector
|
|
*
|
|
* @param int $lifeTime Session maximal lifetime
|
|
* @see session.gc_divisor 100
|
|
* @see session.gc_maxlifetime 1440
|
|
* @see session.gc_probability 1
|
|
* @usage Execution rate 1/100 (session.gc_probability/session.gc_divisor)
|
|
* @return boolean
|
|
*/
|
|
public function gc($lifeTime)
|
|
{
|
|
if ($this->_storageType == self::STORAGE_TYPE_TABLE) {
|
|
// In table storage
|
|
try
|
|
{
|
|
$result = $this->_storage->retrieveEntities($this->_sessionContainer, 'PartitionKey eq \'' . $this->_sessionContainerPartition . '\' and sessionExpires lt ' . (time() - $lifeTime));
|
|
foreach ($result as $sessionRecord)
|
|
{
|
|
$this->_storage->deleteEntity($this->_sessionContainer, $sessionRecord);
|
|
}
|
|
return true;
|
|
}
|
|
catch (Zend_Service_WindowsAzure_exception $ex)
|
|
{
|
|
return false;
|
|
}
|
|
} else if ($this->_storageType == self::STORAGE_TYPE_BLOB) {
|
|
// In blob storage
|
|
try
|
|
{
|
|
$result = $this->_storage->listBlobs($this->_sessionContainer, $this->_sessionContainerPartition, '', null, null, 'metadata');
|
|
foreach ($result as $sessionRecord)
|
|
{
|
|
if ($sessionRecord->Metadata['sessionexpires'] < (time() - $lifeTime)) {
|
|
$this->_storage->deleteBlob($this->_sessionContainer, $sessionRecord->Name);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
catch (Zend_Service_WindowsAzure_exception $ex)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|