web/lib/Zend/Service/WindowsAzure/Storage/Table.php
changeset 807 877f952ae2bd
parent 207 621fa6caec0c
child 1230 68c69c656a2c
equal deleted inserted replaced
805:5e7a0fedabdf 807:877f952ae2bd
    13  * to license@zend.com so we can send you a copy immediately.
    13  * to license@zend.com so we can send you a copy immediately.
    14  *
    14  *
    15  * @category   Zend
    15  * @category   Zend
    16  * @package    Zend_Service_WindowsAzure
    16  * @package    Zend_Service_WindowsAzure
    17  * @subpackage Storage
    17  * @subpackage Storage
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
    18  * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    20  * @version    $Id: Table.php 23170 2010-10-19 18:29:24Z mabe $
    20  * @version    $Id: Table.php 24697 2012-03-23 13:11:04Z ezimuel $
    21  */
    21  */
    22 
       
    23 /**
       
    24  * @see Zend_Service_WindowsAzure_Credentials_CredentialsAbstract
       
    25  */
       
    26 require_once 'Zend/Service/WindowsAzure/Credentials/CredentialsAbstract.php';
       
    27 
       
    28 /**
       
    29  * @see Zend_Service_WindowsAzure_Credentials_SharedKey
       
    30  */
       
    31 require_once 'Zend/Service/WindowsAzure/Credentials/SharedKey.php';
       
    32 
       
    33 /**
       
    34  * @see Zend_Service_WindowsAzure_Credentials_SharedKeyLite
       
    35  */
       
    36 require_once 'Zend/Service/WindowsAzure/Credentials/SharedKeyLite.php';
       
    37 
       
    38 /**
       
    39  * @see Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract
       
    40  */
       
    41 require_once 'Zend/Service/WindowsAzure/RetryPolicy/RetryPolicyAbstract.php';
       
    42 
       
    43 /**
       
    44  * @see Zend_Http_Client
       
    45  */
       
    46 require_once 'Zend/Http/Client.php';
       
    47 
       
    48 /**
       
    49  * @see Zend_Http_Response
       
    50  */
       
    51 require_once 'Zend/Http/Response.php';
       
    52 
       
    53 /**
       
    54  * @see Zend_Service_WindowsAzure_Storage
       
    55  */
       
    56 require_once 'Zend/Service/WindowsAzure/Storage.php';
       
    57 
    22 
    58 /**
    23 /**
    59  * @see Zend_Service_WindowsAzure_Storage_BatchStorageAbstract
    24  * @see Zend_Service_WindowsAzure_Storage_BatchStorageAbstract
    60  */
    25  */
    61 require_once 'Zend/Service/WindowsAzure/Storage/BatchStorageAbstract.php';
    26 require_once 'Zend/Service/WindowsAzure/Storage/BatchStorageAbstract.php';
    64  * @see Zend_Service_WindowsAzure_Storage_TableInstance
    29  * @see Zend_Service_WindowsAzure_Storage_TableInstance
    65  */
    30  */
    66 require_once 'Zend/Service/WindowsAzure/Storage/TableInstance.php';
    31 require_once 'Zend/Service/WindowsAzure/Storage/TableInstance.php';
    67 
    32 
    68 /**
    33 /**
    69  * @see Zend_Service_WindowsAzure_Storage_TableEntity
    34  * @see Zend_Service_WindowsAzure_Storage_TableEntityQuery
    70  */
    35  */
    71 require_once 'Zend/Service/WindowsAzure/Storage/TableEntity.php';
    36 require_once 'Zend/Service/WindowsAzure/Storage/TableEntityQuery.php';
    72 
    37 
    73 /**
    38 /**
    74  * @see Zend_Service_WindowsAzure_Storage_DynamicTableEntity
    39  * @see Zend_Service_WindowsAzure_Storage_DynamicTableEntity
    75  */
    40  */
    76 require_once 'Zend/Service/WindowsAzure/Storage/DynamicTableEntity.php';
    41 require_once 'Zend/Service/WindowsAzure/Storage/DynamicTableEntity.php';
    77 
    42 
    78 /**
    43 /**
    79  * @see Zend_Service_WindowsAzure_Storage_TableEntityQuery
    44  * @see Zend_Service_WindowsAzure_Credentials_SharedKeyLite 
    80  */
    45  */
    81 require_once 'Zend/Service/WindowsAzure/Storage/TableEntityQuery.php';
    46 require_once 'Zend/Service/WindowsAzure/Credentials/SharedKeyLite.php';
    82 
       
    83 /**
       
    84  * @see Zend_Service_WindowsAzure_Exception
       
    85  */
       
    86 require_once 'Zend/Service/WindowsAzure/Exception.php';
       
    87 
       
    88 
    47 
    89 /**
    48 /**
    90  * @category   Zend
    49  * @category   Zend
    91  * @package    Zend_Service_WindowsAzure
    50  * @package    Zend_Service_WindowsAzure
    92  * @subpackage Storage
    51  * @subpackage Storage
    93  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
    52  * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
    94  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    53  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    95  */
    54  */
    96 class Zend_Service_WindowsAzure_Storage_Table
    55 class Zend_Service_WindowsAzure_Storage_Table
    97     extends Zend_Service_WindowsAzure_Storage_BatchStorageAbstract
    56     extends Zend_Service_WindowsAzure_Storage_BatchStorageAbstract
    98 {
    57 {
       
    58 	/**
       
    59 	 * Throw Zend_Service_WindowsAzure_Exception when a property is not specified in Windows Azure?
       
    60 	 * Defaults to true, making behaviour similar to Windows Azure StorageClient in .NET.
       
    61 	 * 
       
    62 	 * @var boolean
       
    63 	 */
       
    64 	protected $_throwExceptionOnMissingData = true;
       
    65 	
       
    66 	/**
       
    67 	 * Throw Zend_Service_WindowsAzure_Exception when a property is not specified in Windows Azure?
       
    68 	 * Defaults to true, making behaviour similar to Windows Azure StorageClient in .NET.
       
    69 	 * 
       
    70 	 * @param boolean $value
       
    71 	 */
       
    72 	public function setThrowExceptionOnMissingData($value = true)
       
    73 	{
       
    74 		$this->_throwExceptionOnMissingData = $value;
       
    75 	}
       
    76 	
       
    77 	/**
       
    78 	 * Throw Zend_Service_WindowsAzure_Exception when a property is not specified in Windows Azure?
       
    79 	 */
       
    80 	public function getThrowExceptionOnMissingData()
       
    81 	{
       
    82 		return $this->_throwExceptionOnMissingData;
       
    83 	}
       
    84 	
    99 	/**
    85 	/**
   100 	 * Creates a new Zend_Service_WindowsAzure_Storage_Table instance
    86 	 * Creates a new Zend_Service_WindowsAzure_Storage_Table instance
   101 	 *
    87 	 *
   102 	 * @param string $host Storage host name
    88 	 * @param string $host Storage host name
   103 	 * @param string $accountName Account name for Windows Azure
    89 	 * @param string $accountName Account name for Windows Azure
   123 	 * @return boolean
   109 	 * @return boolean
   124 	 */
   110 	 */
   125 	public function tableExists($tableName = '')
   111 	public function tableExists($tableName = '')
   126 	{
   112 	{
   127 		if ($tableName === '') {
   113 		if ($tableName === '') {
       
   114 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   128 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   115 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   129 		}
   116 		}
   130 			
   117 			
   131 		// List tables
   118 		// List tables
   132         $tables = $this->listTables(); // 2009-09-19 does not support $this->listTables($tableName); all of a sudden...
   119         $tables = $this->listTables(); // 2009-09-19 does not support $this->listTables($tableName); all of a sudden...
   185 		            (string)$entry->updated
   172 		            (string)$entry->updated
   186 		        );
   173 		        );
   187 		    }
   174 		    }
   188 		    
   175 		    
   189 			// More tables?
   176 			// More tables?
   190 		    if ($response->getHeader('x-ms-continuation-NextTableName') !== null) {
   177 		    if (!is_null($response->getHeader('x-ms-continuation-NextTableName'))) {
       
   178 				require_once 'Zend/Service/WindowsAzure/Exception.php';
   191 		        $returnValue = array_merge($returnValue, $this->listTables($response->getHeader('x-ms-continuation-NextTableName')));
   179 		        $returnValue = array_merge($returnValue, $this->listTables($response->getHeader('x-ms-continuation-NextTableName')));
   192 		    }
   180 		    }
   193 
   181 
   194 		    return $returnValue;
   182 		    return $returnValue;
   195 		} else {
   183 		} else {
   249 		    $entry = $this->_parseResponse($response);
   237 		    $entry = $this->_parseResponse($response);
   250 		    
   238 		    
   251 		    $tableName = $entry->xpath('.//m:properties/d:TableName');
   239 		    $tableName = $entry->xpath('.//m:properties/d:TableName');
   252 		    $tableName = (string)$tableName[0];
   240 		    $tableName = (string)$tableName[0];
   253 		        
   241 		        
       
   242 			
   254 		    return new Zend_Service_WindowsAzure_Storage_TableInstance(
   243 		    return new Zend_Service_WindowsAzure_Storage_TableInstance(
   255 		        (string)$entry->id,
   244 		        (string)$entry->id,
   256 		        $tableName,
   245 		        $tableName,
   257 		        (string)$entry->link['href'],
   246 		        (string)$entry->link['href'],
   258 		        (string)$entry->updated
   247 		        (string)$entry->updated
   259 		    );
   248 		    );
   260 		} else {
   249 		} else {
   261 			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   250                     require_once 'Zend/Service/WindowsAzure/Exception.php';
       
   251                     throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
       
   252 		}
       
   253 	}
       
   254 	
       
   255 	/**
       
   256 	 * Create table if it does not exist
       
   257 	 *
       
   258 	 * @param string $tableName Table name
       
   259 	 * @throws Zend_Service_WindowsAzure_Exception
       
   260 	 */
       
   261 	public function createTableIfNotExists($tableName = '')
       
   262 	{
       
   263 		if (!$this->tableExists($tableName)) {
       
   264 			$this->createTable($tableName);
   262 		}
   265 		}
   263 	}
   266 	}
   264 	
   267 	
   265 	/**
   268 	/**
   266 	 * Delete table
   269 	 * Delete table
   269 	 * @throws Zend_Service_WindowsAzure_Exception
   272 	 * @throws Zend_Service_WindowsAzure_Exception
   270 	 */
   273 	 */
   271 	public function deleteTable($tableName = '')
   274 	public function deleteTable($tableName = '')
   272 	{
   275 	{
   273 		if ($tableName === '') {
   276 		if ($tableName === '') {
       
   277 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   274 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   278 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   275 		}
   279 		}
   276 
   280 
   277         // Add header information
   281         // Add header information
   278         $headers = array();
   282         $headers = array();
   279         $headers['Content-Type'] = 'application/atom+xml';
   283         $headers['Content-Type'] = 'application/atom+xml';
   280 
   284 
   281 		// Perform request
   285 		// Perform request
   282 		$response = $this->_performRequest('Tables(\'' . $tableName . '\')', '', Zend_Http_Client::DELETE, $headers, true, null);
   286 		$response = $this->_performRequest('Tables(\'' . $tableName . '\')', '', Zend_Http_Client::DELETE, $headers, true, null);
   283 		if (!$response->isSuccessful()) {
   287 		if (!$response->isSuccessful()) {
       
   288 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   284 			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   289 			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   285 		}
   290 		}
   286 	}
   291 	}
   287 	
   292 	
   288 	/**
   293 	/**
   294 	 * @throws Zend_Service_WindowsAzure_Exception
   299 	 * @throws Zend_Service_WindowsAzure_Exception
   295 	 */
   300 	 */
   296 	public function insertEntity($tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null)
   301 	public function insertEntity($tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null)
   297 	{
   302 	{
   298 		if ($tableName === '') {
   303 		if ($tableName === '') {
       
   304 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   299 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   305 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   300 		}
   306 		}
   301 		if ($entity === null) {
   307 		if (is_null($entity)) {
       
   308 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   302 			throw new Zend_Service_WindowsAzure_Exception('Entity is not specified.');
   309 			throw new Zend_Service_WindowsAzure_Exception('Entity is not specified.');
   303 		}
   310 		}
   304 		                     
   311 		                     
   305 		// Generate request body
   312 		// Generate request body
   306 		$requestBody = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
   313 		$requestBody = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
   338 		if ($response->isSuccessful()) {
   345 		if ($response->isSuccessful()) {
   339 		    // Parse result
   346 		    // Parse result
   340 		    $result = $this->_parseResponse($response);
   347 		    $result = $this->_parseResponse($response);
   341 		    
   348 		    
   342 		    $timestamp = $result->xpath('//m:properties/d:Timestamp');
   349 		    $timestamp = $result->xpath('//m:properties/d:Timestamp');
   343 		    $timestamp = (string)$timestamp[0];
   350 		    $timestamp = $this->_convertToDateTime( (string)$timestamp[0] );
   344 
   351 
   345 		    $etag      = $result->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata');
   352 		    $etag      = $result->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata');
   346 		    $etag      = (string)$etag['etag'];
   353 		    $etag      = (string)$etag['etag'];
   347 		    
   354 		    
   348 		    // Update properties
   355 		    // Update properties
   349 		    $entity->setTimestamp($timestamp);
   356 		    $entity->setTimestamp($timestamp);
   350 		    $entity->setEtag($etag);
   357 		    $entity->setEtag($etag);
   351 
   358 
   352 		    return $entity;
   359 		    return $entity;
   353 		} else {
   360 		} else {
       
   361 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   354 			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   362 			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   355 		}
   363 		}
   356 	}
   364 	}
   357 	
   365 	
   358 	/**
   366 	/**
   364 	 * @throws Zend_Service_WindowsAzure_Exception
   372 	 * @throws Zend_Service_WindowsAzure_Exception
   365 	 */
   373 	 */
   366 	public function deleteEntity($tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null, $verifyEtag = false)
   374 	public function deleteEntity($tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null, $verifyEtag = false)
   367 	{
   375 	{
   368 		if ($tableName === '') {
   376 		if ($tableName === '') {
       
   377 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   369 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   378 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   370 		}
   379 		}
   371 		if ($entity === null) {
   380 		if (is_null($entity)) {
       
   381 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   372 			throw new Zend_Service_WindowsAzure_Exception('Entity is not specified.');
   382 			throw new Zend_Service_WindowsAzure_Exception('Entity is not specified.');
   373 		}
   383 		}
   374 		                     
   384 		                     
   375         // Add header information
   385         // Add header information
   376         $headers = array();
   386         $headers = array();
   392 		    return null;
   402 		    return null;
   393 		} else {
   403 		} else {
   394 		    $response = $this->_performRequest($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\', RowKey=\'' . $entity->getRowKey() . '\')', '', Zend_Http_Client::DELETE, $headers, true, null);
   404 		    $response = $this->_performRequest($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\', RowKey=\'' . $entity->getRowKey() . '\')', '', Zend_Http_Client::DELETE, $headers, true, null);
   395 		}
   405 		}
   396 		if (!$response->isSuccessful()) {
   406 		if (!$response->isSuccessful()) {
       
   407 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   397 		    throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   408 		    throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   398 		}
   409 		}
   399 	}
   410 	}
   400 	
   411 	
   401 	/**
   412 	/**
   406 	 * @param string $rowKey       Row key
   417 	 * @param string $rowKey       Row key
   407 	 * @param string $entityClass  Entity class name* 
   418 	 * @param string $entityClass  Entity class name* 
   408 	 * @return Zend_Service_WindowsAzure_Storage_TableEntity
   419 	 * @return Zend_Service_WindowsAzure_Storage_TableEntity
   409 	 * @throws Zend_Service_WindowsAzure_Exception
   420 	 * @throws Zend_Service_WindowsAzure_Exception
   410 	 */
   421 	 */
   411 	public function retrieveEntityById($tableName = '', $partitionKey = '', $rowKey = '', $entityClass = 'Zend_Service_WindowsAzure_Storage_DynamicTableEntity')
   422 	public function retrieveEntityById($tableName, $partitionKey, $rowKey, $entityClass = 'Zend_Service_WindowsAzure_Storage_DynamicTableEntity')
   412 	{
   423 	{
   413 		if ($tableName === '') {
   424 		if (is_null($tableName) || $tableName === '') {
       
   425 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   414 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   426 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   415 		}
   427 		}
   416 		if ($partitionKey === '') {
   428 		if (is_null($partitionKey) || $partitionKey === '') {
       
   429 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   417 			throw new Zend_Service_WindowsAzure_Exception('Partition key is not specified.');
   430 			throw new Zend_Service_WindowsAzure_Exception('Partition key is not specified.');
   418 		}
   431 		}
   419 		if ($rowKey === '') {
   432 		if (is_null($rowKey) || $rowKey === '') {
       
   433 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   420 			throw new Zend_Service_WindowsAzure_Exception('Row key is not specified.');
   434 			throw new Zend_Service_WindowsAzure_Exception('Row key is not specified.');
   421 		}
   435 		}
   422 		if ($entityClass === '') {
   436 		if (is_null($entityClass) || $entityClass === '') {
       
   437 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   423 			throw new Zend_Service_WindowsAzure_Exception('Entity class is not specified.');
   438 			throw new Zend_Service_WindowsAzure_Exception('Entity class is not specified.');
   424 		}
   439 		}
   425 
   440 
   426 			
   441 			
   427 		// Check for combined size of partition key and row key
   442 		// Check for combined size of partition key and row key
   428 		// http://msdn.microsoft.com/en-us/library/dd179421.aspx
   443 		// http://msdn.microsoft.com/en-us/library/dd179421.aspx
   429 		if (strlen($partitionKey . $rowKey) >= 256) {
   444 		if (strlen($partitionKey . $rowKey) >= 256) {
   430 		    // Start a batch if possible
   445 		    // Start a batch if possible
   431 		    if ($this->isInBatch()) {
   446 		    if ($this->isInBatch()) {
       
   447 				require_once 'Zend/Service/WindowsAzure/Exception.php';
   432 		        throw new Zend_Service_WindowsAzure_Exception('Entity cannot be retrieved. A transaction is required to retrieve the entity, but another transaction is already active.');
   448 		        throw new Zend_Service_WindowsAzure_Exception('Entity cannot be retrieved. A transaction is required to retrieve the entity, but another transaction is already active.');
   433 		    }
   449 		    }
   434 		        
   450 		        
   435 		    $this->startBatch();
   451 		    $this->startBatch();
   436 		}
   452 		}
   458 	 * 
   474 	 * 
   459 	 * @return Zend_Service_WindowsAzure_Storage_TableEntityQuery
   475 	 * @return Zend_Service_WindowsAzure_Storage_TableEntityQuery
   460 	 */
   476 	 */
   461 	public function select()
   477 	public function select()
   462 	{
   478 	{
       
   479             
   463 	    return new Zend_Service_WindowsAzure_Storage_TableEntityQuery();
   480 	    return new Zend_Service_WindowsAzure_Storage_TableEntityQuery();
   464 	}
   481 	}
   465 	
   482 	
   466 	/**
   483 	/**
   467 	 * Retrieve entities from table
   484 	 * Retrieve entities from table
   475 	 * @throws Zend_Service_WindowsAzure_Exception
   492 	 * @throws Zend_Service_WindowsAzure_Exception
   476 	 */
   493 	 */
   477 	public function retrieveEntities($tableName = '', $filter = '', $entityClass = 'Zend_Service_WindowsAzure_Storage_DynamicTableEntity', $nextPartitionKey = null, $nextRowKey = null)
   494 	public function retrieveEntities($tableName = '', $filter = '', $entityClass = 'Zend_Service_WindowsAzure_Storage_DynamicTableEntity', $nextPartitionKey = null, $nextRowKey = null)
   478 	{
   495 	{
   479 		if ($tableName === '') {
   496 		if ($tableName === '') {
       
   497 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   480 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   498 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   481 		}
   499 		}
   482 		if ($entityClass === '') {
   500 		if ($entityClass === '') {
       
   501 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   483 			throw new Zend_Service_WindowsAzure_Exception('Entity class is not specified.');
   502 			throw new Zend_Service_WindowsAzure_Exception('Entity class is not specified.');
   484 		}
   503 		}
   485 
   504 
   486 		// Convenience...
   505 		// Convenience...
   487 		if (class_exists($filter)) {
   506 		if (class_exists($filter)) {
   495 		// Determine query
   514 		// Determine query
   496 		if (is_string($tableName)) {
   515 		if (is_string($tableName)) {
   497 		    // Option 1: $tableName is a string
   516 		    // Option 1: $tableName is a string
   498 		    
   517 		    
   499 		    // Append parentheses
   518 		    // Append parentheses
   500 		    $tableName .= '()';
   519 		    if (strpos($tableName, '()') === false) {
       
   520 		    	$tableName .= '()';
       
   521 		    }
   501 		    
   522 		    
   502     	    // Build query
   523     	    // Build query
   503     	    $query = array();
   524     	    $query = array();
   504     	    
   525     	    
   505     		// Filter?
   526     		// Filter?
   518 		    $queryString = $tableName->assembleQueryString(true);
   539 		    $queryString = $tableName->assembleQueryString(true);
   519 
   540 
   520 		    // Change $tableName
   541 		    // Change $tableName
   521 		    $tableName = $tableName->assembleFrom(true);
   542 		    $tableName = $tableName->assembleFrom(true);
   522 		} else {
   543 		} else {
       
   544 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   523 		    throw new Zend_Service_WindowsAzure_Exception('Invalid argument: $tableName');
   545 		    throw new Zend_Service_WindowsAzure_Exception('Invalid argument: $tableName');
   524 		}
   546 		}
   525 		
   547 		
   526 		// Add continuation querystring parameters?
   548 		// Add continuation querystring parameters?
   527 		if ($nextPartitionKey !== null && $nextRowKey !== null) {
   549 		if (!is_null($nextPartitionKey) && !is_null($nextRowKey)) {
   528 		    if ($queryString !== '') {
   550 		    if ($queryString !== '') {
   529 		        $queryString .= '&';
   551 		        $queryString .= '&';
       
   552 		    } else {
       
   553 		    	$queryString .= '?';
   530 		    }
   554 		    }
   531 		        
   555 		        
   532 		    $queryString .= '&NextPartitionKey=' . rawurlencode($nextPartitionKey) . '&NextRowKey=' . rawurlencode($nextRowKey);
   556 		    $queryString .= 'NextPartitionKey=' . rawurlencode($nextPartitionKey) . '&NextRowKey=' . rawurlencode($nextRowKey);
   533 		}
   557 		}
   534 
   558 
   535 		// Perform request
   559 		// Perform request
   536 	    $response = null;
   560 	    $response = null;
   537 	    if ($this->isInBatch() && $this->getCurrentBatch()->getOperationCount() == 0) {
   561 	    if ($this->isInBatch() && $this->getCurrentBatch()->getOperationCount() == 0) {
   578     		    $properties = $entry->xpath('.//m:properties');
   602     		    $properties = $entry->xpath('.//m:properties');
   579     		    $properties = $properties[0]->children('http://schemas.microsoft.com/ado/2007/08/dataservices');
   603     		    $properties = $properties[0]->children('http://schemas.microsoft.com/ado/2007/08/dataservices');
   580     		    
   604     		    
   581     		    // Create entity
   605     		    // Create entity
   582     		    $entity = new $entityClass('', '');
   606     		    $entity = new $entityClass('', '');
   583     		    $entity->setAzureValues((array)$properties, true);
   607     		    $entity->setAzureValues((array)$properties, $this->_throwExceptionOnMissingData);
   584     		    
   608     		    
   585     		    // If we have a Zend_Service_WindowsAzure_Storage_DynamicTableEntity, make sure all property types are OK
   609     		    // If we have a Zend_Service_WindowsAzure_Storage_DynamicTableEntity, make sure all property types are set
   586     		    if ($entity instanceof Zend_Service_WindowsAzure_Storage_DynamicTableEntity) {
   610     		    if ($entity instanceof Zend_Service_WindowsAzure_Storage_DynamicTableEntity) {
   587     		        foreach ($properties as $key => $value) {  
   611     		        foreach ($properties as $key => $value) {  
   588     		            $attributes = $value->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata');
   612     		            $attributes = $value->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata');
   589     		            $type = (string)$attributes['type'];
   613     		            $type = (string)$attributes['type'];
   590     		            if ($type !== '') {
   614     		            if ($type !== '') {
   591     		                $entity->setAzurePropertyType($key, $type);
   615     		            	$entity->setAzureProperty($key, (string)$value, $type);
   592     		            }
   616     		            }
   593     		        }
   617     		        }
   594     		    }
   618     		    }
   595     
   619     
   596     		    // Update etag
   620     		    // Update etag
   601     		    // Add to result
   625     		    // Add to result
   602     		    $returnValue[] = $entity;
   626     		    $returnValue[] = $entity;
   603 		    }
   627 		    }
   604 
   628 
   605 			// More entities?
   629 			// More entities?
   606 		    if ($response->getHeader('x-ms-continuation-NextPartitionKey') !== null && $response->getHeader('x-ms-continuation-NextRowKey') !== null) {
   630 		    if (!is_null($response->getHeader('x-ms-continuation-NextPartitionKey')) && !is_null($response->getHeader('x-ms-continuation-NextRowKey'))) {
   607 		        if (strpos($queryString, '$top') === false) {
   631 		        if (strpos($queryString, '$top') === false) {
   608 		            $returnValue = array_merge($returnValue, $this->retrieveEntities($tableName, $filter, $entityClass, $response->getHeader('x-ms-continuation-NextPartitionKey'), $response->getHeader('x-ms-continuation-NextRowKey')));
   632 		            $returnValue = array_merge($returnValue, $this->retrieveEntities($tableName, $filter, $entityClass, $response->getHeader('x-ms-continuation-NextPartitionKey'), $response->getHeader('x-ms-continuation-NextRowKey')));
   609 		        }
   633 		        }
   610 		    }
   634 		    }
   611 		    
   635 		    
   612 		    // Return
   636 		    // Return
   613 		    return $returnValue;
   637 		    return $returnValue;
   614 		} else {
   638 		} else {
       
   639 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   615 		    throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   640 		    throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   616 		}
   641 		}
   617 	}
   642 	}
   618 	
   643 	
   619 	/**
   644 	/**
   640 	 */
   665 	 */
   641 	public function mergeEntity($tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null, $verifyEtag = false, $properties = array())
   666 	public function mergeEntity($tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null, $verifyEtag = false, $properties = array())
   642 	{
   667 	{
   643 		$mergeEntity = null;
   668 		$mergeEntity = null;
   644 		if (is_array($properties) && count($properties) > 0) {
   669 		if (is_array($properties) && count($properties) > 0) {
       
   670 			
   645 			// Build a new object
   671 			// Build a new object
   646 			$mergeEntity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($entity->getPartitionKey(), $entity->getRowKey());
   672 			$mergeEntity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($entity->getPartitionKey(), $entity->getRowKey());
   647 			
   673 			
   648 			// Keep only values mentioned in $properties
   674 			// Keep only values mentioned in $properties
   649 			$azureValues = $entity->getAzureValues();
   675 			$azureValues = $entity->getAzureValues();
   653 				}
   679 				}
   654 			}
   680 			}
   655 		} else {
   681 		} else {
   656 			$mergeEntity = $entity;
   682 			$mergeEntity = $entity;
   657 		}
   683 		}
   658 
       
   659         // Ensure entity timestamp matches updated timestamp 
       
   660         $entity->setTimestamp($this->isoDate());
       
   661 		
   684 		
       
   685 		// Ensure entity timestamp matches updated timestamp 
       
   686         $entity->setTimestamp(new DateTime());
       
   687         
   662 	    return $this->_changeEntity(Zend_Http_Client::MERGE, $tableName, $mergeEntity, $verifyEtag);
   688 	    return $this->_changeEntity(Zend_Http_Client::MERGE, $tableName, $mergeEntity, $verifyEtag);
   663 	}
   689 	}
   664 	
   690 	
   665 	/**
   691 	/**
   666 	 * Get error message from Zend_Http_Response
   692 	 * Get error message from Zend_Http_Response
   689 	 * @throws Zend_Service_WindowsAzure_Exception
   715 	 * @throws Zend_Service_WindowsAzure_Exception
   690 	 */
   716 	 */
   691 	protected function _changeEntity($httpVerb = Zend_Http_Client::PUT, $tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null, $verifyEtag = false)
   717 	protected function _changeEntity($httpVerb = Zend_Http_Client::PUT, $tableName = '', Zend_Service_WindowsAzure_Storage_TableEntity $entity = null, $verifyEtag = false)
   692 	{
   718 	{
   693 		if ($tableName === '') {
   719 		if ($tableName === '') {
       
   720 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   694 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   721 			throw new Zend_Service_WindowsAzure_Exception('Table name is not specified.');
   695 		}
   722 		}
   696 		if ($entity === null) {
   723 		if (is_null($entity)) {	
       
   724 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   697 			throw new Zend_Service_WindowsAzure_Exception('Entity is not specified.');
   725 			throw new Zend_Service_WindowsAzure_Exception('Entity is not specified.');
   698 		}
   726 		}
   699 		                     
   727 		                     
   700         // Add header information
   728         // Add header information
   701         $headers = array();
   729         $headers = array();
   721                               {tpl:Properties}
   749                               {tpl:Properties}
   722                             </m:properties>
   750                             </m:properties>
   723                           </content>
   751                           </content>
   724                         </entry>';
   752                         </entry>';
   725 		
   753 		
   726         // Attempt to get timestamp from entity
   754 		// Attempt to get timestamp from entity
   727         $timestamp = $entity->getTimestamp();
   755         $timestamp = $entity->getTimestamp();
   728         if ($timestamp == Zend_Service_WindowsAzure_Storage_TableEntity::DEFAULT_TIMESTAMP) {
   756         
   729             $timestamp = $this->isoDate();
       
   730         }
       
   731 
       
   732         $requestBody = $this->_fillTemplate($requestBody, array(
   757         $requestBody = $this->_fillTemplate($requestBody, array(
   733         	'Updated'    => $timestamp,
   758         	'Updated'    => $this->_convertToEdmDateTime($timestamp),
   734             'Properties' => $this->_generateAzureRepresentation($entity)
   759             'Properties' => $this->_generateAzureRepresentation($entity)
   735         ));
   760         ));
   736 
   761 
   737         // Add header information
   762         // Add header information
   738         $headers = array();
   763         $headers = array();
   744         }
   769         }
   745         
   770         
   746 		// Perform request
   771 		// Perform request
   747 		$response = null;
   772 		$response = null;
   748 	    if ($this->isInBatch()) {
   773 	    if ($this->isInBatch()) {
   749 		    $this->getCurrentBatch()->enlistOperation($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\',RowKey=\'' . $entity->getRowKey() . '\')', '', $httpVerb, $headers, true, $requestBody);
   774 		    $this->getCurrentBatch()->enlistOperation($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\', RowKey=\'' . $entity->getRowKey() . '\')', '', $httpVerb, $headers, true, $requestBody);
   750 		    return null;
   775 		    return null;
   751 		} else {
   776 		} else {
   752 		    $response = $this->_performRequest($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\',RowKey=\'' . $entity->getRowKey() . '\')', '', $httpVerb, $headers, true, $requestBody);
   777 		    $response = $this->_performRequest($tableName . '(PartitionKey=\'' . $entity->getPartitionKey() . '\', RowKey=\'' . $entity->getRowKey() . '\')', '', $httpVerb, $headers, true, $requestBody);
   753 		}
   778 		}
   754 		if ($response->isSuccessful()) {
   779 		if ($response->isSuccessful()) {
   755 		    // Update properties
   780 		    // Update properties
   756 			$entity->setEtag($response->getHeader('Etag'));
   781 			$entity->setEtag($response->getHeader('Etag'));
   757 			$entity->setTimestamp($response->getHeader('Last-modified'));
   782 			$entity->setTimestamp( $this->_convertToDateTime($response->getHeader('Last-modified')) );
   758 
   783 
   759 		    return $entity;
   784 		    return $entity;
   760 		} else {
   785 		} else {
       
   786 			require_once 'Zend/Service/WindowsAzure/Exception.php';
   761 			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   787 			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
   762 		}
   788 		}
   763 	}
   789 	}
   764 	
   790 	
   765 	/**
   791 	/**
   802 		    $value = array();
   828 		    $value = array();
   803 		    $value[] = '<d:' . $azureValue->Name;
   829 		    $value[] = '<d:' . $azureValue->Name;
   804 		    if ($azureValue->Type != '') {
   830 		    if ($azureValue->Type != '') {
   805 		        $value[] = ' m:type="' . $azureValue->Type . '"';
   831 		        $value[] = ' m:type="' . $azureValue->Type . '"';
   806 		    }
   832 		    }
   807 		    if ($azureValue->Value === null) {
   833 		    if (is_null($azureValue->Value)) {
   808 		        $value[] = ' m:null="true"'; 
   834 		        $value[] = ' m:null="true"'; 
   809 		    }
   835 		    }
   810 		    $value[] = '>';
   836 		    $value[] = '>';
   811 		    
   837 		    
   812 		    if ($azureValue->Value !== null) {
   838 		    if (!is_null($azureValue->Value)) {
   813 		        if (strtolower($azureValue->Type) == 'edm.boolean') {
   839 		        if (strtolower($azureValue->Type) == 'edm.boolean') {
   814 		            $value[] = ($azureValue->Value == true ? '1' : '0');
   840 		            $value[] = ($azureValue->Value == true ? '1' : '0');
       
   841 		        } else if (strtolower($azureValue->Type) == 'edm.datetime') {
       
   842 		        	$value[] = $this->_convertToEdmDateTime($azureValue->Value);
   815 		        } else {
   843 		        } else {
   816 		            $value[] = htmlspecialchars($azureValue->Value);
   844 		            $value[] = htmlspecialchars($azureValue->Value);
   817 		        }
   845 		        }
   818 		    }
   846 		    }
   819 		    
   847 		    
   860 			$forTableStorage,
   888 			$forTableStorage,
   861 			$rawData,
   889 			$rawData,
   862 			$resourceType,
   890 			$resourceType,
   863 			$requiredPermission
   891 			$requiredPermission
   864 		);
   892 		);
   865 	}
   893 	}  
       
   894 	  
       
   895     /**
       
   896      * Converts a string to a DateTime object. Returns false on failure.
       
   897      * 
       
   898      * @param string $value The string value to parse
       
   899      * @return DateTime|boolean
       
   900      */
       
   901     protected function _convertToDateTime($value = '') 
       
   902     {
       
   903     	if ($value instanceof DateTime) {
       
   904     		return $value;
       
   905     	}
       
   906     	
       
   907     	try {
       
   908     		if (substr($value, -1) == 'Z') {
       
   909     			$value = substr($value, 0, strlen($value) - 1);
       
   910     		}
       
   911     		return new DateTime($value, new DateTimeZone('UTC'));
       
   912     	}
       
   913     	catch (Exception $ex) {
       
   914     		return false;
       
   915     	}
       
   916     }
       
   917     
       
   918     /**
       
   919      * Converts a DateTime object into an Edm.DaeTime value in UTC timezone,
       
   920      * represented as a string.
       
   921      * 
       
   922      * @param DateTime $value
       
   923      * @return string
       
   924      */
       
   925     protected function _convertToEdmDateTime(DateTime $value) 
       
   926     {
       
   927     	$cloned = clone $value;
       
   928     	$cloned->setTimezone(new DateTimeZone('UTC'));
       
   929     	return str_replace('+0000', 'Z', $cloned->format(DateTime::ISO8601));
       
   930     }
   866 }
   931 }