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

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

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