PageRenderTime 54ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/WindowsAzure/Table/TableRestProxy.php

http://github.com/WindowsAzure/azure-sdk-for-php
PHP | 1298 lines | 815 code | 138 blank | 345 comment | 50 complexity | 11a23e7c18543be405b656a8bcdbbf09 MD5 | raw file
  1. <?php
  2. /**
  3. * LICENSE: Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. * http://www.apache.org/licenses/LICENSE-2.0
  7. *
  8. * Unless required by applicable law or agreed to in writing, software
  9. * distributed under the License is distributed on an "AS IS" BASIS,
  10. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. * See the License for the specific language governing permissions and
  12. * limitations under the License.
  13. *
  14. * PHP version 5
  15. *
  16. * @category Microsoft
  17. * @package WindowsAzure\Table
  18. * @author Azure PHP SDK <azurephpsdk@microsoft.com>
  19. * @copyright 2012 Microsoft Corporation
  20. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
  21. * @link https://github.com/windowsazure/azure-sdk-for-php
  22. */
  23. namespace WindowsAzure\Table;
  24. use WindowsAzure\Common\Internal\Resources;
  25. use WindowsAzure\Common\Internal\Utilities;
  26. use WindowsAzure\Common\Internal\Validate;
  27. use WindowsAzure\Common\Internal\Http\HttpCallContext;
  28. use WindowsAzure\Common\Models\ServiceProperties;
  29. use WindowsAzure\Common\Internal\ServiceRestProxy;
  30. use WindowsAzure\Common\Models\GetServicePropertiesResult;
  31. use WindowsAzure\Table\Internal\ITable;
  32. use WindowsAzure\Table\Models\TableServiceOptions;
  33. use WindowsAzure\Table\Models\EdmType;
  34. use WindowsAzure\Table\Models\Filters;
  35. use WindowsAzure\Table\Models\Filters\Filter;
  36. use WindowsAzure\Table\Models\GetTableResult;
  37. use WindowsAzure\Table\Models\QueryTablesOptions;
  38. use WindowsAzure\Table\Models\QueryTablesResult;
  39. use WindowsAzure\Table\Models\InsertEntityResult;
  40. use WindowsAzure\Table\Models\UpdateEntityResult;
  41. use WindowsAzure\Table\Models\QueryEntitiesOptions;
  42. use WindowsAzure\Table\Models\QueryEntitiesResult;
  43. use WindowsAzure\Table\Models\DeleteEntityOptions;
  44. use WindowsAzure\Table\Models\GetEntityResult;
  45. use WindowsAzure\Table\Models\BatchOperationType;
  46. use WindowsAzure\Table\Models\BatchOperationParameterName;
  47. use WindowsAzure\Table\Models\BatchResult;
  48. /**
  49. * This class constructs HTTP requests and receive HTTP responses for table
  50. * service layer.
  51. *
  52. * @category Microsoft
  53. * @package WindowsAzure\Table
  54. * @author Azure PHP SDK <azurephpsdk@microsoft.com>
  55. * @copyright 2012 Microsoft Corporation
  56. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
  57. * @version Release: @package_version@
  58. * @link https://github.com/windowsazure/azure-sdk-for-php
  59. */
  60. class TableRestProxy extends ServiceRestProxy implements ITable
  61. {
  62. /**
  63. * @var Utilities\IAtomReaderWriter
  64. */
  65. private $_atomSerializer;
  66. /**
  67. *
  68. * @var Utilities\IMimeReaderWriter
  69. */
  70. private $_mimeSerializer;
  71. /**
  72. * Creates contexts for batch operations.
  73. *
  74. * @param array $operations The batch operations array.
  75. *
  76. * @return array
  77. *
  78. * @throws \InvalidArgumentException
  79. */
  80. private function _createOperationsContexts($operations)
  81. {
  82. $contexts = array();
  83. foreach ($operations as $operation) {
  84. $context = null;
  85. $type = $operation->getType();
  86. switch ($type) {
  87. case BatchOperationType::INSERT_ENTITY_OPERATION:
  88. case BatchOperationType::UPDATE_ENTITY_OPERATION:
  89. case BatchOperationType::MERGE_ENTITY_OPERATION:
  90. case BatchOperationType::INSERT_REPLACE_ENTITY_OPERATION:
  91. case BatchOperationType::INSERT_MERGE_ENTITY_OPERATION:
  92. $table = $operation->getParameter(
  93. BatchOperationParameterName::BP_TABLE
  94. );
  95. $entity = $operation->getParameter(
  96. BatchOperationParameterName::BP_ENTITY
  97. );
  98. $context = $this->_getOperationContext($table, $entity, $type);
  99. break;
  100. case BatchOperationType::DELETE_ENTITY_OPERATION:
  101. $table = $operation->getParameter(
  102. BatchOperationParameterName::BP_TABLE
  103. );
  104. $partitionKey = $operation->getParameter(
  105. BatchOperationParameterName::BP_PARTITION_KEY
  106. );
  107. $rowKey = $operation->getParameter(
  108. BatchOperationParameterName::BP_ROW_KEY
  109. );
  110. $etag = $operation->getParameter(
  111. BatchOperationParameterName::BP_ETAG
  112. );
  113. $options = new DeleteEntityOptions();
  114. $options->setEtag($etag);
  115. $context = $this->_constructDeleteEntityContext(
  116. $table, $partitionKey, $rowKey, $options
  117. );
  118. break;
  119. default:
  120. throw new \InvalidArgumentException();
  121. }
  122. $contexts[] = $context;
  123. }
  124. return $contexts;
  125. }
  126. /**
  127. * Creates operation context for the API.
  128. *
  129. * @param string $table The table name.
  130. * @param Models\Entity $entity The entity object.
  131. * @param string $type The API type.
  132. *
  133. * @return WindowsAzure\Common\Internal\Http\HttpCallContext
  134. *
  135. * @throws \InvalidArgumentException
  136. */
  137. private function _getOperationContext($table, $entity, $type)
  138. {
  139. switch ($type) {
  140. case BatchOperationType::INSERT_ENTITY_OPERATION:
  141. return $this->_constructInsertEntityContext($table, $entity, null);
  142. case BatchOperationType::UPDATE_ENTITY_OPERATION:
  143. return $this->_constructPutOrMergeEntityContext(
  144. $table,
  145. $entity,
  146. Resources::HTTP_PUT,
  147. true,
  148. null
  149. );
  150. case BatchOperationType::MERGE_ENTITY_OPERATION:
  151. return $this->_constructPutOrMergeEntityContext(
  152. $table,
  153. $entity,
  154. Resources::HTTP_MERGE,
  155. true,
  156. null
  157. );
  158. case BatchOperationType::INSERT_REPLACE_ENTITY_OPERATION:
  159. return $this->_constructPutOrMergeEntityContext(
  160. $table,
  161. $entity,
  162. Resources::HTTP_PUT,
  163. false,
  164. null
  165. );
  166. case BatchOperationType::INSERT_MERGE_ENTITY_OPERATION:
  167. return $this->_constructPutOrMergeEntityContext(
  168. $table,
  169. $entity,
  170. Resources::HTTP_MERGE,
  171. false,
  172. null
  173. );
  174. default:
  175. throw new \InvalidArgumentException();
  176. }
  177. }
  178. /**
  179. * Creates MIME part body for batch API.
  180. *
  181. * @param array $operations The batch operations.
  182. * @param array $contexts The contexts objects.
  183. *
  184. * @return array
  185. *
  186. * @throws \InvalidArgumentException
  187. */
  188. private function _createBatchRequestBody($operations, $contexts)
  189. {
  190. $mimeBodyParts = array();
  191. $contentId = 1;
  192. $count = count($operations);
  193. Validate::isTrue(
  194. count($operations) == count($contexts),
  195. Resources::INVALID_OC_COUNT_MSG
  196. );
  197. for ($i = 0; $i < $count; $i++) {
  198. $operation = $operations[$i];
  199. $context = $contexts[$i];
  200. $type = $operation->getType();
  201. switch ($type) {
  202. case BatchOperationType::INSERT_ENTITY_OPERATION:
  203. case BatchOperationType::UPDATE_ENTITY_OPERATION:
  204. case BatchOperationType::MERGE_ENTITY_OPERATION:
  205. case BatchOperationType::INSERT_REPLACE_ENTITY_OPERATION:
  206. case BatchOperationType::INSERT_MERGE_ENTITY_OPERATION:
  207. $contentType = $context->getHeader(Resources::CONTENT_TYPE);
  208. $body = $context->getBody();
  209. $contentType .= ';type=entry';
  210. $context->addOptionalHeader(Resources::CONTENT_TYPE, $contentType);
  211. // Use mb_strlen instead of strlen to get the length of the string
  212. // in bytes instead of the length in chars.
  213. $context->addOptionalHeader(
  214. Resources::CONTENT_LENGTH,
  215. mb_strlen($body)
  216. );
  217. break;
  218. case BatchOperationType::DELETE_ENTITY_OPERATION:
  219. break;
  220. default:
  221. throw new \InvalidArgumentException();
  222. }
  223. $context->addOptionalHeader(Resources::CONTENT_ID, $contentId);
  224. $mimeBodyPart = $context->__toString();
  225. $mimeBodyParts[] = $mimeBodyPart;
  226. $contentId++;
  227. }
  228. return $this->_mimeSerializer->encodeMimeMultipart($mimeBodyParts);
  229. }
  230. /**
  231. * Constructs HTTP call context for deleteEntity API.
  232. *
  233. * @param string $table The name of the table.
  234. * @param string $partitionKey The entity partition key.
  235. * @param string $rowKey The entity row key.
  236. * @param Models\DeleteEntityOptions $options The optional parameters.
  237. *
  238. * @return HttpCallContext
  239. */
  240. private function _constructDeleteEntityContext($table, $partitionKey, $rowKey,
  241. $options
  242. ) {
  243. Validate::isString($table, 'table');
  244. Validate::notNullOrEmpty($table, 'table');
  245. Validate::isTrue(!is_null($partitionKey), Resources::NULL_TABLE_KEY_MSG);
  246. Validate::isTrue(!is_null($rowKey), Resources::NULL_TABLE_KEY_MSG);
  247. $method = Resources::HTTP_DELETE;
  248. $headers = array();
  249. $queryParams = array();
  250. $statusCode = Resources::STATUS_NO_CONTENT;
  251. $path = $this->_getEntityPath($table, $partitionKey, $rowKey);
  252. if (is_null($options)) {
  253. $options = new DeleteEntityOptions();
  254. }
  255. $etagObj = $options->getEtag();
  256. $Etag = !is_null($etagObj);
  257. $this->addOptionalQueryParam(
  258. $queryParams,
  259. Resources::QP_TIMEOUT,
  260. $options->getTimeout()
  261. );
  262. $this->addOptionalHeader(
  263. $headers,
  264. Resources::IF_MATCH,
  265. $Etag ? $etagObj : Resources::ASTERISK
  266. );
  267. $context = new HttpCallContext();
  268. $context->setHeaders($headers);
  269. $context->setMethod($method);
  270. $context->setPath($path);
  271. $context->setQueryParameters($queryParams);
  272. $context->addStatusCode($statusCode);
  273. $context->setUri($this->getUri());
  274. $context->setBody('');
  275. return $context;
  276. }
  277. /**
  278. * Constructs HTTP call context for updateEntity, mergeEntity,
  279. * insertOrReplaceEntity and insertOrMergeEntity.
  280. *
  281. * @param string $table The table name.
  282. * @param Models\Entity $entity The entity instance to use.
  283. * @param string $verb The HTTP method.
  284. * @param boolean $useEtag The flag to include etag or not.
  285. * @param Models\TableServiceOptions $options The optional parameters.
  286. *
  287. * @return HttpCallContext
  288. */
  289. private function _constructPutOrMergeEntityContext($table, $entity, $verb,
  290. $useEtag, $options
  291. ) {
  292. Validate::isString($table, 'table');
  293. Validate::notNullOrEmpty($table, 'table');
  294. Validate::notNullOrEmpty($entity, 'entity');
  295. Validate::isTrue($entity->isValid($msg), $msg);
  296. $method = $verb;
  297. $headers = array();
  298. $queryParams = array();
  299. $statusCode = Resources::STATUS_NO_CONTENT;
  300. $partitionKey = $entity->getPartitionKey();
  301. $rowKey = $entity->getRowKey();
  302. $path = $this->_getEntityPath($table, $partitionKey, $rowKey);
  303. $body = $this->_atomSerializer->getEntity($entity);
  304. if (is_null($options)) {
  305. $options = new TableServiceOptions();
  306. }
  307. if ($useEtag) {
  308. $etag = $entity->getEtag();
  309. $ifMatchValue = is_null($etag) ? Resources::ASTERISK : $etag;
  310. $this->addOptionalHeader($headers, Resources::IF_MATCH, $ifMatchValue);
  311. }
  312. $this->addOptionalQueryParam(
  313. $queryParams,
  314. Resources::QP_TIMEOUT,
  315. $options->getTimeout()
  316. );
  317. $this->addOptionalHeader(
  318. $headers,
  319. Resources::CONTENT_TYPE,
  320. Resources::XML_ATOM_CONTENT_TYPE
  321. );
  322. $context = new HttpCallContext();
  323. $context->setBody($body);
  324. $context->setHeaders($headers);
  325. $context->setMethod($method);
  326. $context->setPath($path);
  327. $context->setQueryParameters($queryParams);
  328. $context->addStatusCode($statusCode);
  329. $context->setUri($this->getUri());
  330. return $context;
  331. }
  332. /**
  333. * Constructs HTTP call context for insertEntity API.
  334. *
  335. * @param string $table The name of the table.
  336. * @param Models\Entity $entity The table entity.
  337. * @param Models\TableServiceOptions $options The optional parameters.
  338. *
  339. * @return HttpCallContext
  340. */
  341. private function _constructInsertEntityContext($table, $entity, $options)
  342. {
  343. Validate::isString($table, 'table');
  344. Validate::notNullOrEmpty($table, 'table');
  345. Validate::notNullOrEmpty($entity, 'entity');
  346. Validate::isTrue($entity->isValid($msg), $msg);
  347. $method = Resources::HTTP_POST;
  348. $context = new HttpCallContext();
  349. $headers = array();
  350. $queryParams = array();
  351. $statusCode = Resources::STATUS_CREATED;
  352. $path = $table;
  353. $body = $this->_atomSerializer->getEntity($entity);
  354. if (is_null($options)) {
  355. $options = new TableServiceOptions();
  356. }
  357. $this->addOptionalQueryParam(
  358. $queryParams,
  359. Resources::QP_TIMEOUT,
  360. $options->getTimeout()
  361. );
  362. $this->addOptionalHeader(
  363. $headers,
  364. Resources::CONTENT_TYPE,
  365. Resources::XML_ATOM_CONTENT_TYPE
  366. );
  367. $context->setBody($body);
  368. $context->setHeaders($headers);
  369. $context->setMethod($method);
  370. $context->setPath($path);
  371. $context->setQueryParameters($queryParams);
  372. $context->addStatusCode($statusCode);
  373. $context->setUri($this->getUri());
  374. return $context;
  375. }
  376. /**
  377. * Constructs URI path for entity.
  378. *
  379. * @param string $table The table name.
  380. * @param string $partitionKey The entity's partition key.
  381. * @param string $rowKey The entity's row key.
  382. *
  383. * @return string
  384. */
  385. private function _getEntityPath($table, $partitionKey, $rowKey)
  386. {
  387. $encodedPK = $this->_encodeODataUriValue($partitionKey);
  388. $encodedRK = $this->_encodeODataUriValue($rowKey);
  389. return "$table(PartitionKey='$encodedPK',RowKey='$encodedRK')";
  390. }
  391. /**
  392. * Does actual work for update and merge entity APIs.
  393. *
  394. * @param string $table The table name.
  395. * @param Models\Entity $entity The entity instance to use.
  396. * @param string $verb The HTTP method.
  397. * @param boolean $useEtag The flag to include etag or not.
  398. * @param Models\TableServiceOptions $options The optional parameters.
  399. *
  400. * @return Models\UpdateEntityResult
  401. */
  402. private function _putOrMergeEntityImpl($table, $entity, $verb, $useEtag,
  403. $options
  404. ) {
  405. $context = $this->_constructPutOrMergeEntityContext(
  406. $table,
  407. $entity,
  408. $verb,
  409. $useEtag,
  410. $options
  411. );
  412. $response = $this->sendContext($context);
  413. return UpdateEntityResult::create($response->getHeader());
  414. }
  415. /**
  416. * Builds filter expression
  417. *
  418. * @param Filter $filter The filter object
  419. *
  420. * @return string
  421. */
  422. private function _buildFilterExpression($filter)
  423. {
  424. $e = Resources::EMPTY_STRING;
  425. $this->_buildFilterExpressionRec($filter, $e);
  426. return $e;
  427. }
  428. /**
  429. * Builds filter expression
  430. *
  431. * @param Filter $filter The filter object
  432. * @param string &$e The filter expression
  433. *
  434. * @return string
  435. */
  436. private function _buildFilterExpressionRec($filter, &$e)
  437. {
  438. if (is_null($filter)) {
  439. return;
  440. }
  441. if ($filter instanceof Filters\PropertyNameFilter) {
  442. $e .= $filter->getPropertyName();
  443. } else if ($filter instanceof Filters\ConstantFilter) {
  444. $value = $filter->getValue();
  445. // If the value is null we just append null regardless of the edmType.
  446. if (is_null($value)) {
  447. $e .= 'null';
  448. } else {
  449. $type = $filter->getEdmType();
  450. $e .= EdmType::serializeQueryValue($type, $value);
  451. }
  452. } else if ($filter instanceof Filters\UnaryFilter) {
  453. $e .= $filter->getOperator();
  454. $e .= '(';
  455. $this->_buildFilterExpressionRec($filter->getOperand(), $e);
  456. $e .= ')';
  457. } else if ($filter instanceof Filters\BinaryFilter) {
  458. $e .= '(';
  459. $this->_buildFilterExpressionRec($filter->getLeft(), $e);
  460. $e .= ' ';
  461. $e .= $filter->getOperator();
  462. $e .= ' ';
  463. $this->_buildFilterExpressionRec($filter->getRight(), $e);
  464. $e .= ')';
  465. } else if ($filter instanceof Filters\QueryStringFilter) {
  466. $e .= $filter->getQueryString();
  467. }
  468. return $e;
  469. }
  470. /**
  471. * Adds query object to the query parameter array
  472. *
  473. * @param array $queryParam The URI query parameters
  474. * @param Models\Query $query The query object
  475. *
  476. * @return array
  477. */
  478. private function _addOptionalQuery($queryParam, $query)
  479. {
  480. if (!is_null($query)) {
  481. $selectedFields = $query->getSelectFields();
  482. if (!empty($selectedFields)) {
  483. $final = $this->_encodeODataUriValues($selectedFields);
  484. $this->addOptionalQueryParam(
  485. $queryParam,
  486. Resources::QP_SELECT,
  487. implode(',', $final)
  488. );
  489. }
  490. if (!is_null($query->getTop())) {
  491. $final = strval($this->_encodeODataUriValue($query->getTop()));
  492. $this->addOptionalQueryParam(
  493. $queryParam,
  494. Resources::QP_TOP,
  495. $final
  496. );
  497. }
  498. if (!is_null($query->getFilter())) {
  499. $final = $this->_buildFilterExpression($query->getFilter());
  500. $this->addOptionalQueryParam(
  501. $queryParam,
  502. Resources::QP_FILTER,
  503. $final
  504. );
  505. }
  506. }
  507. return $queryParam;
  508. }
  509. /**
  510. * Encodes OData URI values
  511. *
  512. * @param array $values The OData URL values
  513. *
  514. * @return array
  515. */
  516. private function _encodeODataUriValues($values)
  517. {
  518. $list = array();
  519. foreach ($values as $value) {
  520. $list[] = $this->_encodeODataUriValue($value);
  521. }
  522. return $list;
  523. }
  524. /**
  525. * Encodes OData URI value
  526. *
  527. * @param string $value The OData URL value
  528. *
  529. * @return string
  530. */
  531. private function _encodeODataUriValue($value)
  532. {
  533. // Replace each single quote (') with double single quotes ('') not doudle
  534. // quotes (")
  535. $value = str_replace('\'', '\'\'', $value);
  536. // Encode the special URL characters
  537. $value = rawurlencode($value);
  538. return $value;
  539. }
  540. /**
  541. * Initializes new TableRestProxy object.
  542. *
  543. * @param IHttpClient $channel The HTTP client channel.
  544. * @param string $uri The storage account uri.
  545. * @param IAtomReaderWriter $atomSerializer The atom serializer.
  546. * @param IMimeReaderWriter $mimeSerializer The MIME serializer.
  547. * @param ISerializable $dataSerializer The data serializer.
  548. */
  549. public function __construct($channel, $uri, $atomSerializer, $mimeSerializer,
  550. $dataSerializer
  551. ) {
  552. parent::__construct(
  553. $channel,
  554. $uri,
  555. Resources::EMPTY_STRING,
  556. $dataSerializer
  557. );
  558. $this->_atomSerializer = $atomSerializer;
  559. $this->_mimeSerializer = $mimeSerializer;
  560. }
  561. /**
  562. * Gets the properties of the Table service.
  563. *
  564. * @param Models\TableServiceOptions $options optional table service options.
  565. *
  566. * @return WindowsAzure\Common\Models\GetServicePropertiesResult
  567. *
  568. * @see http://msdn.microsoft.com/en-us/library/windowsazure/hh452238.aspx
  569. */
  570. public function getServiceProperties($options = null)
  571. {
  572. if (is_null($options)) {
  573. $options = new TableServiceOptions();
  574. }
  575. $context = new HttpCallContext();
  576. $timeout = $options->getTimeout();
  577. $context->setMethod(Resources::HTTP_GET);
  578. $context->addOptionalQueryParameter(Resources::QP_REST_TYPE, 'service');
  579. $context->addOptionalQueryParameter(Resources::QP_COMP, 'properties');
  580. $context->addOptionalQueryParameter(Resources::QP_TIMEOUT, $timeout);
  581. $context->addStatusCode(Resources::STATUS_OK);
  582. $response = $this->sendContext($context);
  583. $parsed = $this->dataSerializer->unserialize($response->getBody());
  584. return GetServicePropertiesResult::create($parsed);
  585. }
  586. /**
  587. * Sets the properties of the Table service.
  588. *
  589. * It's recommended to use getServiceProperties, alter the returned object and
  590. * then use setServiceProperties with this altered object.
  591. *
  592. * @param ServiceProperties $serviceProperties new service properties
  593. * @param Models\TableServiceOptions $options optional parameters
  594. *
  595. * @return none.
  596. *
  597. * @see http://msdn.microsoft.com/en-us/library/windowsazure/hh452240.aspx
  598. */
  599. public function setServiceProperties($serviceProperties, $options = null)
  600. {
  601. Validate::isTrue(
  602. $serviceProperties instanceof ServiceProperties,
  603. Resources::INVALID_SVC_PROP_MSG
  604. );
  605. $method = Resources::HTTP_PUT;
  606. $headers = array();
  607. $postParams = array();
  608. $queryParams = array();
  609. $statusCode = Resources::STATUS_ACCEPTED;
  610. $path = Resources::EMPTY_STRING;
  611. $body = Resources::EMPTY_STRING;
  612. if (is_null($options)) {
  613. $options = new TableServiceOptions();
  614. }
  615. $this->addOptionalQueryParam(
  616. $queryParams,
  617. Resources::QP_TIMEOUT,
  618. $options->getTimeout()
  619. );
  620. $this->addOptionalQueryParam(
  621. $queryParams,
  622. Resources::QP_REST_TYPE,
  623. 'service'
  624. );
  625. $this->addOptionalQueryParam(
  626. $queryParams,
  627. Resources::QP_COMP,
  628. 'properties'
  629. );
  630. $this->addOptionalHeader(
  631. $headers,
  632. Resources::CONTENT_TYPE,
  633. Resources::XML_ATOM_CONTENT_TYPE
  634. );
  635. $body = $serviceProperties->toXml($this->dataSerializer);
  636. $this->send(
  637. $method,
  638. $headers,
  639. $queryParams,
  640. $postParams,
  641. $path,
  642. $statusCode,
  643. $body
  644. );
  645. }
  646. /**
  647. * Quries tables in the given storage account.
  648. *
  649. * @param Models\QueryTablesOptions|string|Models\Filter $options Could be
  650. * optional parameters, table prefix or filter to apply.
  651. *
  652. * @return Models\QueryTablesResult
  653. *
  654. * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179405.aspx
  655. */
  656. public function queryTables($options = null)
  657. {
  658. $method = Resources::HTTP_GET;
  659. $headers = array();
  660. $postParams = array();
  661. $queryParams = array();
  662. $statusCode = Resources::STATUS_OK;
  663. $path = 'Tables';
  664. if (is_null($options)) {
  665. $options = new QueryTablesOptions();
  666. } else if (is_string($options)) {
  667. $prefix = $options;
  668. $options = new QueryTablesOptions();
  669. $options->setPrefix($prefix);
  670. } else if ($options instanceof Filter) {
  671. $filter = $options;
  672. $options = new QueryTablesOptions();
  673. $options->setFilter($filter);
  674. }
  675. $query = $options->getQuery();
  676. $next = $options->getNextTableName();
  677. $prefix = $options->getPrefix();
  678. $timeout = $options->getTimeout();
  679. if (!empty($prefix)) {
  680. // Append Max char to end '{' is 1 + 'z' in AsciiTable ==> upperBound
  681. // is prefix + '{'
  682. $prefixFilter = Filter::applyAnd(
  683. Filter::applyGe(
  684. Filter::applyPropertyName('TableName'),
  685. Filter::applyConstant($prefix, EdmType::STRING)
  686. ),
  687. Filter::applyLe(
  688. Filter::applyPropertyName('TableName'),
  689. Filter::applyConstant($prefix . '{', EdmType::STRING)
  690. )
  691. );
  692. if (is_null($query)) {
  693. $query = new Models\Query();
  694. }
  695. if (is_null($query->getFilter())) {
  696. // use the prefix filter if the query filter is null
  697. $query->setFilter($prefixFilter);
  698. } else {
  699. // combine and use the prefix filter if the query filter exists
  700. $combinedFilter = Filter::applyAnd(
  701. $query->getFilter(), $prefixFilter
  702. );
  703. $query->setFilter($combinedFilter);
  704. }
  705. }
  706. $queryParams = $this->_addOptionalQuery($queryParams, $query);
  707. $this->addOptionalQueryParam(
  708. $queryParams,
  709. Resources::QP_NEXT_TABLE_NAME,
  710. $next
  711. );
  712. $this->addOptionalQueryParam(
  713. $queryParams,
  714. Resources::QP_TIMEOUT,
  715. $timeout
  716. );
  717. // One can specify the NextTableName option to get table entities starting
  718. // from the specified name. However, there appears to be an issue in the
  719. // Azure Table service where this does not engage on the server unless
  720. // $filter appears in the URL. The current behavior is to just ignore the
  721. // NextTableName options, which is not expected or easily detectable.
  722. if ( array_key_exists(Resources::QP_NEXT_TABLE_NAME, $queryParams)
  723. && !array_key_exists(Resources::QP_FILTER, $queryParams)
  724. ) {
  725. $queryParams[Resources::QP_FILTER] = Resources::EMPTY_STRING;
  726. }
  727. $response = $this->send(
  728. $method,
  729. $headers,
  730. $queryParams,
  731. $postParams,
  732. $path,
  733. $statusCode
  734. );
  735. $tables = $this->_atomSerializer->parseTableEntries($response->getBody());
  736. return QueryTablesResult::create($response->getHeader(), $tables);
  737. }
  738. /**
  739. * Creates new table in the storage account
  740. *
  741. * @param string $table The name of the table.
  742. * @param Models\TableServiceOptions $options The optional parameters.
  743. *
  744. * @return none
  745. *
  746. * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd135729.aspx
  747. */
  748. public function createTable($table, $options = null)
  749. {
  750. Validate::isString($table, 'table');
  751. Validate::notNullOrEmpty($table, 'table');
  752. $method = Resources::HTTP_POST;
  753. $headers = array();
  754. $postParams = array();
  755. $queryParams = array();
  756. $statusCode = Resources::STATUS_CREATED;
  757. $path = 'Tables';
  758. $body = $this->_atomSerializer->getTable($table);
  759. if (is_null($options)) {
  760. $options = new TableServiceOptions();
  761. }
  762. $this->addOptionalHeader(
  763. $headers,
  764. Resources::CONTENT_TYPE,
  765. Resources::XML_ATOM_CONTENT_TYPE
  766. );
  767. $this->addOptionalQueryParam(
  768. $queryParams,
  769. Resources::QP_TIMEOUT,
  770. $options->getTimeout()
  771. );
  772. $this->send(
  773. $method,
  774. $headers,
  775. $queryParams,
  776. $postParams,
  777. $path,
  778. $statusCode,
  779. $body
  780. );
  781. }
  782. /**
  783. * Gets the table.
  784. *
  785. * @param string $table The name of the table.
  786. * @param Models\TableServiceOptions $options The optional parameters.
  787. *
  788. * @return Models\GetTableResult
  789. */
  790. public function getTable($table, $options = null)
  791. {
  792. Validate::isString($table, 'table');
  793. Validate::notNullOrEmpty($table, 'table');
  794. $method = Resources::HTTP_GET;
  795. $headers = array();
  796. $postParams = array();
  797. $queryParams = array();
  798. $statusCode = Resources::STATUS_OK;
  799. $path = "Tables('$table')";
  800. if (is_null($options)) {
  801. $options = new TableServiceOptions();
  802. }
  803. $this->addOptionalHeader(
  804. $headers,
  805. Resources::CONTENT_TYPE,
  806. Resources::XML_ATOM_CONTENT_TYPE
  807. );
  808. $this->addOptionalQueryParam(
  809. $queryParams,
  810. Resources::QP_TIMEOUT,
  811. $options->getTimeout()
  812. );
  813. $response = $this->send(
  814. $method,
  815. $headers,
  816. $queryParams,
  817. $postParams,
  818. $path,
  819. $statusCode
  820. );
  821. return GetTableResult::create($response->getBody(), $this->_atomSerializer);
  822. }
  823. /**
  824. * Deletes the specified table and any data it contains.
  825. *
  826. * @param string $table The name of the table.
  827. * @param Models\TableServiceOptions $options optional parameters
  828. *
  829. * @return none
  830. *
  831. * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179387.aspx
  832. */
  833. public function deleteTable($table, $options = null)
  834. {
  835. Validate::isString($table, 'table');
  836. Validate::notNullOrEmpty($table, 'table');
  837. $method = Resources::HTTP_DELETE;
  838. $headers = array();
  839. $postParams = array();
  840. $queryParams = array();
  841. $statusCode = Resources::STATUS_NO_CONTENT;
  842. $path = "Tables('$table')";
  843. if (is_null($options)) {
  844. $options = new TableServiceOptions();
  845. }
  846. $this->addOptionalQueryParam(
  847. $queryParams,
  848. Resources::QP_TIMEOUT,
  849. $options->getTimeout()
  850. );
  851. $this->send(
  852. $method,
  853. $headers,
  854. $queryParams,
  855. $postParams,
  856. $path,
  857. $statusCode
  858. );
  859. }
  860. /**
  861. * Quries entities for the given table name
  862. *
  863. * @param string $table The name of
  864. * the table.
  865. * @param Models\QueryEntitiesOptions|string|Models\Filter $options Coule be
  866. * optional parameters, query string or filter to apply.
  867. *
  868. * @return Models\QueryEntitiesResult
  869. *
  870. * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179421.aspx
  871. */
  872. public function queryEntities($table, $options = null)
  873. {
  874. Validate::isString($table, 'table');
  875. Validate::notNullOrEmpty($table, 'table');
  876. $method = Resources::HTTP_GET;
  877. $headers = array();
  878. $postParams = array();
  879. $queryParams = array();
  880. $statusCode = Resources::STATUS_OK;
  881. $path = $table;
  882. if (is_null($options)) {
  883. $options = new QueryEntitiesOptions();
  884. } else if (is_string($options)) {
  885. $queryString = $options;
  886. $options = new QueryEntitiesOptions();
  887. $options->setFilter(Filter::applyQueryString($queryString));
  888. } else if ($options instanceof Filter) {
  889. $filter = $options;
  890. $options = new QueryEntitiesOptions();
  891. $options->setFilter($filter);
  892. }
  893. $encodedPK = $this->_encodeODataUriValue($options->getNextPartitionKey());
  894. $encodedRK = $this->_encodeODataUriValue($options->getNextRowKey());
  895. $queryParams = $this->_addOptionalQuery($queryParams, $options->getQuery());
  896. $this->addOptionalQueryParam(
  897. $queryParams,
  898. Resources::QP_TIMEOUT,
  899. $options->getTimeout()
  900. );
  901. $this->addOptionalQueryParam(
  902. $queryParams,
  903. Resources::QP_NEXT_PK,
  904. $encodedPK
  905. );
  906. $this->addOptionalQueryParam(
  907. $queryParams,
  908. Resources::QP_NEXT_RK,
  909. $encodedRK
  910. );
  911. $this->addOptionalHeader(
  912. $headers,
  913. Resources::CONTENT_TYPE,
  914. Resources::XML_ATOM_CONTENT_TYPE
  915. );
  916. if (!is_null($options->getQuery())) {
  917. $dsHeader = Resources::DATA_SERVICE_VERSION;
  918. $maxdsValue = Resources::MAX_DATA_SERVICE_VERSION_VALUE;
  919. $fields = $options->getQuery()->getSelectFields();
  920. $hasSelect = !empty($fields);
  921. if ($hasSelect) {
  922. $this->addOptionalHeader($headers, $dsHeader, $maxdsValue);
  923. }
  924. }
  925. $response = $this->send(
  926. $method,
  927. $headers,
  928. $queryParams,
  929. $postParams,
  930. $path,
  931. $statusCode
  932. );
  933. $entities = $this->_atomSerializer->parseEntities($response->getBody());
  934. return QueryEntitiesResult::create($response->getHeader(), $entities);
  935. }
  936. /**
  937. * Inserts new entity to the table.
  938. *
  939. * @param string $table name of the table.
  940. * @param Models\Entity $entity table entity.
  941. * @param Models\TableServiceOptions $options optional parameters.
  942. *
  943. * @return Models\InsertEntityResult
  944. *
  945. * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179433.aspx
  946. */
  947. public function insertEntity($table, $entity, $options = null)
  948. {
  949. $context = $this->_constructInsertEntityContext($table, $entity, $options);
  950. $response = $this->sendContext($context);
  951. $body = $response->getBody();
  952. $headers = $response->getHeader();
  953. return InsertEntityResult::create($body, $headers, $this->_atomSerializer);
  954. }
  955. /**
  956. * Updates an existing entity or inserts a new entity if it does not exist in the
  957. * table.
  958. *
  959. * @param string $table name of the table
  960. * @param Models\Entity $entity table entity
  961. * @param Models\TableServiceOptions $options optional parameters
  962. *
  963. * @return Models\UpdateEntityResult
  964. *
  965. * @see http://msdn.microsoft.com/en-us/library/windowsazure/hh452241.aspx
  966. */
  967. public function insertOrMergeEntity($table, $entity, $options = null)
  968. {
  969. return $this->_putOrMergeEntityImpl(
  970. $table,
  971. $entity,
  972. Resources::HTTP_MERGE,
  973. false,
  974. $options
  975. );
  976. }
  977. /**
  978. * Replaces an existing entity or inserts a new entity if it does not exist in
  979. * the table.
  980. *
  981. * @param string $table name of the table
  982. * @param Models\Entity $entity table entity
  983. * @param Models\TableServiceOptions $options optional parameters
  984. *
  985. * @return Models\UpdateEntityResult
  986. *
  987. * @see http://msdn.microsoft.com/en-us/library/windowsazure/hh452242.aspx
  988. */
  989. public function insertOrReplaceEntity($table, $entity, $options = null)
  990. {
  991. return $this->_putOrMergeEntityImpl(
  992. $table,
  993. $entity,
  994. Resources::HTTP_PUT,
  995. false,
  996. $options
  997. );
  998. }
  999. /**
  1000. * Updates an existing entity in a table. The Update Entity operation replaces
  1001. * the entire entity and can be used to remove properties.
  1002. *
  1003. * @param string $table The table name.
  1004. * @param Models\Entity $entity The table entity.
  1005. * @param Models\TableServiceOptions $options The optional parameters.
  1006. *
  1007. * @return Models\UpdateEntityResult
  1008. *
  1009. * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179427.aspx
  1010. */
  1011. public function updateEntity($table, $entity, $options = null)
  1012. {
  1013. return $this->_putOrMergeEntityImpl(
  1014. $table,
  1015. $entity,
  1016. Resources::HTTP_PUT,
  1017. true,
  1018. $options
  1019. );
  1020. }
  1021. /**
  1022. * Updates an existing entity by updating the entity's properties. This operation
  1023. * does not replace the existing entity, as the updateEntity operation does.
  1024. *
  1025. * @param string $table The table name.
  1026. * @param Models\Entity $entity The table entity.
  1027. * @param Models\TableServiceOptions $options The optional parameters.
  1028. *
  1029. * @return Models\UpdateEntityResult
  1030. *
  1031. * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179392.aspx
  1032. */
  1033. public function mergeEntity($table, $entity, $options = null)
  1034. {
  1035. return $this->_putOrMergeEntityImpl(
  1036. $table,
  1037. $entity,
  1038. Resources::HTTP_MERGE,
  1039. true,
  1040. $options
  1041. );
  1042. }
  1043. /**
  1044. * Deletes an existing entity in a table.
  1045. *
  1046. * @param string $table The name of the table.
  1047. * @param string $partitionKey The entity partition key.
  1048. * @param string $rowKey The entity row key.
  1049. * @param Models\DeleteEntityOptions $options The optional parameters.
  1050. *
  1051. * @return none
  1052. *
  1053. * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd135727.aspx
  1054. */
  1055. public function deleteEntity($table, $partitionKey, $rowKey, $options = null)
  1056. {
  1057. $context = $this->_constructDeleteEntityContext(
  1058. $table,
  1059. $partitionKey,
  1060. $rowKey,
  1061. $options
  1062. );
  1063. $this->sendContext($context);
  1064. }
  1065. /**
  1066. * Gets table entity.
  1067. *
  1068. * @param string $table The name of the table.
  1069. * @param string $partitionKey The entity partition key.
  1070. * @param string $rowKey The entity row key.
  1071. * @param Models\TableServiceOptions $options The optional parameters.
  1072. *
  1073. * @return Models\GetEntityResult
  1074. *
  1075. * @see http://msdn.microsoft.com/en-us/library/windowsazure/dd179421.aspx
  1076. */
  1077. public function getEntity($table, $partitionKey, $rowKey, $options = null)
  1078. {
  1079. Validate::isString($table, 'table');
  1080. Validate::notNullOrEmpty($table, 'table');
  1081. Validate::isTrue(!is_null($partitionKey), Resources::NULL_TABLE_KEY_MSG);
  1082. Validate::isTrue(!is_null($rowKey), Resources::NULL_TABLE_KEY_MSG);
  1083. $method = Resources::HTTP_GET;
  1084. $headers = array();
  1085. $queryParams = array();
  1086. $statusCode = Resources::STATUS_OK;
  1087. $path = $this->_getEntityPath($table, $partitionKey, $rowKey);
  1088. if (is_null($options)) {
  1089. $options = new TableServiceOptions();
  1090. }
  1091. $this->addOptionalHeader(
  1092. $headers,
  1093. Resources::CONTENT_TYPE,
  1094. Resources::XML_ATOM_CONTENT_TYPE
  1095. );
  1096. $this->addOptionalQueryParam(
  1097. $queryParams,
  1098. Resources::QP_TIMEOUT,
  1099. $options->getTimeout()
  1100. );
  1101. $context = new HttpCallContext();
  1102. $context->setHeaders($headers);
  1103. $context->setMethod($method);
  1104. $context->setPath($path);
  1105. $context->setQueryParameters($queryParams);
  1106. $context->addStatusCode($statusCode);
  1107. $response = $this->sendContext($context);
  1108. $entity = $this->_atomSerializer->parseEntity($response->getBody());
  1109. $result = new GetEntityResult();
  1110. $result->setEntity($entity);
  1111. return $result;
  1112. }
  1113. /**
  1114. * Does batch of operations on the table service.
  1115. *
  1116. * @param Models\BatchOperations $batchOperations The operations to apply.
  1117. * @param Models\TableServiceOptions $options The optional parameters.
  1118. *
  1119. * @return Models\BatchResult
  1120. */
  1121. public function batch($batchOperations, $options = null)
  1122. {
  1123. Validate::notNullOrEmpty($batchOperations, 'batchOperations');
  1124. $method = Resources::HTTP_POST;
  1125. $operations = $batchOperations->getOperations();
  1126. $contexts = $this->_createOperationsContexts($operations);
  1127. $mime = $this->_createBatchRequestBody($operations, $contexts);
  1128. $body = $mime['body'];
  1129. $headers = $mime['headers'];
  1130. $postParams = array();
  1131. $queryParams = array();
  1132. $statusCode = Resources::STATUS_ACCEPTED;
  1133. $path = '$batch';
  1134. if (is_null($options)) {
  1135. $options = new TableServiceOptions();
  1136. }
  1137. $this->addOptionalQueryParam(
  1138. $queryParams,
  1139. Resources::QP_TIMEOUT,
  1140. $options->getTimeout()
  1141. );
  1142. $response = $this->send(
  1143. $method,
  1144. $headers,
  1145. $queryParams,
  1146. $postParams,
  1147. $path,
  1148. $statusCode,
  1149. $body
  1150. );
  1151. return BatchResult::create(
  1152. $response->getBody(),
  1153. $operations,
  1154. $contexts,
  1155. $this->_atomSerializer,
  1156. $this->_mimeSerializer
  1157. );
  1158. }
  1159. }
  1160. ?>