PageRenderTime 33ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Service/Amazon/Sqs.php

https://bitbucket.org/nosen/jelly2
PHP | 465 lines | 219 code | 64 blank | 182 comment | 36 complexity | 0226ebf3ea7bcce32fefb50bea612235 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
  17. * @subpackage Amazon_Sqs
  18. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Sqs.php 23953 2011-05-03 05:47:39Z ralph $
  21. */
  22. /**
  23. * @see Zend_Service_Amazon_Abstract
  24. */
  25. require_once 'Zend/Service/Amazon/Abstract.php';
  26. /**
  27. * @see Zend_Crypt_Hmac
  28. */
  29. require_once 'Zend/Crypt/Hmac.php';
  30. /**
  31. * Class for connecting to the Amazon Simple Queue Service (SQS)
  32. *
  33. * @category Zend
  34. * @package Zend_Service
  35. * @subpackage Amazon_Sqs
  36. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  37. * @license http://framework.zend.com/license/new-bsd New BSD License
  38. * @see http://aws.amazon.com/sqs/ Amazon Simple Queue Service
  39. */
  40. class Zend_Service_Amazon_Sqs extends Zend_Service_Amazon_Abstract
  41. {
  42. /**
  43. * Default timeout for createQueue() function
  44. */
  45. const CREATE_TIMEOUT_DEFAULT = 30;
  46. /**
  47. * HTTP end point for the Amazon SQS service
  48. */
  49. protected $_sqsEndpoint = 'queue.amazonaws.com';
  50. /**
  51. * The API version to use
  52. */
  53. protected $_sqsApiVersion = '2009-02-01';
  54. /**
  55. * Signature Version
  56. */
  57. protected $_sqsSignatureVersion = '2';
  58. /**
  59. * Signature Encoding Method
  60. */
  61. protected $_sqsSignatureMethod = 'HmacSHA256';
  62. /**
  63. * Constructor
  64. *
  65. * @param string $accessKey
  66. * @param string $secretKey
  67. * @param string $region
  68. */
  69. public function __construct($accessKey = null, $secretKey = null, $region = null)
  70. {
  71. parent::__construct($accessKey, $secretKey, $region);
  72. }
  73. /**
  74. * Create a new queue
  75. *
  76. * Visibility timeout is how long a message is left in the queue "invisible"
  77. * to other readers. If the message is acknowleged (deleted) before the
  78. * timeout, then the message is deleted. However, if the timeout expires
  79. * then the message will be made available to other queue readers.
  80. *
  81. * @param string $queue_name queue name
  82. * @param integer $timeout default visibility timeout
  83. * @return string|boolean
  84. * @throws Zend_Service_Amazon_Sqs_Exception
  85. */
  86. public function create($queue_name, $timeout = null)
  87. {
  88. $params = array();
  89. $params['QueueName'] = $queue_name;
  90. $timeout = ($timeout === null) ? self::CREATE_TIMEOUT_DEFAULT : (int)$timeout;
  91. $params['DefaultVisibilityTimeout'] = $timeout;
  92. $retry_count = 0;
  93. do {
  94. $retry = false;
  95. $result = $this->_makeRequest(null, 'CreateQueue', $params);
  96. if (!isset($result->CreateQueueResult->QueueUrl)
  97. || empty($result->CreateQueueResult->QueueUrl)
  98. ) {
  99. if ($result->Error->Code == 'AWS.SimpleQueueService.QueueNameExists') {
  100. return false;
  101. } elseif ($result->Error->Code == 'AWS.SimpleQueueService.QueueDeletedRecently') {
  102. // Must sleep for 60 seconds, then try re-creating the queue
  103. sleep(60);
  104. $retry = true;
  105. $retry_count++;
  106. } else {
  107. require_once 'Zend/Service/Amazon/Sqs/Exception.php';
  108. throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
  109. }
  110. } else {
  111. return (string) $result->CreateQueueResult->QueueUrl;
  112. }
  113. } while ($retry);
  114. return false;
  115. }
  116. /**
  117. * Delete a queue and all of it's messages
  118. *
  119. * Returns false if the queue is not found, true if the queue exists
  120. *
  121. * @param string $queue_url queue URL
  122. * @return boolean
  123. * @throws Zend_Service_Amazon_Sqs_Exception
  124. */
  125. public function delete($queue_url)
  126. {
  127. $result = $this->_makeRequest($queue_url, 'DeleteQueue');
  128. if ($result->Error->Code !== null) {
  129. require_once 'Zend/Service/Amazon/Sqs/Exception.php';
  130. throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
  131. }
  132. return true;
  133. }
  134. /**
  135. * Get an array of all available queues
  136. *
  137. * @return array
  138. * @throws Zend_Service_Amazon_Sqs_Exception
  139. */
  140. public function getQueues()
  141. {
  142. $result = $this->_makeRequest(null, 'ListQueues');
  143. if (isset($result->Error)) {
  144. require_once 'Zend/Service/Amazon/Sqs/Exception.php';
  145. throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
  146. }
  147. if (!isset($result->ListQueuesResult->QueueUrl)
  148. || empty($result->ListQueuesResult->QueueUrl)
  149. ) {
  150. return array();
  151. }
  152. $queues = array();
  153. foreach ($result->ListQueuesResult->QueueUrl as $queue_url) {
  154. $queues[] = (string)$queue_url;
  155. }
  156. return $queues;
  157. }
  158. /**
  159. * Return the approximate number of messages in the queue
  160. *
  161. * @param string $queue_url Queue URL
  162. * @return integer
  163. * @throws Zend_Service_Amazon_Sqs_Exception
  164. */
  165. public function count($queue_url)
  166. {
  167. return (int)$this->getAttribute($queue_url, 'ApproximateNumberOfMessages');
  168. }
  169. /**
  170. * Send a message to the queue
  171. *
  172. * @param string $queue_url Queue URL
  173. * @param string $message Message to send to the queue
  174. * @return string Message ID
  175. * @throws Zend_Service_Amazon_Sqs_Exception
  176. */
  177. public function send($queue_url, $message)
  178. {
  179. $params = array();
  180. $params['MessageBody'] = urlencode($message);
  181. $checksum = md5($params['MessageBody']);
  182. $result = $this->_makeRequest($queue_url, 'SendMessage', $params);
  183. if (!isset($result->SendMessageResult->MessageId)
  184. || empty($result->SendMessageResult->MessageId)
  185. ) {
  186. require_once 'Zend/Service/Amazon/Sqs/Exception.php';
  187. throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
  188. } else if ((string) $result->SendMessageResult->MD5OfMessageBody != $checksum) {
  189. require_once 'Zend/Service/Amazon/Sqs/Exception.php';
  190. throw new Zend_Service_Amazon_Sqs_Exception('MD5 of body does not match message sent');
  191. }
  192. return (string) $result->SendMessageResult->MessageId;
  193. }
  194. /**
  195. * Get messages in the queue
  196. *
  197. * @param string $queue_url Queue name
  198. * @param integer $max_messages Maximum number of messages to return
  199. * @param integer $timeout Visibility timeout for these messages
  200. * @return array
  201. * @throws Zend_Service_Amazon_Sqs_Exception
  202. */
  203. public function receive($queue_url, $max_messages = null, $timeout = null)
  204. {
  205. $params = array();
  206. // If not set, the visibility timeout on the queue is used
  207. if ($timeout !== null) {
  208. $params['VisibilityTimeout'] = (int)$timeout;
  209. }
  210. // SQS will default to only returning one message
  211. if ($max_messages !== null) {
  212. $params['MaxNumberOfMessages'] = (int)$max_messages;
  213. }
  214. $result = $this->_makeRequest($queue_url, 'ReceiveMessage', $params);
  215. if (isset($result->Error)) {
  216. require_once 'Zend/Service/Amazon/Sqs/Exception.php';
  217. throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
  218. }
  219. if (!isset($result->ReceiveMessageResult->Message)
  220. || empty($result->ReceiveMessageResult->Message)
  221. ) {
  222. // no messages found
  223. return array();
  224. }
  225. $data = array();
  226. foreach ($result->ReceiveMessageResult->Message as $message) {
  227. $data[] = array(
  228. 'message_id' => (string)$message->MessageId,
  229. 'handle' => (string)$message->ReceiptHandle,
  230. 'md5' => (string)$message->MD5OfBody,
  231. 'body' => urldecode((string)$message->Body),
  232. );
  233. }
  234. return $data;
  235. }
  236. /**
  237. * Delete a message from the queue
  238. *
  239. * Returns true if the message is deleted, false if the deletion is
  240. * unsuccessful.
  241. *
  242. * @param string $queue_url Queue URL
  243. * @param string $handle Message handle as returned by SQS
  244. * @return boolean
  245. * @throws Zend_Service_Amazon_Sqs_Exception
  246. */
  247. public function deleteMessage($queue_url, $handle)
  248. {
  249. $params = array();
  250. $params['ReceiptHandle'] = (string)$handle;
  251. $result = $this->_makeRequest($queue_url, 'DeleteMessage', $params);
  252. if (isset($result->Error->Code)
  253. && !empty($result->Error->Code)
  254. ) {
  255. return false;
  256. }
  257. // Will always return true unless ReceiptHandle is malformed
  258. return true;
  259. }
  260. /**
  261. * Get the attributes for the queue
  262. *
  263. * @param string $queue_url Queue URL
  264. * @param string $attribute
  265. * @return string
  266. * @throws Zend_Service_Amazon_Sqs_Exception
  267. */
  268. public function getAttribute($queue_url, $attribute = 'All')
  269. {
  270. $params = array();
  271. $params['AttributeName'] = $attribute;
  272. $result = $this->_makeRequest($queue_url, 'GetQueueAttributes', $params);
  273. if (!isset($result->GetQueueAttributesResult->Attribute)
  274. || empty($result->GetQueueAttributesResult->Attribute)
  275. ) {
  276. require_once 'Zend/Service/Amazon/Sqs/Exception.php';
  277. throw new Zend_Service_Amazon_Sqs_Exception($result->Error->Code);
  278. }
  279. if(count($result->GetQueueAttributesResult->Attribute) > 1) {
  280. $attr_result = array();
  281. foreach($result->GetQueueAttributesResult->Attribute as $attribute) {
  282. $attr_result[(string)$attribute->Name] = (string)$attribute->Value;
  283. }
  284. return $attr_result;
  285. } else {
  286. return (string) $result->GetQueueAttributesResult->Attribute->Value;
  287. }
  288. }
  289. /**
  290. * Make a request to Amazon SQS
  291. *
  292. * @param string $queue Queue Name
  293. * @param string $action SQS action
  294. * @param array $params
  295. * @return SimpleXMLElement
  296. */
  297. private function _makeRequest($queue_url, $action, $params = array())
  298. {
  299. $params['Action'] = $action;
  300. $params = $this->addRequiredParameters($queue_url, $params);
  301. if ($queue_url === null) {
  302. $queue_url = '/';
  303. }
  304. $client = self::getHttpClient();
  305. switch ($action) {
  306. case 'ListQueues':
  307. case 'CreateQueue':
  308. $client->setUri('http://'.$this->_sqsEndpoint);
  309. break;
  310. default:
  311. $client->setUri($queue_url);
  312. break;
  313. }
  314. $retry_count = 0;
  315. do {
  316. $retry = false;
  317. $client->resetParameters();
  318. $client->setParameterGet($params);
  319. $response = $client->request('GET');
  320. $response_code = $response->getStatus();
  321. // Some 5xx errors are expected, so retry automatically
  322. if ($response_code >= 500 && $response_code < 600 && $retry_count <= 5) {
  323. $retry = true;
  324. $retry_count++;
  325. sleep($retry_count / 4 * $retry_count);
  326. }
  327. } while ($retry);
  328. unset($client);
  329. return new SimpleXMLElement($response->getBody());
  330. }
  331. /**
  332. * Adds required authentication and version parameters to an array of
  333. * parameters
  334. *
  335. * The required parameters are:
  336. * - AWSAccessKey
  337. * - SignatureVersion
  338. * - Timestamp
  339. * - Version and
  340. * - Signature
  341. *
  342. * If a required parameter is already set in the <tt>$parameters</tt> array,
  343. * it is overwritten.
  344. *
  345. * @param string $queue_url Queue URL
  346. * @param array $parameters the array to which to add the required
  347. * parameters.
  348. * @return array
  349. */
  350. protected function addRequiredParameters($queue_url, array $parameters)
  351. {
  352. $parameters['AWSAccessKeyId'] = $this->_getAccessKey();
  353. $parameters['SignatureVersion'] = $this->_sqsSignatureVersion;
  354. $parameters['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z', time()+10);
  355. $parameters['Version'] = $this->_sqsApiVersion;
  356. $parameters['SignatureMethod'] = $this->_sqsSignatureMethod;
  357. $parameters['Signature'] = $this->_signParameters($queue_url, $parameters);
  358. return $parameters;
  359. }
  360. /**
  361. * Computes the RFC 2104-compliant HMAC signature for request parameters
  362. *
  363. * This implements the Amazon Web Services signature, as per the following
  364. * specification:
  365. *
  366. * 1. Sort all request parameters (including <tt>SignatureVersion</tt> and
  367. * excluding <tt>Signature</tt>, the value of which is being created),
  368. * ignoring case.
  369. *
  370. * 2. Iterate over the sorted list and append the parameter name (in its
  371. * original case) and then its value. Do not URL-encode the parameter
  372. * values before constructing this string. Do not use any separator
  373. * characters when appending strings.
  374. *
  375. * @param string $queue_url Queue URL
  376. * @param array $parameters the parameters for which to get the signature.
  377. *
  378. * @return string the signed data.
  379. */
  380. protected function _signParameters($queue_url, array $paramaters)
  381. {
  382. $data = "GET\n";
  383. $data .= $this->_sqsEndpoint . "\n";
  384. if ($queue_url !== null) {
  385. $data .= parse_url($queue_url, PHP_URL_PATH);
  386. }
  387. else {
  388. $data .= '/';
  389. }
  390. $data .= "\n";
  391. uksort($paramaters, 'strcmp');
  392. unset($paramaters['Signature']);
  393. $arrData = array();
  394. foreach($paramaters as $key => $value) {
  395. $arrData[] = $key . '=' . str_replace('%7E', '~', urlencode($value));
  396. }
  397. $data .= implode('&', $arrData);
  398. $hmac = Zend_Crypt_Hmac::compute($this->_getSecretKey(), 'SHA256', $data, Zend_Crypt_Hmac::BINARY);
  399. return base64_encode($hmac);
  400. }
  401. }