PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/codes-php/phpjakarta/WindowsAzure/Table/TableRestProxy.php

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