<?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; } } } }