PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Service/WindowsAzure/Storage/Queue.php

http://rewardvn.googlecode.com/
PHP | 555 lines | 306 code | 57 blank | 192 comment | 85 complexity | e6f7ff9a8feab388d3a30235e709488e MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Service_WindowsAzure
  17. * @subpackage Storage
  18. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://todo name_todo
  20. * @version $Id$
  21. */
  22. /**
  23. * @see Zend_Service_WindowsAzure_Credentials_SharedKey
  24. */
  25. require_once 'Zend/Service/WindowsAzure/Credentials/SharedKey.php';
  26. /**
  27. * @see Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract
  28. */
  29. require_once 'Zend/Service/WindowsAzure/RetryPolicy/RetryPolicyAbstract.php';
  30. /**
  31. * @see Zend_Http_Client
  32. */
  33. require_once 'Zend/Http/Client.php';
  34. /**
  35. * @see Zend_Http_Response
  36. */
  37. require_once 'Zend/Http/Response.php';
  38. /**
  39. * @see Zend_Service_WindowsAzure_Storage
  40. */
  41. require_once 'Zend/Service/WindowsAzure/Storage.php';
  42. /**
  43. * Zend_Service_WindowsAzure_Storage_QueueInstance
  44. */
  45. require_once 'Zend/Service/WindowsAzure/Storage/QueueInstance.php';
  46. /**
  47. * Zend_Service_WindowsAzure_Storage_QueueMessage
  48. */
  49. require_once 'Zend/Service/WindowsAzure/Storage/QueueMessage.php';
  50. /**
  51. * @see Zend_Service_WindowsAzure_Exception
  52. */
  53. require_once 'Zend/Service/WindowsAzure/Exception.php';
  54. /**
  55. * @category Zend
  56. * @package Zend_Service_WindowsAzure
  57. * @subpackage Storage
  58. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  59. * @license http://framework.zend.com/license/new-bsd New BSD License
  60. */
  61. class Zend_Service_WindowsAzure_Storage_Queue extends Zend_Service_WindowsAzure_Storage
  62. {
  63. /**
  64. * Maximal message size (in bytes)
  65. */
  66. const MAX_MESSAGE_SIZE = 8388608;
  67. /**
  68. * Maximal message ttl (in seconds)
  69. */
  70. const MAX_MESSAGE_TTL = 604800;
  71. /**
  72. * Creates a new Zend_Service_WindowsAzure_Storage_Queue instance
  73. *
  74. * @param string $host Storage host name
  75. * @param string $accountName Account name for Windows Azure
  76. * @param string $accountKey Account key for Windows Azure
  77. * @param boolean $usePathStyleUri Use path-style URI's
  78. * @param Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy Retry policy to use when making requests
  79. */
  80. public function __construct($host = Zend_Service_WindowsAzure_Storage::URL_DEV_QUEUE, $accountName = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::DEVSTORE_ACCOUNT, $accountKey = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::DEVSTORE_KEY, $usePathStyleUri = false, Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy = null)
  81. {
  82. parent::__construct($host, $accountName, $accountKey, $usePathStyleUri, $retryPolicy);
  83. // API version
  84. $this->_apiVersion = '2009-09-19';
  85. }
  86. /**
  87. * Check if a queue exists
  88. *
  89. * @param string $queueName Queue name
  90. * @return boolean
  91. */
  92. public function queueExists($queueName = '')
  93. {
  94. if ($queueName === '') {
  95. throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
  96. }
  97. if (!self::isValidQueueName($queueName)) {
  98. throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
  99. }
  100. // List queues
  101. $queues = $this->listQueues($queueName, 1);
  102. foreach ($queues as $queue) {
  103. if ($queue->Name == $queueName) {
  104. return true;
  105. }
  106. }
  107. return false;
  108. }
  109. /**
  110. * Create queue
  111. *
  112. * @param string $queueName Queue name
  113. * @param array $metadata Key/value pairs of meta data
  114. * @return object Queue properties
  115. * @throws Zend_Service_WindowsAzure_Exception
  116. */
  117. public function createQueue($queueName = '', $metadata = array())
  118. {
  119. if ($queueName === '') {
  120. throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
  121. }
  122. if (!self::isValidQueueName($queueName)) {
  123. throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
  124. }
  125. // Create metadata headers
  126. $headers = array();
  127. $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
  128. // Perform request
  129. $response = $this->_performRequest($queueName, '', Zend_Http_Client::PUT, $headers);
  130. if ($response->isSuccessful()) {
  131. return new Zend_Service_WindowsAzure_Storage_QueueInstance(
  132. $queueName,
  133. $metadata
  134. );
  135. } else {
  136. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  137. }
  138. }
  139. /**
  140. * Get queue
  141. *
  142. * @param string $queueName Queue name
  143. * @return Zend_Service_WindowsAzure_Storage_QueueInstance
  144. * @throws Zend_Service_WindowsAzure_Exception
  145. */
  146. public function getQueue($queueName = '')
  147. {
  148. if ($queueName === '') {
  149. throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
  150. }
  151. if (!self::isValidQueueName($queueName)) {
  152. throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
  153. }
  154. // Perform request
  155. $response = $this->_performRequest($queueName, '?comp=metadata', Zend_Http_Client::GET);
  156. if ($response->isSuccessful()) {
  157. // Parse metadata
  158. $metadata = $this->_parseMetadataHeaders($response->getHeaders());
  159. // Return queue
  160. $queue = new Zend_Service_WindowsAzure_Storage_QueueInstance(
  161. $queueName,
  162. $metadata
  163. );
  164. $queue->ApproximateMessageCount = intval($response->getHeader('x-ms-approximate-message-count'));
  165. return $queue;
  166. } else {
  167. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  168. }
  169. }
  170. /**
  171. * Get queue metadata
  172. *
  173. * @param string $queueName Queue name
  174. * @return array Key/value pairs of meta data
  175. * @throws Zend_Service_WindowsAzure_Exception
  176. */
  177. public function getQueueMetadata($queueName = '')
  178. {
  179. if ($queueName === '') {
  180. throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
  181. }
  182. if (!self::isValidQueueName($queueName)) {
  183. throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
  184. }
  185. return $this->getQueue($queueName)->Metadata;
  186. }
  187. /**
  188. * Set queue metadata
  189. *
  190. * Calling the Set Queue Metadata operation overwrites all existing metadata that is associated with the queue. It's not possible to modify an individual name/value pair.
  191. *
  192. * @param string $queueName Queue name
  193. * @param array $metadata Key/value pairs of meta data
  194. * @throws Zend_Service_WindowsAzure_Exception
  195. */
  196. public function setQueueMetadata($queueName = '', $metadata = array())
  197. {
  198. if ($queueName === '') {
  199. throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
  200. }
  201. if (!self::isValidQueueName($queueName)) {
  202. throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
  203. }
  204. if (count($metadata) == 0) {
  205. return;
  206. }
  207. // Create metadata headers
  208. $headers = array();
  209. $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
  210. // Perform request
  211. $response = $this->_performRequest($queueName, '?comp=metadata', Zend_Http_Client::PUT, $headers);
  212. if (!$response->isSuccessful()) {
  213. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  214. }
  215. }
  216. /**
  217. * Delete queue
  218. *
  219. * @param string $queueName Queue name
  220. * @throws Zend_Service_WindowsAzure_Exception
  221. */
  222. public function deleteQueue($queueName = '')
  223. {
  224. if ($queueName === '') {
  225. throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
  226. }
  227. if (!self::isValidQueueName($queueName)) {
  228. throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
  229. }
  230. // Perform request
  231. $response = $this->_performRequest($queueName, '', Zend_Http_Client::DELETE);
  232. if (!$response->isSuccessful()) {
  233. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  234. }
  235. }
  236. /**
  237. * List queues
  238. *
  239. * @param string $prefix Optional. Filters the results to return only queues whose name begins with the specified prefix.
  240. * @param int $maxResults Optional. Specifies the maximum number of queues to return per call to Azure storage. This does NOT affect list size returned by this function. (maximum: 5000)
  241. * @param string $marker Optional string value that identifies the portion of the list to be returned with the next list operation.
  242. * @param string $include Optional. Include this parameter to specify that the queue's metadata be returned as part of the response body. (allowed values: '', 'metadata')
  243. * @param int $currentResultCount Current result count (internal use)
  244. * @return array
  245. * @throws Zend_Service_WindowsAzure_Exception
  246. */
  247. public function listQueues($prefix = null, $maxResults = null, $marker = null, $include = null, $currentResultCount = 0)
  248. {
  249. // Build query string
  250. $queryString = array('comp=list');
  251. if (!is_null($prefix)) {
  252. $queryString[] = 'prefix=' . $prefix;
  253. }
  254. if (!is_null($maxResults)) {
  255. $queryString[] = 'maxresults=' . $maxResults;
  256. }
  257. if (!is_null($marker)) {
  258. $queryString[] = 'marker=' . $marker;
  259. }
  260. if (!is_null($include)) {
  261. $queryString[] = 'include=' . $include;
  262. }
  263. $queryString = self::createQueryStringFromArray($queryString);
  264. // Perform request
  265. $response = $this->_performRequest('', $queryString, Zend_Http_Client::GET);
  266. if ($response->isSuccessful()) {
  267. $xmlQueues = $this->_parseResponse($response)->Queues->Queue;
  268. $xmlMarker = (string)$this->_parseResponse($response)->NextMarker;
  269. $queues = array();
  270. if (!is_null($xmlQueues)) {
  271. for ($i = 0; $i < count($xmlQueues); $i++) {
  272. $queues[] = new Zend_Service_WindowsAzure_Storage_QueueInstance(
  273. (string)$xmlQueues[$i]->Name,
  274. $this->_parseMetadataElement($xmlQueues[$i])
  275. );
  276. }
  277. }
  278. $currentResultCount = $currentResultCount + count($queues);
  279. if (!is_null($maxResults) && $currentResultCount < $maxResults) {
  280. if (!is_null($xmlMarker) && $xmlMarker != '') {
  281. $queues = array_merge($queues, $this->listQueues($prefix, $maxResults, $xmlMarker, $include, $currentResultCount));
  282. }
  283. }
  284. if (!is_null($maxResults) && count($queues) > $maxResults) {
  285. $queues = array_slice($queues, 0, $maxResults);
  286. }
  287. return $queues;
  288. } else {
  289. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  290. }
  291. }
  292. /**
  293. * Put message into queue
  294. *
  295. * @param string $queueName Queue name
  296. * @param string $message Message
  297. * @param int $ttl Message Time-To-Live (in seconds). Defaults to 7 days if the parameter is omitted.
  298. * @throws Zend_Service_WindowsAzure_Exception
  299. */
  300. public function putMessage($queueName = '', $message = '', $ttl = null)
  301. {
  302. if ($queueName === '') {
  303. throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
  304. }
  305. if (!self::isValidQueueName($queueName)) {
  306. throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
  307. }
  308. if (strlen($message) > self::MAX_MESSAGE_SIZE) {
  309. throw new Zend_Service_WindowsAzure_Exception('Message is too big. Message content should be < 8KB.');
  310. }
  311. if ($message == '') {
  312. throw new Zend_Service_WindowsAzure_Exception('Message is not specified.');
  313. }
  314. if (!is_null($ttl) && ($ttl <= 0 || $ttl > self::MAX_MESSAGE_SIZE)) {
  315. throw new Zend_Service_WindowsAzure_Exception('Message TTL is invalid. Maximal TTL is 7 days (' . self::MAX_MESSAGE_SIZE . ' seconds) and should be greater than zero.');
  316. }
  317. // Build query string
  318. $queryString = array();
  319. if (!is_null($ttl)) {
  320. $queryString[] = 'messagettl=' . $ttl;
  321. }
  322. $queryString = self::createQueryStringFromArray($queryString);
  323. // Build body
  324. $rawData = '';
  325. $rawData .= '<QueueMessage>';
  326. $rawData .= ' <MessageText>' . base64_encode($message) . '</MessageText>';
  327. $rawData .= '</QueueMessage>';
  328. // Perform request
  329. $response = $this->_performRequest($queueName . '/messages', $queryString, Zend_Http_Client::POST, array(), false, $rawData);
  330. if (!$response->isSuccessful()) {
  331. throw new Zend_Service_WindowsAzure_Exception('Error putting message into queue.');
  332. }
  333. }
  334. /**
  335. * Get queue messages
  336. *
  337. * @param string $queueName Queue name
  338. * @param string $numOfMessages Optional. A nonzero integer value that specifies the number of messages to retrieve from the queue, up to a maximum of 32. By default, a single message is retrieved from the queue with this operation.
  339. * @param int $visibilityTimeout Optional. An integer value that specifies the message's visibility timeout in seconds. The maximum value is 2 hours. The default message visibility timeout is 30 seconds.
  340. * @param string $peek Peek only?
  341. * @return array
  342. * @throws Zend_Service_WindowsAzure_Exception
  343. */
  344. public function getMessages($queueName = '', $numOfMessages = 1, $visibilityTimeout = null, $peek = false)
  345. {
  346. if ($queueName === '') {
  347. throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
  348. }
  349. if (!self::isValidQueueName($queueName)) {
  350. throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
  351. }
  352. if ($numOfMessages < 1 || $numOfMessages > 32 || intval($numOfMessages) != $numOfMessages) {
  353. throw new Zend_Service_WindowsAzure_Exception('Invalid number of messages to retrieve.');
  354. }
  355. if (!is_null($visibilityTimeout) && ($visibilityTimeout <= 0 || $visibilityTimeout > 7200)) {
  356. throw new Zend_Service_WindowsAzure_Exception('Visibility timeout is invalid. Maximum value is 2 hours (7200 seconds) and should be greater than zero.');
  357. }
  358. // Build query string
  359. $queryString = array();
  360. if ($peek) {
  361. $queryString[] = 'peekonly=true';
  362. }
  363. if ($numOfMessages > 1) {
  364. $queryString[] = 'numofmessages=' . $numOfMessages;
  365. }
  366. if (!$peek && !is_null($visibilityTimeout)) {
  367. $queryString[] = 'visibilitytimeout=' . $visibilityTimeout;
  368. }
  369. $queryString = self::createQueryStringFromArray($queryString);
  370. // Perform request
  371. $response = $this->_performRequest($queueName . '/messages', $queryString, Zend_Http_Client::GET);
  372. if ($response->isSuccessful()) {
  373. // Parse results
  374. $result = $this->_parseResponse($response);
  375. if (!$result) {
  376. return array();
  377. }
  378. $xmlMessages = null;
  379. if (count($result->QueueMessage) > 1) {
  380. $xmlMessages = $result->QueueMessage;
  381. } else {
  382. $xmlMessages = array($result->QueueMessage);
  383. }
  384. $messages = array();
  385. for ($i = 0; $i < count($xmlMessages); $i++) {
  386. $messages[] = new Zend_Service_WindowsAzure_Storage_QueueMessage(
  387. (string)$xmlMessages[$i]->MessageId,
  388. (string)$xmlMessages[$i]->InsertionTime,
  389. (string)$xmlMessages[$i]->ExpirationTime,
  390. ($peek ? '' : (string)$xmlMessages[$i]->PopReceipt),
  391. ($peek ? '' : (string)$xmlMessages[$i]->TimeNextVisible),
  392. (string)$xmlMessages[$i]->DequeueCount,
  393. base64_decode((string)$xmlMessages[$i]->MessageText)
  394. );
  395. }
  396. return $messages;
  397. } else {
  398. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  399. }
  400. }
  401. /**
  402. * Peek queue messages
  403. *
  404. * @param string $queueName Queue name
  405. * @param string $numOfMessages Optional. A nonzero integer value that specifies the number of messages to retrieve from the queue, up to a maximum of 32. By default, a single message is retrieved from the queue with this operation.
  406. * @return array
  407. * @throws Zend_Service_WindowsAzure_Exception
  408. */
  409. public function peekMessages($queueName = '', $numOfMessages = 1)
  410. {
  411. return $this->getMessages($queueName, $numOfMessages, null, true);
  412. }
  413. /**
  414. * Clear queue messages
  415. *
  416. * @param string $queueName Queue name
  417. * @throws Zend_Service_WindowsAzure_Exception
  418. */
  419. public function clearMessages($queueName = '')
  420. {
  421. if ($queueName === '') {
  422. throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
  423. }
  424. if (!self::isValidQueueName($queueName)) {
  425. throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
  426. }
  427. // Perform request
  428. $response = $this->_performRequest($queueName . '/messages', '', Zend_Http_Client::DELETE);
  429. if (!$response->isSuccessful()) {
  430. throw new Zend_Service_WindowsAzure_Exception('Error clearing messages from queue.');
  431. }
  432. }
  433. /**
  434. * Delete queue message
  435. *
  436. * @param string $queueName Queue name
  437. * @param Zend_Service_WindowsAzure_Storage_QueueMessage $message Message to delete from queue. A message retrieved using "peekMessages" can NOT be deleted!
  438. * @throws Zend_Service_WindowsAzure_Exception
  439. */
  440. public function deleteMessage($queueName = '', Zend_Service_WindowsAzure_Storage_QueueMessage $message)
  441. {
  442. if ($queueName === '') {
  443. throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
  444. }
  445. if (!self::isValidQueueName($queueName)) {
  446. throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
  447. }
  448. if ($message->PopReceipt == '') {
  449. throw new Zend_Service_WindowsAzure_Exception('A message retrieved using "peekMessages" can NOT be deleted! Use "getMessages" instead.');
  450. }
  451. // Perform request
  452. $response = $this->_performRequest($queueName . '/messages/' . $message->MessageId, '?popreceipt=' . $message->PopReceipt, Zend_Http_Client::DELETE);
  453. if (!$response->isSuccessful()) {
  454. throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
  455. }
  456. }
  457. /**
  458. * Is valid queue name?
  459. *
  460. * @param string $queueName Queue name
  461. * @return boolean
  462. */
  463. public static function isValidQueueName($queueName = '')
  464. {
  465. if (preg_match("/^[a-z0-9][a-z0-9-]*$/", $queueName) === 0) {
  466. return false;
  467. }
  468. if (strpos($queueName, '--') !== false) {
  469. return false;
  470. }
  471. if (strtolower($queueName) != $queueName) {
  472. return false;
  473. }
  474. if (strlen($queueName) < 3 || strlen($queueName) > 63) {
  475. return false;
  476. }
  477. if (substr($queueName, -1) == '-') {
  478. return false;
  479. }
  480. return true;
  481. }
  482. /**
  483. * Get error message from Zend_Http_Response
  484. *
  485. * @param Zend_Http_Response $response Repsonse
  486. * @param string $alternativeError Alternative error message
  487. * @return string
  488. */
  489. protected function _getErrorMessage(Zend_Http_Response $response, $alternativeError = 'Unknown error.')
  490. {
  491. $response = $this->_parseResponse($response);
  492. if ($response && $response->Message) {
  493. return (string)$response->Message;
  494. } else {
  495. return $alternativeError;
  496. }
  497. }
  498. }