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