web/lib/Zend/Service/WindowsAzure/Storage/Table.php
changeset 807 877f952ae2bd
parent 207 621fa6caec0c
child 1230 68c69c656a2c
--- 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 @@
                           </content>
                         </entry>';
 		
-        // 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));
+    }
 }