Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 464 lines (412 sloc) 14.497 kB
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
1 <?php
b5816c7 @thousandsofthem Timeout bug fix. Expires_in bug fix. Readme updated. Phpdoc banners.
thousandsofthem authored
2 /**
3 * PHP client for IronMQ
4 * IronMQ is a scalable, reliable, high performance message queue in the cloud.
5 *
6 * @link https://github.com/iron-io/iron_mq_php
7 * @link http://www.iron.io/products/mq
8 * @link http://docs.iron.io/
9 * @version 1.0
10 * @package IronMQPHP
11 * @copyright Feel free to copy, steal, take credit for, or whatever you feel like doing with this code. ;)
12 */
13
14 /**
15 * The Http_Exception class represents an HTTP response status that is not 200 OK.
16 */
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
17 class Http_Exception extends Exception{
18 const NOT_MODIFIED = 304;
19 const BAD_REQUEST = 400;
20 const NOT_FOUND = 404;
21 const NOT_ALOWED = 405;
22 const CONFLICT = 409;
23 const PRECONDITION_FAILED = 412;
24 const INTERNAL_ERROR = 500;
25 }
1c0e04b @paddyforan Better JSON error handling.
paddyforan authored
26
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
27 class IronMQ_Exception extends Exception{
28
29 }
30
b5816c7 @thousandsofthem Timeout bug fix. Expires_in bug fix. Readme updated. Phpdoc banners.
thousandsofthem authored
31 /**
32 * The JSON_Exception class represents an failures of decoding json strings.
33 */
1c0e04b @paddyforan Better JSON error handling.
paddyforan authored
34 class JSON_Exception extends Exception {
35 public $error = null;
36 public $error_code = JSON_ERROR_NONE;
37
38 function __construct($error_code) {
39 $this->error_code = $error_code;
40 switch($error_code) {
41 case JSON_ERROR_DEPTH:
42 $this->error = 'Maximum stack depth exceeded.';
43 break;
44 case JSON_ERROR_CTRL_CHAR:
45 $this->error = "Unexpected control characted found.";
46 break;
47 case JSON_ERROR_SYNTAX:
48 $this->error = "Syntax error, malformed JSON";
49 break;
50 }
51 parent::__construct();
52 }
53
54 function __toString() {
55 return $this->error;
56 }
57 }
58
59
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
60 class IronMQ_Message {
61 private $body;
62 private $timeout;
63 private $delay;
64 private $expires_in;
65
66 const max_expires_in = 2592000;
67
68 /**
b5816c7 @thousandsofthem Timeout bug fix. Expires_in bug fix. Readme updated. Phpdoc banners.
thousandsofthem authored
69 * Create a new message.
70 *
5190513 @paddyforan Updated message posting to use a better API.
paddyforan authored
71 * @param array|string $message
72 * An array of message properties or a string of the message body.
73 * Fields in message array:
74 * Required:
75 * - body: The message data, as a string.
76 * Optional:
77 * - timeout: Timeout, in seconds. After timeout, item will be placed back on queue. Defaults to 60.
78 * - delay: The item will not be available on the queue until this many seconds have passed. Defaults to 0.
79 * - expires_in: How long, in seconds, to keep the item on the queue before it is deleted. Defaults to 604800 (7 days). Maximum is 2592000 (30 days).
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
80 */
5190513 @paddyforan Updated message posting to use a better API.
paddyforan authored
81 function __construct($message) {
0bca771 @paddyforan Fixed bug with supplying just a message body.
paddyforan authored
82 if(is_string($message)) {
83 $this->setBody($message);
84 } elseif(is_array($message)) {
85 $this->setBody($message['body']);
86 if(array_key_exists("timeout", $message)) {
87 $this->setTimeout($message['timeout']);
88 }
89 if(array_key_exists("delay", $message)) {
90 $this->setDelay($message['delay']);
91 }
92 if(array_key_exists("expires_in", $message)) {
93 $this->setExpiresIn($message['expires_in']);
94 }
5190513 @paddyforan Updated message posting to use a better API.
paddyforan authored
95 }
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
96 }
97
98 public function getBody() {
99 return $this->body;
100 }
101
102 public function setBody($body) {
103 if(empty($body)) {
104 throw new InvalidArgumentException("Please specify a body");
105 } else {
106 $this->body = $body;
107 }
108 }
109
110 public function getTimeout() {
b5816c7 @thousandsofthem Timeout bug fix. Expires_in bug fix. Readme updated. Phpdoc banners.
thousandsofthem authored
111 if(!empty($this->timeout) || $this->timeout === 0) {# 0 is considered empty, but we want people to be able to set a timeout of 0
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
112 return $this->timeout;
113 } else {
114 return null;
115 }
116 }
117
118 public function setTimeout($timeout) {
119 $this->timeout = $timeout;
120 }
121
122 public function getDelay() {
123 if(!empty($this->delay) || $this->delay == 0) {# 0 is considered empty, but we want people to be able to set a delay of 0
124 return $this->delay;
125 } else {
126 return null;
127 }
128 }
129
130 public function setDelay($delay) {
131 $this->delay = $delay;
132 }
133
134 public function getExpiresIn() {
135 return $this->expires_in;
136 }
137
138 public function setExpiresIn($expires_in) {
b5816c7 @thousandsofthem Timeout bug fix. Expires_in bug fix. Readme updated. Phpdoc banners.
thousandsofthem authored
139 if($expires_in > self::max_expires_in) {
140 throw new InvalidArgumentException("Expires In can't be greater than ".self::max_expires_in.".");
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
141 } else {
142 $this->expires_in = $expires_in;
143 }
144 }
145
146 public function asArray() {
147 $array = array();
148 $array['body'] = $this->getBody();
149 if($this->getTimeout() != null) {
150 $array['timeout'] = $this->getTimeout();
151 }
152 if($this->getDelay() != null) {
153 $array['delay'] = $this->getDelay();
154 }
155 if($this->getExpiresIn() != null) {
156 $array['expires_in'] = $this->getExpiresIn();
157 }
158 return $array;
159 }
160 }
161
162 class IronMQ{
163
164 //Header Constants
165 const header_user_agent = "IronMQ PHP v0.1";
166 const header_accept = "application/json";
167 const header_accept_encoding = "gzip, deflate";
168 const HTTP_OK = 200;
169 const HTTP_CREATED = 201;
170 const HTTP_ACEPTED = 202;
171
172 const POST = 'POST';
173 const GET = 'GET';
174 const DELETE = 'DELETE';
175
176 public $debug_enabled = false;
177
a27ffd3 @thousandsofthem Revamp using $project_id.
thousandsofthem authored
178 private $required_config_fields = array('token','project_id');
0884aca @thousandsofthem All except token marked as optional. Code cleanup.
thousandsofthem authored
179 private $default_values = array(
180 'protocol' => 'http',
181 'host' => 'mq-aws-us-east-1.iron.io',
182 'port' => '80',
183 'api_version' => '1',
184 );
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
185
186 private $url;
187 private $token;
188 private $api_version;
189 private $version;
190 private $project_id;
191
192 /**
193 * @param string|array $config_file_or_options
194 * Array of options or name of config file.
195 * Fields in options array or in config:
0884aca @thousandsofthem All except token marked as optional. Code cleanup.
thousandsofthem authored
196 *
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
197 * Required:
198 * - token
0884aca @thousandsofthem All except token marked as optional. Code cleanup.
thousandsofthem authored
199 * - project_id
a27ffd3 @thousandsofthem Revamp using $project_id.
thousandsofthem authored
200 * Optional:
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
201 * - protocol
202 * - host
203 * - port
204 * - api_version
205 */
206 function __construct($config_file_or_options){
207 $config = $this->getConfigData($config_file_or_options);
208 $token = $config['token'];
a27ffd3 @thousandsofthem Revamp using $project_id.
thousandsofthem authored
209 $project_id = $config['project_id'];
0884aca @thousandsofthem All except token marked as optional. Code cleanup.
thousandsofthem authored
210
211 $protocol = empty($config['protocol']) ? $this->default_values['protocol'] : $config['protocol'];
212 $host = empty($config['host']) ? $this->default_values['host'] : $config['host'];
213 $port = empty($config['port']) ? $this->default_values['port'] : $config['port'];
214 $api_version = empty($config['api_version'])? $this->default_values['api_version'] : $config['api_version'];
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
215
216 $this->url = "$protocol://$host:$port/$api_version/";
217 $this->token = $token;
218 $this->api_version = $api_version;
219 $this->version = $api_version;
0884aca @thousandsofthem All except token marked as optional. Code cleanup.
thousandsofthem authored
220 $this->project_id = $project_id;
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
221 }
222
b5816c7 @thousandsofthem Timeout bug fix. Expires_in bug fix. Readme updated. Phpdoc banners.
thousandsofthem authored
223 /**
224 * Switch active project
225 *
226 * string @param $project_id Project ID
227 * @throws InvalidArgumentException
228 */
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
229 public function setProjectId($project_id) {
230 if (!empty($project_id)){
231 $this->project_id = $project_id;
232 }
233 if (empty($this->project_id)){
234 throw new InvalidArgumentException("Please set project_id");
235 }
236 }
237
a27ffd3 @thousandsofthem Revamp using $project_id.
thousandsofthem authored
238 public function getQueues($page = 0){
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
239 $url = "projects/{$this->project_id}/queues";
9d6b447 @paddyforan Stopped hard-coding the params into the URL and started passing them …
paddyforan authored
240 $params = array();
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
241 if($page > 0) {
9d6b447 @paddyforan Stopped hard-coding the params into the URL and started passing them …
paddyforan authored
242 $params['page'] = $page;
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
243 }
244 $this->setJsonHeaders();
0884aca @thousandsofthem All except token marked as optional. Code cleanup.
thousandsofthem authored
245 return self::json_decode($this->apiCall(self::GET, $url, $params));
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
246 }
247
b5816c7 @thousandsofthem Timeout bug fix. Expires_in bug fix. Readme updated. Phpdoc banners.
thousandsofthem authored
248 /**
249 * Get information about queue.
250 * Also returns queue size.
251 *
252 * @param string $queue_name
253 * @return mixed
254 */
a27ffd3 @thousandsofthem Revamp using $project_id.
thousandsofthem authored
255 public function getQueue($queue_name) {
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
256 $url = "projects/{$this->project_id}/queues/{$queue_name}";
257 $this->setJsonHeaders();
0884aca @thousandsofthem All except token marked as optional. Code cleanup.
thousandsofthem authored
258 return self::json_decode($this->apiCall(self::GET, $url));
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
259 }
260
9ee346a @thousandsofthem Readme updated.
thousandsofthem authored
261 /**
262 * Push a message on the queue
263 *
b5816c7 @thousandsofthem Timeout bug fix. Expires_in bug fix. Readme updated. Phpdoc banners.
thousandsofthem authored
264 * Examples:
265 * <code>
266 * $ironmq->postMessage("test_queue", "Hello world");
267 * </code>
268 * <code>
269 * $ironmq->postMessage("test_queue", array(
270 * "body" => "Test Message"
271 * "timeout" => 120,
272 * 'delay' => 2,
273 * 'expires_in' => 2*24*3600 # 2 days
274 * ));
275 * </code>
276 *
9ee346a @thousandsofthem Readme updated.
thousandsofthem authored
277 * @param string $queue_name Name of the queue.
278 * @param array|string $message
279 * @return mixed
280 */
a27ffd3 @thousandsofthem Revamp using $project_id.
thousandsofthem authored
281 public function postMessage($queue_name, $message) {
5190513 @paddyforan Updated message posting to use a better API.
paddyforan authored
282 $msg = new IronMQ_Message($message);
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
283 $req = array(
284 "messages" => array($msg->asArray())
285 );
286 $this->setCommonHeaders();
287 $url = "projects/{$this->project_id}/queues/{$queue_name}/messages";
288 $res = $this->apiCall(self::POST, $url, $req);
0884aca @thousandsofthem All except token marked as optional. Code cleanup.
thousandsofthem authored
289 return self::json_decode($res);
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
290 }
291
b5816c7 @thousandsofthem Timeout bug fix. Expires_in bug fix. Readme updated. Phpdoc banners.
thousandsofthem authored
292 /**
293 * Push multiple messages on the queue
294 *
295 * @param string $queue_name Name of the queue.
296 * @param array $messages array of messages, each message same as for postMessage() method
297 * @return mixed
298 */
a27ffd3 @thousandsofthem Revamp using $project_id.
thousandsofthem authored
299 public function postMessages($queue_name, $messages) {
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
300 $req = array(
301 "messages" => array()
302 );
303 foreach($messages as $message) {
5190513 @paddyforan Updated message posting to use a better API.
paddyforan authored
304 $msg = new IronMQ_Message($message);
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
305 array_push($req['messages'], $msg->asArray());
306 }
307 $this->setCommonHeaders();
308 $url = "projects/{$this->project_id}/queues/{$queue_name}/messages";
309 $res = $this->apiCall(self::POST, $url, $req);
0884aca @thousandsofthem All except token marked as optional. Code cleanup.
thousandsofthem authored
310 return self::json_decode($res);
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
311 }
312
a27ffd3 @thousandsofthem Revamp using $project_id.
thousandsofthem authored
313 public function getMessages($queue_name, $count=1) {
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
314 $url = "projects/{$this->project_id}/queues/{$queue_name}/messages";
9d6b447 @paddyforan Stopped hard-coding the params into the URL and started passing them …
paddyforan authored
315 $params = array();
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
316 if($count > 1) {
9d6b447 @paddyforan Stopped hard-coding the params into the URL and started passing them …
paddyforan authored
317 $params['count'] = $count;
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
318 }
319 $this->setJsonHeaders();
9d6b447 @paddyforan Stopped hard-coding the params into the URL and started passing them …
paddyforan authored
320 $response = $this->apiCall(self::GET, $url, $params);
ceea47f @paddyforan Updated getMessages to return null if there are no messages to return.
paddyforan authored
321 $result = self::json_decode($response);
322 if(count($result->messages) < 1) {
323 return null;
324 } else {
325 return $result;
326 }
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
327 }
328
a27ffd3 @thousandsofthem Revamp using $project_id.
thousandsofthem authored
329 public function getMessage($queue_name) {
330 return $this->getMessages($queue_name, 1);
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
331 }
332
a27ffd3 @thousandsofthem Revamp using $project_id.
thousandsofthem authored
333 public function deleteMessage($queue_name, $message_id) {
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
334 $this->setCommonHeaders();
335 $url = "projects/{$this->project_id}/queues/{$queue_name}/messages/{$message_id}";
336 return $this->apiCall(self::DELETE, $url);
337 }
338
339 /* PRIVATE FUNCTIONS */
340
341 private function compiledHeaders(){
342
343 # Set default headers if no headers set.
344 if ($this->headers == null){
345 $this->setCommonHeaders();
346 }
347
348 $headers = array();
349 foreach ($this->headers as $k => $v){
350 $headers[] = "$k: $v";
351 }
352 return $headers;
353 }
354
355 private function apiCall($type, $url, $params = array()){
356 $url = "{$this->url}$url";
357
358 $s = curl_init();
359 if (! isset($params['oauth'])) {
360 $params['oauth'] = $this->token;
361 }
362 switch ($type) {
363 case self::DELETE:
364 $fullUrl = $url . '?' . http_build_query($params);
365 $this->debug('apiCall fullUrl', $fullUrl);
366 curl_setopt($s, CURLOPT_URL, $fullUrl);
367 curl_setopt($s, CURLOPT_CUSTOMREQUEST, self::DELETE);
368 break;
369 case self::POST:
370 $this->debug('apiCall url', $url);
371 curl_setopt($s, CURLOPT_URL, $url);
372 curl_setopt($s, CURLOPT_POST, true);
373 curl_setopt($s, CURLOPT_POSTFIELDS, json_encode($params));
374 break;
375 case self::GET:
376 $fullUrl = $url . '?' . http_build_query($params);
377 $this->debug('apiCall fullUrl', $fullUrl);
378 curl_setopt($s, CURLOPT_URL, $fullUrl);
379 break;
380 }
381
382 curl_setopt($s, CURLOPT_RETURNTRANSFER, true);
383 curl_setopt($s, CURLOPT_HTTPHEADER, $this->compiledHeaders());
384 $_out = curl_exec($s);
385 $status = curl_getinfo($s, CURLINFO_HTTP_CODE);
386 curl_close($s);
387 switch ($status) {
388 case self::HTTP_OK:
389 case self::HTTP_CREATED:
390 case self::HTTP_ACEPTED:
391 $out = $_out;
392 break;
393 default:
394 throw new Http_Exception("http error: {$status} | {$_out}", $status);
395 }
396 return $out;
397 }
398
399
400 /**
401 * @param array|string $config_file_or_options
402 * array of options or name of config file
403 * @return array
404 * @throws InvalidArgumentException
405 */
406 private function getConfigData($config_file_or_options){
407 if (is_string($config_file_or_options)){
408 $ini = parse_ini_file($config_file_or_options, true);
409 if ($ini === false){
410 throw new InvalidArgumentException("Config file $config_file_or_options not found");
411 }
412 if (empty($ini['iron_mq'])){
413 throw new InvalidArgumentException("Config file $config_file_or_options has no section 'iron_mq'");
414 }
415 $config = $ini['iron_mq'];
416 }elseif(is_array($config_file_or_options)){
417 $config = $config_file_or_options;
418 }else{
419 throw new InvalidArgumentException("Wrong parameter type");
420 }
421 foreach ($this->required_config_fields as $field){
422 if (empty($config[$field])){
423 throw new InvalidArgumentException("Required config key missing: '$field'");
424 }
425 }
426 return $config;
427 }
428
429 private function setCommonHeaders(){
430 $this->headers = array(
431 'Authorization' => "OAuth {$this->token}",
432 'User-Agent' => self::header_user_agent,
433 'Content-Type' => 'application/json',
434 'Accept' => self::header_accept,
435 'Accept-Encoding' => self::header_accept_encoding
436 );
437 }
438
439 private function setJsonHeaders(){
440 $this->setCommonHeaders();
441 }
442
443 private function setPostHeaders(){
444 $this->setCommonHeaders();
445 $this->headers['Content-Type'] ='multipart/form-data';
446 }
447
448 private function debug($var_name, $variable){
449 if ($this->debug_enabled){
450 echo "{$var_name}: ".var_export($variable,true)."\n";
451 }
452 }
0884aca @thousandsofthem All except token marked as optional. Code cleanup.
thousandsofthem authored
453
454 private static function json_decode($response){
455 $data = json_decode($response);
456 $json_error = json_last_error();
457 if($json_error != JSON_ERROR_NONE) {
458 throw new JSON_Exception($json_error);
459 }
460 return $data;
461 }
462
248b1d5 @paddyforan Translated IronWorker PHP lib to work with IronMQ.
paddyforan authored
463 }
Something went wrong with that request. Please try again.