PageRenderTime 18ms CodeModel.GetById 1ms app.highlight 12ms RepoModel.GetById 2ms app.codeStats 0ms

/php/3.3/Pubnub.php

https://github.com/brentbushnell/pubnub-api
PHP | 563 lines | 312 code | 101 blank | 150 comment | 57 complexity | 06c33d2d3299ecb9b6c8834f83eaa43a MD5 | raw file
  1<?php
  2require_once('PubnubAES.php');
  3
  4/**
  5 * PubNub 3.4 Real-time Push Cloud API
  6 * @package Pubnub
  7 */
  8class Pubnub
  9{
 10    private $ORIGIN = 'PHP.pubnub.com';  // Change this to your custom origin, or IUNDERSTAND.pubnub.com
 11    private $PUBLISH_KEY = 'demo';
 12    private $SUBSCRIBE_KEY = 'demo';
 13    private $SECRET_KEY = false;
 14    private $CIPHER_KEY = '';
 15    private $SSL = false;
 16    private $SESSION_UUID = '';
 17
 18    // New style response contains channel after timetoken
 19    // Old style response does not
 20
 21    private $NEW_STYLE_RESPONSE = true;
 22    private $PEM_PATH = __DIR__;
 23
 24    /**
 25     * Pubnub
 26     *
 27     * Init the Pubnub Client API
 28     *
 29     * @param string $publish_key required key to send messages.
 30     * @param string $subscribe_key required key to receive messages.
 31     * @param string $secret_key optional key to sign messages.
 32     * @param string $origin optional setting for cloud origin.
 33     * @param boolean $ssl required for 2048 bit encrypted messages.
 34     */
 35
 36
 37    function Pubnub(
 38        $publish_key = 'demo',
 39        $subscribe_key = 'demo',
 40        $secret_key = false,
 41        $cipher_key = false,
 42        $ssl = false,
 43        $origin = false,
 44        $pem_path = false
 45    )
 46    {
 47
 48        $this->SESSION_UUID = $this->uuid();
 49
 50        $this->PUBLISH_KEY = $publish_key;
 51        $this->SUBSCRIBE_KEY = $subscribe_key;
 52        $this->SECRET_KEY = $secret_key;
 53
 54        if (!isBlank($cipher_key)) {
 55            $this->CIPHER_KEY = $cipher_key;
 56        }
 57
 58        $this->SSL = $ssl;
 59
 60        if ($pem_path != false)
 61            $this->PEM_PATH = $pem_path;
 62
 63        if ($origin)
 64            $this->ORIGIN = $origin;
 65
 66        if ($this->ORIGIN == "PHP.pubnub.com") {
 67            trigger_error("Before running in production, please contact support@pubnub.com for your custom origin.\nPlease set the origin from PHP.pubnub.com to IUNDERSTAND.pubnub.com to remove this warning.\n", E_USER_NOTICE);
 68        }
 69
 70
 71        if ($ssl)
 72            $this->ORIGIN = 'https://' . $this->ORIGIN;
 73        else
 74            $this->ORIGIN = 'http://' . $this->ORIGIN;
 75    }
 76
 77
 78    /**
 79     * Publish
 80     *
 81     * Send a message to a channel.
 82     *
 83     * @param array $args with channel and message.
 84     * @return array success information.
 85     */
 86
 87    function publish($args)
 88    {
 89        ## Fail if bad input.
 90        if (!(isset($args['channel']) && isset($args['message']))) {
 91            echo('Missing Channel or Message');
 92            return false;
 93        }
 94
 95        ## Capture User Input
 96        $channel = $args['channel'];
 97        $message_org = $args['message'];
 98
 99        $message = $this->sendMessage($message_org);
100
101
102        ## Sign Message
103        $signature = "0";
104        if ($this->SECRET_KEY) {
105            ## Generate String to Sign
106            $string_to_sign = implode('/', array(
107                $this->PUBLISH_KEY,
108                $this->SUBSCRIBE_KEY,
109                $this->SECRET_KEY,
110                $channel,
111                $message
112            ));
113
114            $signature = md5($string_to_sign);
115        }
116
117        ## Send Message
118        $publishResponse = $this->_request(array(
119            'publish',
120            $this->PUBLISH_KEY,
121            $this->SUBSCRIBE_KEY,
122            $signature,
123            $channel,
124            '0',
125            $message
126        ));
127
128        if ($publishResponse == null)
129            return array(0, "Error during publish.");
130        else
131            return $publishResponse;
132
133    }
134
135    public function sendMessage($message_org)
136
137    {
138        if ($this->CIPHER_KEY != false) {
139            $message = json_encode(encrypt(json_encode($message_org), $this->CIPHER_KEY));
140        } else {
141            $message = json_encode($message_org);
142        }
143        return $message;
144    }
145
146    function here_now($args)
147    {
148        if (!($args['channel'])) {
149            echo('Missing Channel');
150            return false;
151        }
152
153        ## Capture User Input
154        $channel = $args['channel'];
155
156        return $this->_request(array(
157            'v2',
158            'presence',
159            'sub_key',
160            $this->SUBSCRIBE_KEY,
161            'channel',
162            $channel
163        ));
164    }
165
166    /**
167     * Subscribe
168     *
169     * This is BLOCKING.
170     * Listen for a message on a channel.
171     *
172     * @param array $args with channel and message.
173     * @return mixed false on fail, array on success.
174     */
175    function subscribe($args, $presence = false)
176    {
177        ## Capture User Input
178        $channel = $args['channel'];
179        $callback = $args['callback'];
180        $timetoken = isset($args['timetoken']) ? $args['timetoken'] : '0';
181
182        ## Fail if missing channel
183        if (!$channel) {
184            echo("Missing Channel.\n");
185            return false;
186        }
187
188        ## Fail if missing callback
189        if (!$callback) {
190            echo("Missing Callback.\n");
191            return false;
192        }
193
194        if ($presence == true) {
195            $mode = "presence";
196        } else
197            $mode = "default";
198
199
200        while (1) {
201
202            try {
203                ## Wait for Message
204                $response = $this->_request(array(
205                    'subscribe',
206                    $this->SUBSCRIBE_KEY,
207                    $channel,
208                    '0',
209                    $timetoken
210                ));
211
212                if ($response == "_PUBNUB_TIMEOUT") {
213                    continue;
214                } elseif ($response == "_PUBNUB_MESSAGE_TOO_LARGE") {
215                    $timetoken = $this->throwAndResetTimetoken($callback, "Message Too Large");
216                    continue;
217                } elseif ($response == null || $timetoken == null) {
218                    $timetoken = $this->throwAndResetTimetoken($callback, "Bad server response.");
219                    continue;
220                }
221
222                $messages = $response[0];
223                $timetoken = $response[1];
224
225                // determine the channel
226
227                if ((count($response) == 3)) {
228                    $derivedChannel = explode(",", $response[2]);
229                } else {
230                    $channel_array = array();
231                    for ($a = 0; $a < sizeof($messages); $a++) {
232                        array_push($channel_array, $channel);
233                    }
234                    $derivedChannel = $channel_array;
235                }
236
237
238                if (!count($messages)) {
239                    continue;
240                }
241
242                $receivedMessages = $this->decodeAndDecrypt($messages, $mode);
243
244
245                $returnArray =  $this->NEW_STYLE_RESPONSE ?  array($receivedMessages, $derivedChannel, $timetoken) : array($receivedMessages, $timetoken);
246
247                # Call once for each message for each channel
248
249                $exit_now = false;
250                for ($i = 0; $i < sizeof($receivedMessages); $i++) {
251
252                    $cbReturn = $callback(array("message" => $returnArray[0][$i], "channel" => $returnArray[1][$i], "timetoken" => $returnArray[2]));
253
254                    if ($cbReturn == false) {
255                        $exit_now = true;
256                    }
257
258                }
259
260                if ($exit_now) {
261                    return;
262                }
263
264
265            } catch (Exception $error) {
266                $this->handleError($error, $args);
267                $timetoken = $this->throwAndResetTimetoken($callback, "Unknown error.");
268                continue;
269
270            }
271        }
272    }
273
274    public function throwAndResetTimetoken($callback, $errorMessage)
275    {
276        $callback(array(0, $errorMessage));
277        $timetoken = "0";
278        return $timetoken;
279    }
280
281    public function decodeAndDecrypt($messages, $mode = "default")
282    {
283        $receivedMessages = array();
284
285        if ($mode == "presence") {
286            return $messages;
287
288        } elseif ($mode == "default") {
289            $messageArray = $messages;
290            $receivedMessages = $this->decodeDecryptLoop($messageArray);
291
292        } elseif ($mode == "detailedHistory") {
293
294            $decodedMessages = $this->decodeDecryptLoop($messages);
295            $receivedMessages = array($decodedMessages[0], $messages[1], $messages[2] );
296        }
297
298        return $receivedMessages;
299    }
300
301    public function decodeDecryptLoop($messageArray)
302    {
303        $receivedMessages = array();
304        foreach ($messageArray as $message) {
305
306            if ($this->CIPHER_KEY) {
307                $decryptedMessage = decrypt($message, $this->CIPHER_KEY);
308                $message = json_decode($decryptedMessage, true);
309            }
310
311            array_push($receivedMessages, $message);
312        }
313        return $receivedMessages;
314    }
315
316
317    public function handleError($error, $args)
318    {
319        $errorMsg = 'Error on line ' . $error->getLine() . ' in ' . $error->getFile() . $error->getMessage();
320        trigger_error($errorMsg, E_COMPILE_WARNING);
321
322
323        sleep(1);
324    }
325
326    /**
327     * Presence
328     *
329     * This is BLOCKING.
330     * Listen for a message on a channel.
331     *
332     * @param array $args with channel and message.
333     * @return mixed false on fail, array on success.
334     */
335    function presence($args)
336    {
337        ## Capture User Input
338        $args['channel'] = ($args['channel'] . "-pnpres");
339        $this->subscribe($args, true);
340    }
341
342    /**
343     * Detailed History
344     *
345     * Load history from a channel.
346     *
347     * @param array $args with 'channel' and 'limit'.
348     * @return mixed false on fail, array on success.
349     */
350
351    function detailedHistory($args)
352    {
353        ## Capture User Input
354
355        ## Fail if bad input.
356        if (!$args['channel']) {
357            echo('Missing Channel');
358            return false;
359        }
360
361        $channel = $args['channel'];
362        $urlParams = "";
363
364        if ($args['count'] || $args['start'] || $args['end'] || $args['reverse']) {
365
366            $urlParamSep = "?";
367            if (isset($args['count'])) {
368                $urlParams .= $urlParamSep . "count=" . $args['count'];
369                $urlParamSep = "&";
370            }
371            if (isset($args['start'])) {
372                $urlParams .= $urlParamSep . "start=" . $args['start'];
373                $urlParamSep = "&";
374            }
375            if (isset($args['end'])) {
376                $urlParams .= $urlParamSep . "end=" . $args['end'];
377                $urlParamSep = "&";
378            }
379            if (isset($args['reverse'])) {
380                $urlParams .= $urlParamSep . "reverse=" . $args['reverse'];
381            }
382
383        }
384
385        $response = $this->_request(array(
386            'v2',
387            'history',
388            "sub-key",
389            $this->SUBSCRIBE_KEY,
390            "channel",
391            $channel
392        ), $urlParams);
393        ;
394
395        $receivedMessages = $this->decodeAndDecrypt($response, "detailedHistory");
396
397        return $receivedMessages;
398
399    }
400
401    /**
402     * History
403     *
404     * Load history from a channel.
405     *
406     * @param array $args with 'channel' and 'limit'.
407     * @return mixed false on fail, array on success.
408     */
409    function history($args)
410    {
411        ## Capture User Input
412        $limit = +$args['limit'] ? +$args['limit'] : 10;
413        $channel = $args['channel'];
414
415        ## Fail if bad input.
416        if (!$channel) {
417            echo('Missing Channel');
418            return false;
419        }
420
421        ## Get History
422        $response = $this->_request(array(
423            'history',
424            $this->SUBSCRIBE_KEY,
425            $channel,
426            '0',
427            $limit
428        ));
429        ;
430
431        $receivedMessages = $this->decodeAndDecrypt($response);
432
433        return $receivedMessages;
434
435    }
436
437    /**
438     * Time
439     *
440     * Timestamp from PubNub Cloud.
441     *
442     * @return int timestamp.
443     */
444    function time()
445    {
446        ## Get History
447        $response = $this->_request(array(
448            'time',
449            '0'
450        ));
451
452        return $response[0];
453    }
454
455    /**
456     * UUID
457     *
458     * UUID generator
459     *
460     * @return UUID
461     */
462    function uuid()
463    {
464        if (function_exists('com_create_guid') === true) {
465            return trim(com_create_guid(), '{}');
466        }
467
468        return sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535));
469    }
470
471    /**
472     * Request URL
473     *
474     * @param array $request of url directories.
475     * @return array from JSON response.
476     */
477    private function _request($request, $urlParams = false)
478    {
479        $request = array_map('Pubnub::_encode', $request);
480
481        array_unshift($request, $this->ORIGIN);
482
483        if (($request[1] === 'presence') || ($request[1] === 'subscribe')) {
484            array_push($request, '?uuid=' . $this->SESSION_UUID);
485        }
486
487        $urlString = implode('/', $request);
488
489        if ($urlParams) {
490            $urlString .= $urlParams;
491        }
492
493        $ch = curl_init();
494
495        $pubnubHeaders = array("V: 3.4", "Accept: */*");
496        curl_setopt($ch, CURLOPT_HTTPHEADER, $pubnubHeaders);
497        curl_setopt($ch, CURLOPT_USERAGENT, "PHP");
498        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
499        curl_setopt($ch, CURLOPT_TIMEOUT, 310);
500
501        curl_setopt($ch, CURLOPT_URL, $urlString);
502
503        if ($this->SSL) {
504            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
505            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
506
507
508            $pemPathAndFilename = $this->PEM_PATH . "/pubnub.com.pem";
509            if (file_exists($pemPathAndFilename))
510                curl_setopt($ch, CURLOPT_CAINFO, $pemPathAndFilename);
511            else {
512                trigger_error("Can't find PEM file. Please set pem_path in initializer.");
513                exit;
514            }
515
516        }
517
518
519        $output = curl_exec($ch);
520        $curlError = curl_errno($ch);
521        $curlResponseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
522
523        curl_close($ch);
524
525        $JSONdecodedResponse = json_decode($output, true);
526
527        if ($JSONdecodedResponse != null)
528            return $JSONdecodedResponse;
529        elseif ($curlError == 28)
530            return "_PUBNUB_TIMEOUT";
531        elseif ($curlResponseCode == 400 || $curlResponseCode == 404)
532            return "_PUBNUB_MESSAGE_TOO_LARGE";
533
534    }
535
536    /**
537     * Encode
538     *
539     * @param string $part of url directories.
540     * @return string encoded string.
541     */
542    private static function _encode($part)
543    {
544        $pieces = array_map('Pubnub::_encode_char', str_split($part));
545        return implode('', $pieces);
546    }
547
548    /**
549     * Encode Char
550     *
551     * @param string $char val.
552     * @return string encoded char.
553     */
554    private static function _encode_char($char)
555    {
556        if (strpos(' ~`!@#$%^&*()+=[]\\{}|;\':",./<>?', $char) === false)
557            return $char;
558        else
559            return rawurlencode($char);
560    }
561}
562
563?>