Microsoft_WindowsAzure
[ class tree: Microsoft_WindowsAzure ] [ index: Microsoft_WindowsAzure ] [ all elements ]

Source for file Table.php

Documentation is available at Table.php

  1. <?php
  2. /**
  3.  * Copyright (c) 2009, RealDolmen
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions are met:
  8.  *     * Redistributions of source code must retain the above copyright
  9.  *       notice, this list of conditions and the following disclaimer.
  10.  *     * Redistributions in binary form must reproduce the above copyright
  11.  *       notice, this list of conditions and the following disclaimer in the
  12.  *       documentation and/or other materials provided with the distribution.
  13.  *     * Neither the name of RealDolmen nor the
  14.  *       names of its contributors may be used to endorse or promote products
  15.  *       derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY RealDolmen ''AS IS'' AND ANY
  18.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20.  * DISCLAIMED. IN NO EVENT SHALL RealDolmen BE LIABLE FOR ANY
  21.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  26.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  *
  28.  * @category   Microsoft
  29.  * @package    Microsoft_WindowsAzure
  30.  * @subpackage Storage
  31.  * @copyright  Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  32.  * @license    http://phpazure.codeplex.com/license
  33.  * @version    $Id: Blob.php 14561 2009-05-07 08:05:12Z unknown $
  34.  */
  35.  
  36. /**
  37.  * @see Microsoft_WindowsAzure_Credentials
  38.  */
  39. require_once 'Microsoft/WindowsAzure/Credentials.php';
  40.  
  41. /**
  42.  * @see Microsoft_WindowsAzure_SharedKeyCredentials
  43.  */
  44. require_once 'Microsoft/WindowsAzure/SharedKeyCredentials.php';
  45.  
  46. /**
  47.  * @see Microsoft_WindowsAzure_SharedKeyLiteCredentials
  48.  */
  49. require_once 'Microsoft/WindowsAzure/SharedKeyLiteCredentials.php';
  50.  
  51. /**
  52.  * @see Microsoft_WindowsAzure_RetryPolicy
  53.  */
  54. require_once 'Microsoft/WindowsAzure/RetryPolicy.php';
  55.  
  56. /**
  57.  * @see Microsoft_Http_Transport
  58.  */
  59. require_once 'Microsoft/Http/Transport.php';
  60.  
  61. /**
  62.  * @see Microsoft_Http_Response
  63.  */
  64. require_once 'Microsoft/Http/Response.php';
  65.  
  66. /**
  67.  * @see Microsoft_WindowsAzure_Storage
  68.  */
  69. require_once 'Microsoft/WindowsAzure/Storage.php';
  70.  
  71. /**
  72.  * @see Microsoft_WindowsAzure_Storage_BatchStorage
  73.  */
  74. require_once 'Microsoft/WindowsAzure/Storage/BatchStorage.php';
  75.  
  76. /**
  77.  * @see Microsoft_WindowsAzure_Storage_TableInstance
  78.  */
  79. require_once 'Microsoft/WindowsAzure/Storage/TableInstance.php';
  80.  
  81. /**
  82.  * @see Microsoft_WindowsAzure_Storage_TableEntity
  83.  */
  84. require_once 'Microsoft/WindowsAzure/Storage/TableEntity.php';
  85.  
  86. /**
  87.  * @see Microsoft_WindowsAzure_Storage_DynamicTableEntity
  88.  */
  89. require_once 'Microsoft/WindowsAzure/Storage/DynamicTableEntity.php';
  90.  
  91. /**
  92.  * @see Microsoft_WindowsAzure_Storage_TableEntityQuery
  93.  */
  94. require_once 'Microsoft/WindowsAzure/Storage/TableEntityQuery.php';
  95.  
  96. /**
  97.  * @see Microsoft_WindowsAzure_Exception
  98.  */
  99. require_once 'Microsoft/WindowsAzure/Exception.php';
  100.  
  101.  
  102. /**
  103.  * @category   Microsoft
  104.  * @package    Microsoft_WindowsAzure
  105.  * @subpackage Storage
  106.  * @copyright  Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  107.  * @license    http://phpazure.codeplex.com/license
  108.  */
  109. {
  110.     /**
  111.      * Creates a new Microsoft_WindowsAzure_Storage_Table instance
  112.      *
  113.      * @param string $host Storage host name
  114.      * @param string $accountName Account name for Windows Azure
  115.      * @param string $accountKey Account key for Windows Azure
  116.      * @param boolean $usePathStyleUri Use path-style URI's
  117.      * @param Microsoft_WindowsAzure_RetryPolicy $retryPolicy Retry policy to use when making requests
  118.      */
  119.     public function __construct($host Microsoft_WindowsAzure_Storage::URL_DEV_TABLE$accountName Microsoft_WindowsAzure_Credentials::DEVSTORE_ACCOUNT$accountKey Microsoft_WindowsAzure_Credentials::DEVSTORE_KEY$usePathStyleUri falseMicrosoft_WindowsAzure_RetryPolicy $retryPolicy null)
  120.     {
  121.         parent::__construct($host$accountName$accountKey$usePathStyleUri$retryPolicy);
  122.  
  123.         // Always use SharedKeyLite authentication
  124.         $this->_credentials = new Microsoft_WindowsAzure_SharedKeyLiteCredentials($accountName$accountKey$this->_usePathStyleUri);
  125.         
  126.         // API version
  127.         $this->_apiVersion = '2009-04-14';
  128.     }
  129.     
  130.     /**
  131.      * Check if a table exists
  132.      * 
  133.      * @param string $tableName Table name
  134.      * @return boolean 
  135.      */
  136.     public function tableExists($tableName '')
  137.     {
  138.         if ($tableName === '')
  139.             throw new Microsoft_WindowsAzure_Exception('Table name is not specified.');
  140.             
  141.         // List tables
  142.         $tables $this->listTables($tableName);
  143.         foreach ($tables as $table)
  144.         {
  145.             if ($table->Name == $tableName)
  146.                 return true;
  147.         }
  148.         
  149.         return false;
  150.     }
  151.     
  152.     /**
  153.      * List tables
  154.      *
  155.      * @param  string $nextTableName Next table name, used for listing tables when total amount of tables is > 1000.
  156.      * @return array 
  157.      * @throws Microsoft_WindowsAzure_Exception
  158.      */
  159.     public function listTables($nextTableName '')
  160.     {
  161.         // Build query string
  162.         $queryString '';
  163.         if ($nextTableName != '')
  164.         {
  165.             $queryString '?NextTableName=' $nextTableName;
  166.         }
  167.         
  168.         // Perform request
  169.         $response $this->performRequest('Tables'$queryStringMicrosoft_Http_Transport::VERB_GETnulltrue);
  170.         if ($response->isSuccessful())
  171.         {        
  172.             // Parse result
  173.             $result $this->parseResponse($response);    
  174.             
  175.             if (!$result || !$result->entry)
  176.                 return array();
  177.             
  178.             $entries null;
  179.             if (count($result->entry1)
  180.             {
  181.                 $entries $result->entry;
  182.             
  183.             else 
  184.             {
  185.                 $entries array($result->entry);
  186.             }
  187.  
  188.             // Create return value
  189.             $returnValue array();            
  190.             foreach ($entries as $entry)
  191.             {
  192.                 $tableName $entry->xpath('.//m:properties/d:TableName');
  193.                 $tableName = (string)$tableName[0];
  194.                 
  195.                 $returnValue[new Microsoft_WindowsAzure_Storage_TableInstance(
  196.                     (string)$entry->id,
  197.                     $tableName,
  198.                     (string)$entry->link['href'],
  199.                     (string)$entry->updated
  200.                 );
  201.             }
  202.             
  203.             // More tables?
  204.             if (!is_null($response->getHeader('x-ms-continuation-NextTableName')))
  205.             {
  206.                 $returnValue array_merge($returnValue$this->listTables($response->getHeader('x-ms-continuation-NextTableName')));
  207.             }
  208.  
  209.             return $returnValue;
  210.         }
  211.         else
  212.         {
  213.             throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response'Resource could not be accessed.'));
  214.         }
  215.     }
  216.     
  217.     /**
  218.      * Create table
  219.      *
  220.      * @param string $tableName Table name
  221.      * @return Microsoft_WindowsAzure_Storage_TableInstance 
  222.      * @throws Microsoft_WindowsAzure_Exception
  223.      */
  224.     public function createTable($tableName '')
  225.     {
  226.         if ($tableName === '')
  227.             throw new Microsoft_WindowsAzure_Exception('Table name is not specified.');
  228.             
  229.         // Generate request body
  230.         $requestBody '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
  231.                         <entry
  232.                             xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
  233.                             xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
  234.                             xmlns="http://www.w3.org/2005/Atom">
  235.                           <title />
  236.                           <updated>{tpl:Updated}</updated>
  237.                           <author>
  238.                             <name />
  239.                           </author>
  240.                           <id />
  241.                           <content type="application/xml">
  242.                             <m:properties>
  243.                               <d:TableName>{tpl:TableName}</d:TableName>
  244.                             </m:properties>
  245.                           </content>
  246.                         </entry>';
  247.         
  248.         $requestBody $this->fillTemplate($requestBodyarray(
  249.             'BaseUrl' => $this->getBaseUrl(),
  250.             'TableName' => $tableName,
  251.             'Updated' => $this->isoDate(),
  252.             'AccountName' => $this->_accountName
  253.         ));
  254.         
  255.         // Add header information
  256.         $headers array();
  257.         $headers['Content-Type''application/atom+xml';
  258.         $headers['DataServiceVersion''1.0;NetFx';
  259.         $headers['MaxDataServiceVersion''1.0;NetFx';        
  260.  
  261.         // Perform request
  262.         $response $this->performRequest('Tables'''Microsoft_Http_Transport::VERB_POST$headerstrue$requestBody);
  263.         if ($response->isSuccessful())
  264.         {
  265.             // Parse response
  266.             $entry $this->parseResponse($response);
  267.             
  268.             $tableName $entry->xpath('.//m:properties/d:TableName');
  269.             $tableName = (string)$tableName[0];
  270.                 
  271.             return new Microsoft_WindowsAzure_Storage_TableInstance(
  272.                 (string)$entry->id,
  273.                 $tableName,
  274.                 (string)$entry->link['href'],
  275.                 (string)$entry->updated
  276.             );
  277.         }
  278.         else
  279.         {
  280.             throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response'Resource could not be accessed.'));
  281.         }
  282.     }
  283.     
  284.     /**
  285.      * Delete table
  286.      *
  287.      * @param string $tableName Table name
  288.      * @throws Microsoft_WindowsAzure_Exception
  289.      */
  290.     public function deleteTable($tableName '')
  291.     {
  292.         if ($tableName === '')
  293.             throw new Microsoft_WindowsAzure_Exception('Table name is not specified.');
  294.  
  295.         // Add header information
  296.         $headers array();
  297.         $headers['Content-Type''application/atom+xml';
  298.  
  299.         // Perform request
  300.         $response $this->performRequest('Tables(\'' $tableName '\')'''Microsoft_Http_Transport::VERB_DELETE$headerstruenull);
  301.         if (!$response->isSuccessful())
  302.         {
  303.             throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response'Resource could not be accessed.'));
  304.         }
  305.     }
  306.     
  307.     /**
  308.      * Insert entity into table
  309.      * 
  310.      * @param string                              $tableName   Table name
  311.      * @param Microsoft_WindowsAzure_Storage_TableEntity $entity      Entity to insert
  312.      * @return Microsoft_WindowsAzure_Storage_TableEntity 
  313.      * @throws Microsoft_WindowsAzure_Exception
  314.      */
  315.     public function insertEntity($tableName ''Microsoft_WindowsAzure_Storage_TableEntity $entity null)
  316.     {
  317.         if ($tableName === '')
  318.             throw new Microsoft_WindowsAzure_Exception('Table name is not specified.');
  319.         if (is_null($entity))
  320.             throw new Microsoft_WindowsAzure_Exception('Entity is not specified.');
  321.                              
  322.         // Generate request body
  323.         $requestBody '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
  324.                         <entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
  325.                           <title />
  326.                           <updated>{tpl:Updated}</updated>
  327.                           <author>
  328.                             <name />
  329.                           </author>
  330.                           <id />
  331.                           <content type="application/xml">
  332.                             <m:properties>
  333.                               {tpl:Properties}
  334.                             </m:properties>
  335.                           </content>
  336.                         </entry>';
  337.         
  338.         $requestBody $this->fillTemplate($requestBodyarray(
  339.             'Updated'    => $this->isoDate(),
  340.             'Properties' => $this->generateAzureRepresentation($entity)
  341.         ));
  342.  
  343.         // Add header information
  344.         $headers array();
  345.         $headers['Content-Type''application/atom+xml';
  346.  
  347.         // Perform request
  348.         $response null;
  349.         if ($this->isInBatch())
  350.         {
  351.             $this->getCurrentBatch()->enlistOperation($tableName''Microsoft_Http_Transport::VERB_POST$headerstrue$requestBody);
  352.             return null;
  353.         }
  354.         else
  355.         {
  356.             $response $this->performRequest($tableName''Microsoft_Http_Transport::VERB_POST$headerstrue$requestBody);
  357.         }
  358.         if ($response->isSuccessful())
  359.         {
  360.             // Parse result
  361.             $result $this->parseResponse($response);
  362.             
  363.             $timestamp $result->xpath('//m:properties/d:Timestamp');
  364.             $timestamp = (string)$timestamp[0];
  365.  
  366.             $etag      $result->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata');
  367.             $etag      = (string)$etag['etag'];
  368.             
  369.             // Update properties
  370.             $entity->setTimestamp($timestamp);
  371.             $entity->setEtag($etag);
  372.  
  373.             return $entity;
  374.         }
  375.         else
  376.         {
  377.             throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response'Resource could not be accessed.'));
  378.         }
  379.     }
  380.     
  381.     /**
  382.      * Delete entity from table
  383.      * 
  384.      * @param string                              $tableName   Table name
  385.      * @param Microsoft_WindowsAzure_Storage_TableEntity $entity      Entity to delete
  386.      * @param boolean                             $verifyEtag  Verify etag of the entity (used for concurrency)
  387.      * @throws Microsoft_WindowsAzure_Exception
  388.      */
  389.     public function deleteEntity($tableName ''Microsoft_WindowsAzure_Storage_TableEntity $entity null$verifyEtag false)
  390.     {
  391.         if ($tableName === '')
  392.             throw new Microsoft_WindowsAzure_Exception('Table name is not specified.');
  393.         if (is_null($entity))
  394.             throw new Microsoft_WindowsAzure_Exception('Entity is not specified.');
  395.                              
  396.         // Add header information
  397.         $headers array();
  398.         if (!$this->isInBatch()) // http://social.msdn.microsoft.com/Forums/en-US/windowsazure/thread/9e255447-4dc7-458a-99d3-bdc04bdc5474/
  399.             $headers['Content-Type']   'application/atom+xml';
  400.         $headers['Content-Length'0;
  401.         if (!$verifyEtag)
  402.         {
  403.             $headers['If-Match']       '*';
  404.         
  405.         else 
  406.         {
  407.             $headers['If-Match']       $entity->getEtag();
  408.         }
  409.  
  410.         // Perform request
  411.         $response null;
  412.         if ($this->isInBatch())
  413.         {
  414.             $this->getCurrentBatch()->enlistOperation($tableName '(PartitionKey=\'' $entity->getPartitionKey('\', RowKey=\'' $entity->getRowKey('\')'''Microsoft_Http_Transport::VERB_DELETE$headerstruenull);
  415.             return null;
  416.         }
  417.         else
  418.         {
  419.             $response $this->performRequest($tableName '(PartitionKey=\'' $entity->getPartitionKey('\', RowKey=\'' $entity->getRowKey('\')'''Microsoft_Http_Transport::VERB_DELETE$headerstruenull);
  420.         }
  421.         if (!$response->isSuccessful())
  422.         {
  423.             throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response'Resource could not be accessed.'));
  424.         }
  425.     }
  426.     
  427.     /**
  428.      * Retrieve entity from table, by id
  429.      * 
  430.      * @param string $tableName    Table name
  431.      * @param string $partitionKey Partition key
  432.      * @param string $rowKey       Row key
  433.      * @param string $entityClass  Entity class name*
  434.      * @return Microsoft_WindowsAzure_Storage_TableEntity 
  435.      * @throws Microsoft_WindowsAzure_Exception
  436.      */
  437.     public function retrieveEntityById($tableName ''$partitionKey ''$rowKey ''$entityClass 'Microsoft_WindowsAzure_Storage_DynamicTableEntity')
  438.     {
  439.         if ($tableName === '')
  440.             throw new Microsoft_WindowsAzure_Exception('Table name is not specified.');
  441.         if ($partitionKey === '')
  442.             throw new Microsoft_WindowsAzure_Exception('Partition key is not specified.');
  443.         if ($rowKey === '')
  444.             throw new Microsoft_WindowsAzure_Exception('Row key is not specified.');
  445.         if ($entityClass === '')
  446.             throw new Microsoft_WindowsAzure_Exception('Entity class is not specified.');
  447.  
  448.             
  449.         // Check for combined size of partition key and row key
  450.         // http://msdn.microsoft.com/en-us/library/dd179421.aspx
  451.         if (strlen($partitionKey $rowKey>= 256)
  452.         {
  453.             // Start a batch if possible
  454.             if ($this->isInBatch())
  455.                 throw new Microsoft_WindowsAzure_Exception('Entity cannot be retrieved. A transaction is required to retrieve the entity, but another transaction is already active.');
  456.                 
  457.             $this->startBatch();
  458.         }
  459.         
  460.         // Fetch entities from Azure
  461.         $result $this->retrieveEntities(
  462.             $this->select()
  463.                  ->from($tableName)
  464.                  ->wherePartitionKey($partitionKey)
  465.                  ->whereRowKey($rowKey),
  466.             '',
  467.             $entityClass
  468.         );
  469.         
  470.         // Return
  471.         if (count($result== 1)
  472.         {
  473.             return $result[0];
  474.         }
  475.         
  476.         return null;
  477.     }
  478.     
  479.     /**
  480.      * Create a new Microsoft_WindowsAzure_Storage_TableEntityQuery
  481.      * 
  482.      * @return Microsoft_WindowsAzure_Storage_TableEntityQuery 
  483.      */
  484.     public function select()
  485.     {
  486.         return new Microsoft_WindowsAzure_Storage_TableEntityQuery();
  487.     }
  488.     
  489.     /**
  490.      * Retrieve entities from table
  491.      * 
  492.      * @param string $tableName|Microsoft_WindowsAzure_Storage_TableEntityQuery    Table name -or- Microsoft_WindowsAzure_Storage_TableEntityQuery instance
  493.      * @param string $filter                                                Filter condition (not applied when $tableName is a Microsoft_WindowsAzure_Storage_TableEntityQuery instance)
  494.      * @param string $entityClass                                           Entity class name
  495.      * @param string $nextPartitionKey                                      Next partition key, used for listing entities when total amount of entities is > 1000.
  496.      * @param string $nextRowKey                                            Next row key, used for listing entities when total amount of entities is > 1000.
  497.      * @return array Array of Microsoft_WindowsAzure_Storage_TableEntity
  498.      * @throws Microsoft_WindowsAzure_Exception
  499.      */
  500.     public function retrieveEntities($tableName ''$filter ''$entityClass 'Microsoft_WindowsAzure_Storage_DynamicTableEntity'$nextPartitionKey null$nextRowKey null)
  501.     {
  502.         if ($tableName === '')
  503.             throw new Microsoft_WindowsAzure_Exception('Table name is not specified.');
  504.         if ($entityClass === '')
  505.             throw new Microsoft_WindowsAzure_Exception('Entity class is not specified.');
  506.  
  507.         // Convenience...
  508.         if (class_exists($filter))
  509.         {
  510.             $entityClass $filter;
  511.             $filter '';
  512.         }
  513.             
  514.         // Query string
  515.         $queryString '';
  516.  
  517.         // Determine query
  518.         if (is_string($tableName))
  519.         {
  520.             // Option 1: $tableName is a string
  521.             
  522.             // Append parentheses
  523.             $tableName .= '()';
  524.             
  525.             // Build query
  526.             $query array();
  527.             
  528.             // Filter?
  529.             if ($filter !== '')
  530.             {
  531.                 $query['$filter=' rawurlencode($filter);
  532.             }
  533.                 
  534.             // Build queryString
  535.             if (count($query0)
  536.             {
  537.                 $queryString '?' implode('&'$query);
  538.             }
  539.         }
  540.         else if (get_class($tableName== 'Microsoft_WindowsAzure_Storage_TableEntityQuery')
  541.         {
  542.             // Option 2: $tableName is a Microsoft_WindowsAzure_Storage_TableEntityQuery instance
  543.  
  544.             // Build queryString
  545.             $queryString $tableName->assembleQueryString(true);
  546.  
  547.             // Change $tableName
  548.             $tableName $tableName->assembleFrom(true);
  549.         }
  550.         else
  551.         {
  552.             throw new Microsoft_WindowsAzure_Exception('Invalid argument: $tableName');
  553.         }
  554.         
  555.         // Add continuation querystring parameters?
  556.         if (!is_null($nextPartitionKey&& !is_null($nextRowKey))
  557.         {
  558.             if ($queryString !== '')
  559.                 $queryString .= '&';
  560.                 
  561.             $queryString .= '&NextPartitionKey=' rawurlencode($nextPartitionKey'&NextRowKey=' rawurlencode($nextRowKey);
  562.         }
  563.  
  564.         // Perform request
  565.         $response null;
  566.         if ($this->isInBatch(&& $this->getCurrentBatch()->getOperationCount(== 0)
  567.         {
  568.             $this->getCurrentBatch()->enlistOperation($tableName$queryStringMicrosoft_Http_Transport::VERB_GETarray()truenull);
  569.             $response $this->getCurrentBatch()->commit();
  570.             
  571.             // Get inner response (multipart)
  572.             $innerResponse $response->getBody();
  573.             $innerResponse substr($innerResponsestrpos($innerResponse'HTTP/1.1 200 OK'));
  574.             $innerResponse substr($innerResponse0strpos($innerResponse'--batchresponse'));
  575.             $response Microsoft_Http_Response::fromString($innerResponse);
  576.         }
  577.         else
  578.         {
  579.             $response $this->performRequest($tableName$queryStringMicrosoft_Http_Transport::VERB_GETarray()truenull);
  580.         }
  581.         if ($response->isSuccessful())
  582.         {
  583.             // Parse result
  584.             $result $this->parseResponse($response);
  585.             if (!$result)
  586.                 return array();
  587.  
  588.             $entries null;
  589.             if ($result->entry)
  590.             {
  591.                 if (count($result->entry1)
  592.                 {
  593.                     $entries $result->entry;
  594.                 }
  595.                 else
  596.                 {
  597.                     $entries array($result->entry);
  598.                 }
  599.             }
  600.             else
  601.             {
  602.                 // This one is tricky... If we have properties defined, we have an entity.
  603.                 $properties $result->xpath('//m:properties');
  604.                 if ($properties)
  605.                 {
  606.                     $entries array($result);
  607.                 
  608.                 else
  609.                 {
  610.                     return array();
  611.                 }
  612.             }
  613.  
  614.             // Create return value
  615.             $returnValue array();            
  616.             foreach ($entries as $entry)
  617.             {
  618.                 // Parse properties
  619.                 $properties $entry->xpath('.//m:properties');
  620.                 $properties $properties[0]->children('http://schemas.microsoft.com/ado/2007/08/dataservices');
  621.                 
  622.                 // Create entity
  623.                 $entity new $entityClass('''');
  624.                 $entity->setAzureValues((array)$propertiestrue);
  625.                 
  626.                 // If we have a Microsoft_WindowsAzure_Storage_DynamicTableEntity, make sure all property types are OK
  627.                 if ($entity instanceof Microsoft_WindowsAzure_Storage_DynamicTableEntity)
  628.                 {
  629.                     foreach ($properties as $key => $value)
  630.                     {  
  631.                         $attributes $value->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata');
  632.                         $type = (string)$attributes['type'];
  633.                         if ($type !== '')
  634.                         {
  635.                             $entity->setAzurePropertyType($key$type);
  636.                         }
  637.                     }
  638.                 }
  639.     
  640.                 // Update etag
  641.                 $etag      $entry->attributes('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata');
  642.                 $etag      = (string)$etag['etag'];
  643.                 $entity->setEtag($etag);
  644.                 
  645.                 // Add to result
  646.                 $returnValue[$entity;
  647.             }
  648.  
  649.             // More entities?
  650.             if (!is_null($response->getHeader('x-ms-continuation-NextPartitionKey')) && !is_null($response->getHeader('x-ms-continuation-NextRowKey')))
  651.             {
  652.                 if (strpos($queryString'$top'=== false)
  653.                     $returnValue array_merge($returnValue$this->retrieveEntities($tableName$filter$entityClass$response->getHeader('x-ms-continuation-NextPartitionKey')$response->getHeader('x-ms-continuation-NextRowKey')));
  654.             }
  655.             
  656.             // Return
  657.             return $returnValue;
  658.         }
  659.         else
  660.         {
  661.             throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response'Resource could not be accessed.'));
  662.         }
  663.     }
  664.     
  665.     /**
  666.      * Update entity by replacing it
  667.      * 
  668.      * @param string                              $tableName   Table name
  669.      * @param Microsoft_WindowsAzure_Storage_TableEntity $entity      Entity to update
  670.      * @param boolean                             $verifyEtag  Verify etag of the entity (used for concurrency)
  671.      * @throws Microsoft_WindowsAzure_Exception
  672.      */
  673.     public function updateEntity($tableName ''Microsoft_WindowsAzure_Storage_TableEntity $entity null$verifyEtag false)
  674.     {
  675.         return $this->changeEntity(Microsoft_Http_Transport::VERB_PUT$tableName$entity$verifyEtag);
  676.     }
  677.     
  678.     /**
  679.      * Update entity by adding or updating properties
  680.      * 
  681.      * @param string                              $tableName   Table name
  682.      * @param Microsoft_WindowsAzure_Storage_TableEntity $entity      Entity to update
  683.      * @param boolean                             $verifyEtag  Verify etag of the entity (used for concurrency)
  684.      * @param array                               $properties  Properties to merge. All properties will be used when omitted.
  685.      * @throws Microsoft_WindowsAzure_Exception
  686.      */
  687.     public function mergeEntity($tableName ''Microsoft_WindowsAzure_Storage_TableEntity $entity null$verifyEtag false$properties array())
  688.     {
  689.         $mergeEntity null;
  690.         if (is_array($properties&& count($properties0)
  691.         {
  692.             // Build a new object
  693.             $mergeEntity new Microsoft_WindowsAzure_Storage_DynamicTableEntity($entity->getPartitionKey()$entity->getRowKey());
  694.             
  695.             // Keep only values mentioned in $properties
  696.             $azureValues $entity->getAzureValues();
  697.             foreach ($azureValues as $key => $value)
  698.             {
  699.                 if (in_array($value->Name$properties))
  700.                 {
  701.                     $mergeEntity->setAzureProperty($value->Name$value->Value$value->Type);
  702.                 }
  703.             }
  704.         }
  705.         else
  706.         {
  707.             $mergeEntity $entity;
  708.         }
  709.         
  710.         return $this->changeEntity(Microsoft_Http_Transport::VERB_MERGE$tableName$mergeEntity$verifyEtag);
  711.     }
  712.     
  713.     /**
  714.      * Get error message from Microsoft_Http_Response
  715.      * 
  716.      * @param Microsoft_Http_Response $response Repsonse
  717.      * @param string $alternativeError Alternative error message
  718.      * @return string 
  719.      */
  720.     protected function getErrorMessage(Microsoft_Http_Response $response$alternativeError 'Unknown error.')
  721.     {
  722.         $response $this->parseResponse($response);
  723.         if ($response && $response->message)
  724.             return (string)$response->message;
  725.         else
  726.             return $alternativeError;
  727.     }
  728.     
  729.     /**
  730.      * Update entity / merge entity
  731.      * 
  732.      * @param string                              $httpVerb    HTTP verb to use (PUT = update, MERGE = merge)
  733.      * @param string                              $tableName   Table name
  734.      * @param Microsoft_WindowsAzure_Storage_TableEntity $entity      Entity to update
  735.      * @param boolean                             $verifyEtag  Verify etag of the entity (used for concurrency)
  736.      * @throws Microsoft_WindowsAzure_Exception
  737.      */
  738.     protected function changeEntity($httpVerb Microsoft_Http_Transport::VERB_PUT$tableName ''Microsoft_WindowsAzure_Storage_TableEntity $entity null$verifyEtag false)
  739.     {
  740.         if ($tableName === '')
  741.             throw new Microsoft_WindowsAzure_Exception('Table name is not specified.');
  742.         if (is_null($entity))
  743.             throw new Microsoft_WindowsAzure_Exception('Entity is not specified.');
  744.                              
  745.         // Add header information
  746.         $headers array();
  747.         $headers['Content-Type']   'application/atom+xml';
  748.         $headers['Content-Length'0;
  749.         if (!$verifyEtag
  750.         {
  751.             $headers['If-Match']       '*';
  752.         
  753.         else 
  754.         {
  755.             $headers['If-Match']       $entity->getEtag();
  756.         }
  757.  
  758.         // Generate request body
  759.         $requestBody '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
  760.                         <entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
  761.                           <title />
  762.                           <updated>{tpl:Updated}</updated>
  763.                           <author>
  764.                             <name />
  765.                           </author>
  766.                           <id />
  767.                           <content type="application/xml">
  768.                             <m:properties>
  769.                               {tpl:Properties}
  770.                             </m:properties>
  771.                           </content>
  772.                         </entry>';
  773.         
  774.         $requestBody $this->fillTemplate($requestBodyarray(
  775.             'Updated'    => $this->isoDate(),
  776.             'Properties' => $this->generateAzureRepresentation($entity)
  777.         ));
  778.  
  779.         // Add header information
  780.         $headers array();
  781.         $headers['Content-Type''application/atom+xml';
  782.         if (!$verifyEtag
  783.         {
  784.             $headers['If-Match']       '*';
  785.         
  786.         else 
  787.         {
  788.             $headers['If-Match']       $entity->getEtag();
  789.         }
  790.         
  791.         // Perform request
  792.         $response null;
  793.         if ($this->isInBatch())
  794.         {
  795.             $this->getCurrentBatch()->enlistOperation($tableName '(PartitionKey=\'' $entity->getPartitionKey('\', RowKey=\'' $entity->getRowKey('\')'''$httpVerb$headerstrue$requestBody);
  796.             return null;
  797.         }
  798.         else
  799.         {
  800.             $response $this->performRequest($tableName '(PartitionKey=\'' $entity->getPartitionKey('\', RowKey=\'' $entity->getRowKey('\')'''$httpVerb$headerstrue$requestBody);
  801.         }
  802.         if ($response->isSuccessful())
  803.         {
  804.             // Update properties
  805.             $entity->setEtag($response->getHeader('Etag'));
  806.             $entity->setTimestamp($response->getHeader('Last-modified'));
  807.  
  808.             return $entity;
  809.         }
  810.         else
  811.         {
  812.             throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response'Resource could not be accessed.'));
  813.         }
  814.     }
  815.     
  816.     /**
  817.      * Generate RFC 1123 compliant date string
  818.      * 
  819.      * @return string 
  820.      */
  821.     protected function rfcDate()
  822.     {
  823.         return gmdate('D, d M Y H:i:s'time()) ' GMT'// RFC 1123
  824.     }
  825.     
  826.     /**
  827.      * Fill text template with variables from key/value array
  828.      * 
  829.      * @param string $templateText Template text
  830.      * @param array $variables Array containing key/value pairs
  831.      * @return string 
  832.      */
  833.     protected function fillTemplate($templateText$variables array())
  834.     {
  835.         foreach ($variables as $key => $value)
  836.         {
  837.             $templateText str_replace('{tpl:' $key '}'$value$templateText);
  838.         }
  839.         return $templateText;
  840.     }
  841.     
  842.     /**
  843.      * Generate Azure representation from entity (creates atompub markup from properties)
  844.      * 
  845.      * @param Microsoft_WindowsAzure_Storage_TableEntity $entity 
  846.      * @return string 
  847.      */
  848.     protected function generateAzureRepresentation(Microsoft_WindowsAzure_Storage_TableEntity $entity null)
  849.     {
  850.         // Generate Azure representation from entity
  851.         $azureRepresentation array();
  852.         $azureValues         $entity->getAzureValues();
  853.         foreach ($azureValues as $azureValue)
  854.         {
  855.             $value array();
  856.             $value['<d:' $azureValue->Name;
  857.             if ($azureValue->Type != '')
  858.                 $value[' m:type="' $azureValue->Type '"';
  859.             if (is_null($azureValue->Value))
  860.                 $value[' m:null="true"'
  861.             $value['>';
  862.             
  863.             if (!is_null($azureValue->Value))
  864.             {
  865.                 if (strtolower($azureValue->Type== 'edm.boolean')
  866.                 {
  867.                     $value[($azureValue->Value == true '1' '0');
  868.                 }
  869.                 else
  870.                 {
  871.                     $value[$azureValue->Value;
  872.                 }
  873.             }
  874.             
  875.             $value['</d:' $azureValue->Name '>';
  876.             $azureRepresentation[implode(''$value);
  877.         }
  878.  
  879.         return implode(''$azureRepresentation);
  880.     }
  881. }

Documentation generated on Thu, 26 Nov 2009 08:05:33 +0100 by phpDocumentor 1.4.3