diff -r 5e7a0fedabdf -r 877f952ae2bd web/lib/Zend/Service/WindowsAzure/Storage/Table.php --- a/web/lib/Zend/Service/WindowsAzure/Storage/Table.php Thu Mar 21 17:31:31 2013 +0100 +++ b/web/lib/Zend/Service/WindowsAzure/Storage/Table.php Thu Mar 21 19:50:53 2013 +0100 @@ -15,45 +15,10 @@ * @category Zend * @package Zend_Service_WindowsAzure * @subpackage Storage - * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @version $Id: Table.php 23170 2010-10-19 18:29:24Z mabe $ - */ - -/** - * @see Zend_Service_WindowsAzure_Credentials_CredentialsAbstract - */ -require_once 'Zend/Service/WindowsAzure/Credentials/CredentialsAbstract.php'; - -/** - * @see Zend_Service_WindowsAzure_Credentials_SharedKey - */ -require_once 'Zend/Service/WindowsAzure/Credentials/SharedKey.php'; - -/** - * @see Zend_Service_WindowsAzure_Credentials_SharedKeyLite + * @version $Id: Table.php 24697 2012-03-23 13:11:04Z ezimuel $ */ -require_once 'Zend/Service/WindowsAzure/Credentials/SharedKeyLite.php'; - -/** - * @see Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract - */ -require_once 'Zend/Service/WindowsAzure/RetryPolicy/RetryPolicyAbstract.php'; - -/** - * @see Zend_Http_Client - */ -require_once 'Zend/Http/Client.php'; - -/** - * @see Zend_Http_Response - */ -require_once 'Zend/Http/Response.php'; - -/** - * @see Zend_Service_WindowsAzure_Storage - */ -require_once 'Zend/Service/WindowsAzure/Storage.php'; /** * @see Zend_Service_WindowsAzure_Storage_BatchStorageAbstract @@ -66,9 +31,9 @@ require_once 'Zend/Service/WindowsAzure/Storage/TableInstance.php'; /** - * @see Zend_Service_WindowsAzure_Storage_TableEntity + * @see Zend_Service_WindowsAzure_Storage_TableEntityQuery */ -require_once 'Zend/Service/WindowsAzure/Storage/TableEntity.php'; +require_once 'Zend/Service/WindowsAzure/Storage/TableEntityQuery.php'; /** * @see Zend_Service_WindowsAzure_Storage_DynamicTableEntity @@ -76,27 +41,48 @@ require_once 'Zend/Service/WindowsAzure/Storage/DynamicTableEntity.php'; /** - * @see Zend_Service_WindowsAzure_Storage_TableEntityQuery + * @see Zend_Service_WindowsAzure_Credentials_SharedKeyLite */ -require_once 'Zend/Service/WindowsAzure/Storage/TableEntityQuery.php'; - -/** - * @see Zend_Service_WindowsAzure_Exception - */ -require_once 'Zend/Service/WindowsAzure/Exception.php'; - +require_once 'Zend/Service/WindowsAzure/Credentials/SharedKeyLite.php'; /** * @category Zend * @package Zend_Service_WindowsAzure * @subpackage Storage - * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Service_WindowsAzure_Storage_Table extends Zend_Service_WindowsAzure_Storage_BatchStorageAbstract { /** + * Throw Zend_Service_WindowsAzure_Exception when a property is not specified in Windows Azure? + * Defaults to true, making behaviour similar to Windows Azure StorageClient in .NET. + * + * @var boolean + */ + protected $_throwExceptionOnMissingData = true; + + /** + * Throw Zend_Service_WindowsAzure_Exception when a property is not specified in Windows Azure? + * Defaults to true, making behaviour similar to Windows Azure StorageClient in .NET. + * + * @param boolean $value + */ + public function setThrowExceptionOnMissingData($value = true) + { + $this->_throwExceptionOnMissingData = $value; + } + + /** + * Throw Zend_Service_WindowsAzure_Exception when a property is not specified in Windows Azure? + */ + public function getThrowExceptionOnMissingData() + { + return $this->_throwExceptionOnMissingData; + } + + /** * Creates a new Zend_Service_WindowsAzure_Storage_Table instance * * @param string $host Storage host name @@ -125,6 +111,7 @@ public function tableExists($tableName = '') { if ($tableName === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.'); } @@ -187,7 +174,8 @@ } // More tables? - if ($response->getHeader('x-ms-continuation-NextTableName') !== null) { + if (!is_null($response->getHeader('x-ms-continuation-NextTableName'))) { + require_once 'Zend/Service/WindowsAzure/Exception.php'; $returnValue = array_merge($returnValue, $this->listTables($response->getHeader('x-ms-continuation-NextTableName'))); } @@ -251,6 +239,7 @@ $tableName = $entry->xpath('.//m:properties/d:TableName'); $tableName = (string)$tableName[0]; + return new Zend_Service_WindowsAzure_Storage_TableInstance( (string)$entry->id, $tableName, @@ -258,7 +247,21 @@ (string)$entry->updated ); } else { - throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + require_once 'Zend/Service/WindowsAzure/Exception.php'; + throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); + } + } + + /** + * Create table if it does not exist + * + * @param string $tableName Table name + * @throws Zend_Service_WindowsAzure_Exception + */ + public function createTableIfNotExists($tableName = '') + { + if (!$this->tableExists($tableName)) { + $this->createTable($tableName); } } @@ -271,6 +274,7 @@ public function deleteTable($tableName = '') { if ($tableName === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.'); } @@ -281,6 +285,7 @@ // Perform request $response = $this->_performRequest('Tables(\'' . $tableName . '\')', '', Zend_Http_Client::DELETE, $headers, true, null); if (!$response->isSuccessful()) { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); } } @@ -296,9 +301,11 @@ public function insertEntity($tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null) { if ($tableName === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.'); } - if ($entity === null) { + if (is_null($entity)) { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Entity is not specified.'); } @@ -340,7 +347,7 @@ $result = $this->_parseResponse($response); $timestamp = $result->xpath('//m:properties/d:Timestamp'); - $timestamp = (string)$timestamp[0]; + $timestamp = $this->_convertToDateTime( (string)$timestamp[0] ); $etag = $result->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata'); $etag = (string)$etag['etag']; @@ -351,6 +358,7 @@ return $entity; } else { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); } } @@ -366,9 +374,11 @@ public function deleteEntity($tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null, $verifyEtag = false) { if ($tableName === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.'); } - if ($entity === null) { + if (is_null($entity)) { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Entity is not specified.'); } @@ -394,6 +404,7 @@ $response = $this->_performRequest($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\', RowKey=\'' . $entity->getRowKey() . '\')', '', Zend_Http_Client::DELETE, $headers, true, null); } if (!$response->isSuccessful()) { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); } } @@ -408,18 +419,22 @@ * @return Zend_Service_WindowsAzure_Storage_TableEntity * @throws Zend_Service_WindowsAzure_Exception */ - public function retrieveEntityById($tableName = '', $partitionKey = '', $rowKey = '', $entityClass = 'Zend_Service_WindowsAzure_Storage_DynamicTableEntity') + public function retrieveEntityById($tableName, $partitionKey, $rowKey, $entityClass = 'Zend_Service_WindowsAzure_Storage_DynamicTableEntity') { - if ($tableName === '') { + if (is_null($tableName) || $tableName === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.'); } - if ($partitionKey === '') { + if (is_null($partitionKey) || $partitionKey === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Partition key is not specified.'); } - if ($rowKey === '') { + if (is_null($rowKey) || $rowKey === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Row key is not specified.'); } - if ($entityClass === '') { + if (is_null($entityClass) || $entityClass === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Entity class is not specified.'); } @@ -429,6 +444,7 @@ if (strlen($partitionKey . $rowKey) >= 256) { // Start a batch if possible if ($this->isInBatch()) { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Entity cannot be retrieved. A transaction is required to retrieve the entity, but another transaction is already active.'); } @@ -460,6 +476,7 @@ */ public function select() { + return new Zend_Service_WindowsAzure_Storage_TableEntityQuery(); } @@ -477,9 +494,11 @@ public function retrieveEntities($tableName = '', $filter = '', $entityClass = 'Zend_Service_WindowsAzure_Storage_DynamicTableEntity', $nextPartitionKey = null, $nextRowKey = null) { if ($tableName === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.'); } if ($entityClass === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Entity class is not specified.'); } @@ -497,7 +516,9 @@ // Option 1: $tableName is a string // Append parentheses - $tableName .= '()'; + if (strpos($tableName, '()') === false) { + $tableName .= '()'; + } // Build query $query = array(); @@ -520,16 +541,19 @@ // Change $tableName $tableName = $tableName->assembleFrom(true); } else { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Invalid argument: $tableName'); } // Add continuation querystring parameters? - if ($nextPartitionKey !== null && $nextRowKey !== null) { + if (!is_null($nextPartitionKey) && !is_null($nextRowKey)) { if ($queryString !== '') { $queryString .= '&'; + } else { + $queryString .= '?'; } - $queryString .= '&NextPartitionKey=' . rawurlencode($nextPartitionKey) . '&NextRowKey=' . rawurlencode($nextRowKey); + $queryString .= 'NextPartitionKey=' . rawurlencode($nextPartitionKey) . '&NextRowKey=' . rawurlencode($nextRowKey); } // Perform request @@ -580,15 +604,15 @@ // Create entity $entity = new $entityClass('', ''); - $entity->setAzureValues((array)$properties, true); + $entity->setAzureValues((array)$properties, $this->_throwExceptionOnMissingData); - // If we have a Zend_Service_WindowsAzure_Storage_DynamicTableEntity, make sure all property types are OK + // If we have a Zend_Service_WindowsAzure_Storage_DynamicTableEntity, make sure all property types are set if ($entity instanceof Zend_Service_WindowsAzure_Storage_DynamicTableEntity) { foreach ($properties as $key => $value) { $attributes = $value->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata'); $type = (string)$attributes['type']; if ($type !== '') { - $entity->setAzurePropertyType($key, $type); + $entity->setAzureProperty($key, (string)$value, $type); } } } @@ -603,7 +627,7 @@ } // More entities? - if ($response->getHeader('x-ms-continuation-NextPartitionKey') !== null && $response->getHeader('x-ms-continuation-NextRowKey') !== null) { + if (!is_null($response->getHeader('x-ms-continuation-NextPartitionKey')) && !is_null($response->getHeader('x-ms-continuation-NextRowKey'))) { if (strpos($queryString, '$top') === false) { $returnValue = array_merge($returnValue, $this->retrieveEntities($tableName, $filter, $entityClass, $response->getHeader('x-ms-continuation-NextPartitionKey'), $response->getHeader('x-ms-continuation-NextRowKey'))); } @@ -612,6 +636,7 @@ // Return return $returnValue; } else { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); } } @@ -642,6 +667,7 @@ { $mergeEntity = null; if (is_array($properties) && count($properties) > 0) { + // Build a new object $mergeEntity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($entity->getPartitionKey(), $entity->getRowKey()); @@ -655,10 +681,10 @@ } else { $mergeEntity = $entity; } - - // Ensure entity timestamp matches updated timestamp - $entity->setTimestamp($this->isoDate()); + // Ensure entity timestamp matches updated timestamp + $entity->setTimestamp(new DateTime()); + return $this->_changeEntity(Zend_Http_Client::MERGE, $tableName, $mergeEntity, $verifyEtag); } @@ -691,9 +717,11 @@ protected function _changeEntity($httpVerb = Zend_Http_Client::PUT, $tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null, $verifyEtag = false) { if ($tableName === '') { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.'); } - if ($entity === null) { + if (is_null($entity)) { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Entity is not specified.'); } @@ -723,14 +751,11 @@ '; - // Attempt to get timestamp from entity + // Attempt to get timestamp from entity $timestamp = $entity->getTimestamp(); - if ($timestamp == Zend_Service_WindowsAzure_Storage_TableEntity::DEFAULT_TIMESTAMP) { - $timestamp = $this->isoDate(); - } - + $requestBody = $this->_fillTemplate($requestBody, array( - 'Updated' => $timestamp, + 'Updated' => $this->_convertToEdmDateTime($timestamp), 'Properties' => $this->_generateAzureRepresentation($entity) )); @@ -746,18 +771,19 @@ // Perform request $response = null; if ($this->isInBatch()) { - $this->getCurrentBatch()->enlistOperation($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\',RowKey=\'' . $entity->getRowKey() . '\')', '', $httpVerb, $headers, true, $requestBody); + $this->getCurrentBatch()->enlistOperation($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\', RowKey=\'' . $entity->getRowKey() . '\')', '', $httpVerb, $headers, true, $requestBody); return null; } else { - $response = $this->_performRequest($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\',RowKey=\'' . $entity->getRowKey() . '\')', '', $httpVerb, $headers, true, $requestBody); + $response = $this->_performRequest($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\', RowKey=\'' . $entity->getRowKey() . '\')', '', $httpVerb, $headers, true, $requestBody); } if ($response->isSuccessful()) { // Update properties $entity->setEtag($response->getHeader('Etag')); - $entity->setTimestamp($response->getHeader('Last-modified')); + $entity->setTimestamp( $this->_convertToDateTime($response->getHeader('Last-modified')) ); return $entity; } else { + require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.')); } } @@ -804,14 +830,16 @@ if ($azureValue->Type != '') { $value[] = ' m:type="' . $azureValue->Type . '"'; } - if ($azureValue->Value === null) { + if (is_null($azureValue->Value)) { $value[] = ' m:null="true"'; } $value[] = '>'; - if ($azureValue->Value !== null) { + if (!is_null($azureValue->Value)) { if (strtolower($azureValue->Type) == 'edm.boolean') { $value[] = ($azureValue->Value == true ? '1' : '0'); + } else if (strtolower($azureValue->Type) == 'edm.datetime') { + $value[] = $this->_convertToEdmDateTime($azureValue->Value); } else { $value[] = htmlspecialchars($azureValue->Value); } @@ -862,5 +890,42 @@ $resourceType, $requiredPermission ); - } + } + + /** + * Converts a string to a DateTime object. Returns false on failure. + * + * @param string $value The string value to parse + * @return DateTime|boolean + */ + protected function _convertToDateTime($value = '') + { + if ($value instanceof DateTime) { + return $value; + } + + try { + if (substr($value, -1) == 'Z') { + $value = substr($value, 0, strlen($value) - 1); + } + return new DateTime($value, new DateTimeZone('UTC')); + } + catch (Exception $ex) { + return false; + } + } + + /** + * Converts a DateTime object into an Edm.DaeTime value in UTC timezone, + * represented as a string. + * + * @param DateTime $value + * @return string + */ + protected function _convertToEdmDateTime(DateTime $value) + { + $cloned = clone $value; + $cloned->setTimezone(new DateTimeZone('UTC')); + return str_replace('+0000', 'Z', $cloned->format(DateTime::ISO8601)); + } }