├── README.md ├── composer.json └── lib └── instapush.php /README.md: -------------------------------------------------------------------------------- 1 | Instapush PHP Wrapper 2 | ----- 3 | 4 | The [Instapush PHP Wrapper] (http://www.instapush.im/) is 5 | a PHP wrapper for Instapush API. 6 | 7 | Instapush allows you to recieve push notifications about any trasnaction you care about in your web app immediatly using events based approach. 8 | 9 | This wrapper makes async request hence will minimally affect application speed (if any). 10 | 11 | Usage 12 | ----- 13 | The minimal you'll need to have is: 14 | ```php 15 | require("lib/instapush.php"); 16 | $ip = InstaPush::getInstance("APPLICATION_ID", "APPLICATION_SECRET"); 17 | $ip->track("signup", array( 18 | "email"=> "test@ss.cc" 19 | )); 20 | ``` 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pushbots/instapush-php", 3 | "description": "Instapush PHP Wrapper", 4 | "authors": [ 5 | { 6 | "name": "Instapush", 7 | "email": "info@instapush.im" 8 | } 9 | ], 10 | "require": { 11 | "php": ">=5.3.0" 12 | }, 13 | "autoload": { 14 | "classmap": ["lib/instapush.php"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/instapush.php: -------------------------------------------------------------------------------- 1 | 41 | * 42 | * Option 43 | * Description 44 | * Default 45 | * 46 | * 47 | * ssl 48 | * Enable/disable debug mode 49 | * false 50 | * 51 | * 52 | * debug 53 | * Enable/Disable SSL in the request. 54 | * true 55 | * 56 | * 57 | * 58 | * Example: Tracking an Event 59 | * ------------- 60 | * 61 | * $ip = InstaPush::getInstance("APPLICATION_ID", "APPLICATION_SECRET"); 62 | * 63 | * $ip->track("signup", array( 64 | * "email"=> "test@ss.cc" 65 | * )); 66 | * 67 | */ 68 | 69 | class InstaPush 70 | { 71 | /** 72 | * @var string InstaPush Application ID 73 | */ 74 | private $appId; 75 | 76 | /** 77 | * @var string InstaPush Application secret 78 | */ 79 | private $appSecret; 80 | 81 | /** 82 | * Default options that can be overridden via the $options constructor arg 83 | * @var array 84 | */ 85 | private $_defaults = array( 86 | "host" => "api.instapush.im", 87 | "ssl" => false, // use ssl when available 88 | "debug" => false // enable/disable debug mode 89 | ); 90 | 91 | /** 92 | * @var string the host to connect to (e.g. api.instapush.im) 93 | */ 94 | protected $_host; 95 | 96 | /** 97 | * @var string the protocol to use for the socket connection 98 | */ 99 | private $_protocol; 100 | 101 | /** 102 | * @var string the port to use for the socket connection 103 | */ 104 | private $_port; 105 | 106 | 107 | /** 108 | * @var resource holds the socket resource 109 | */ 110 | private $_socket; 111 | 112 | /** 113 | * @var int timeout the socket connection timeout in seconds 114 | */ 115 | private $_timeout; 116 | 117 | /** 118 | * @var bool whether or not to wait for a response 119 | */ 120 | private $_async; 121 | 122 | /** 123 | * An array of options to be used by the InstaPush library. 124 | * @var array 125 | */ 126 | protected $_options = array(); 127 | 128 | /** 129 | * An instance of the InstaPush class (for singleton use) 130 | * @var InstaPush 131 | */ 132 | private static $_instance; 133 | 134 | const VERSION = '0.2'; 135 | 136 | 137 | /** 138 | * Instantiates a new InstaPush instance. 139 | * @param $appId Instapush Applciation Id. 140 | * @param $appSecret Instapush Application Secret. 141 | */ 142 | public function __construct($appId, $appSecret, $options = array()) { 143 | $this->appId = $appId; 144 | $this->appSecret = $appSecret; 145 | $options = array_merge($this->_defaults, $options); 146 | $this->_options = $options; 147 | $this->_host = $options['host']; 148 | $this->_timeout = array_key_exists('timeout', $options) ? $options['timeout'] : 4; 149 | $this->_async = array_key_exists('async', $options) && $options['async'] === false ? false : true; 150 | 151 | if (array_key_exists('ssl', $options) && $options['ssl'] == true) { 152 | $this->_protocol = "ssl"; 153 | $this->_port = 443; 154 | } else { 155 | $this->_protocol = "tcp"; 156 | $this->_port = 80; 157 | } 158 | } 159 | 160 | /** 161 | * Returns a singleton instance of InstaPush 162 | * @param $appId Instapush Applciation Id. 163 | * @param $appSecret Instapush Application Secret. 164 | * @return InstaPush 165 | */ 166 | public static function getInstance($appId, $appSecret, $options = array()) { 167 | if(!isset(self::$_instance)) { 168 | self::$_instance = new InstaPush($appId, $appSecret, $options); 169 | } 170 | return self::$_instance; 171 | } 172 | 173 | /** 174 | * Returns InstaPush Application ID. 175 | */ 176 | public static function getAppId() 177 | { 178 | return self::$appId; 179 | } 180 | 181 | 182 | /** 183 | * Log a message to PHP's error log 184 | * @param $msg 185 | */ 186 | protected function _log($msg) { 187 | $arr = debug_backtrace(); 188 | $class = $arr[0]['class']; 189 | $line = $arr[0]['line']; 190 | error_log ( "[ $class - line $line ] : " . $msg ); 191 | } 192 | 193 | /** 194 | * Returns true if in debug mode, false if in production mode 195 | * @return bool 196 | */ 197 | protected function _debug() { 198 | return array_key_exists("debug", $this->_options) && $this->_options["debug"] == true; 199 | } 200 | 201 | /** 202 | * Track an event. 203 | * @param string $event Event title e.g. registration, subscription, fatal error..etc 204 | * @param array $trackers What to track for that event. 205 | */ 206 | public function track($event, $trackers) { 207 | $this->event['event'] = $event; 208 | foreach ($trackers as $tracker => $value) { 209 | $this->event[$tracker] = $value; 210 | } 211 | $Push = $this->_consume(); 212 | return $Push; 213 | } 214 | 215 | /** 216 | * Attempt to open a new socket connection, cache it, and return the resource 217 | * @param bool $retry 218 | * @return bool|resource 219 | */ 220 | private function _createSocket($retry = true) { 221 | try { 222 | $socket = fsockopen($this->_protocol . "://" . $this->_host, $this->_port, $err_no, $err_msg, $this->_timeout); 223 | 224 | if ($this->_debug()) { 225 | $this->_log("Opening socket connection to " . $this->_protocol . "://" . $this->_host . ":" . $this->_port); 226 | } 227 | 228 | if ($err_no != 0) { 229 | $this->_handleError($err_no, $err_msg); 230 | return $retry == true ? $this->_createSocket(false) : false; 231 | } else { 232 | // cache the socket 233 | $this->_socket = $socket; 234 | return $socket; 235 | } 236 | 237 | } catch (Exception $e) { 238 | $this->_handleError($e->getCode(), $e->getMessage()); 239 | return $retry == true ? $this->_createSocket(false) : false; 240 | } 241 | } 242 | 243 | 244 | /** 245 | * Attempt to close and dereference a socket resource 246 | */ 247 | private function _destroySocket() { 248 | $socket = $this->_socket; 249 | $this->_socket = null; 250 | fclose($socket); 251 | } 252 | 253 | /** 254 | * Return cached socket if open or create a new socket 255 | * @return bool|resource 256 | */ 257 | private function _getSocket() { 258 | if(is_resource($this->_socket)) { 259 | 260 | if ($this->_debug()) { 261 | $this->_log("Using existing socket"); 262 | } 263 | 264 | return $this->_socket; 265 | } else { 266 | 267 | if ($this->_debug()) { 268 | $this->_log("Creating new socket at ".time()); 269 | } 270 | 271 | return $this->_createSocket(); 272 | } 273 | } 274 | 275 | /** 276 | * Write $data through the given $socket 277 | * @param $socket 278 | * @param $data 279 | * @param bool $retry 280 | * @return bool 281 | */ 282 | private function _write($socket, $data, $retry = true) { 283 | 284 | $bytes_sent = 0; 285 | $bytes_total = strlen($data); 286 | $socket_closed = false; 287 | $success = true; 288 | $max_bytes_per_write = 8192; 289 | 290 | // if we have no data to write just return true 291 | if ($bytes_total == 0) { 292 | return true; 293 | } 294 | 295 | // try to write the data 296 | while (!$socket_closed && $bytes_sent < $bytes_total) { 297 | 298 | try { 299 | $bytes = fwrite($socket, $data, $max_bytes_per_write); 300 | 301 | if ($this->_debug()) { 302 | $this->_log("Socket wrote ".$bytes." bytes"); 303 | } 304 | 305 | // if we actually wrote data, then remove the written portion from $data left to write 306 | if ($bytes > 0) { 307 | $data = substr($data, $max_bytes_per_write); 308 | } 309 | 310 | } catch (Exception $e) { 311 | $this->_handleError($e->getCode(), $e->getMessage()); 312 | $socket_closed = true; 313 | } 314 | 315 | if (isset($bytes) && $bytes) { 316 | $bytes_sent += $bytes; 317 | } else { 318 | $socket_closed = true; 319 | } 320 | } 321 | 322 | // create a new socket if the current one is closed and retry the message 323 | if ($socket_closed) { 324 | 325 | $this->_destroySocket(); 326 | 327 | if ($retry) { 328 | if ($this->_debug()) { 329 | $this->_log("Retrying socket write..."); 330 | } 331 | $socket = $this->_getSocket(); 332 | if ($socket) return $this->_write($socket, $data, false); 333 | } 334 | 335 | return false; 336 | } 337 | 338 | 339 | // only wait for the response in debug mode or if we explicitly want to be synchronous 340 | if ($this->_debug() || !$this->_async) { 341 | //@TODO Handle Server response 342 | 343 | } 344 | 345 | return $success; 346 | } 347 | 348 | /** 349 | * Handles errors that occur in a consumer 350 | * @param $code 351 | * @param $msg 352 | */ 353 | protected function _handleError($code, $msg) { 354 | if (isset($this->_options['error_callback'])) { 355 | $handler = $this->_options['error_callback']; 356 | $handler($code, $msg); 357 | } 358 | 359 | if ($this->_debug()) { 360 | $arr = debug_backtrace(); 361 | $class = get_class($arr[0]['object']); 362 | $line = $arr[0]['line']; 363 | error_log ( "[ $class - line $line ] : " . print_r($msg, true) ); 364 | } 365 | } 366 | 367 | /** 368 | * Consumes messages and writes them to Instapush API using a socket 369 | */ 370 | private function _consume(){ 371 | 372 | $socket = $this->_getSocket(); 373 | 374 | if (!is_resource($socket)) { 375 | return false; 376 | } 377 | 378 | // Request POST String 379 | foreach ($this->event as $key => &$val) { 380 | if (is_array($val)) $val = implode(',', $val); 381 | $post_params[] = $key.'='.urlencode($val); 382 | } 383 | 384 | $data = implode('&', $post_params); 385 | 386 | 387 | $body = "POST /php HTTP/1.1\r\n"; 388 | $body.= "Host: " . $this->_host . "\r\n"; 389 | $body.= "X-INSTAPUSH-APPID: " . $this->appId ."\r\n"; 390 | $body.= "X-INSTAPUSH-APPSECRET: " . $this->appSecret ."\r\n"; 391 | $body.= "Content-Type: application/x-www-form-urlencoded\r\n"; 392 | $body.= "Content-Length: ".strlen($data)."\r\n"; 393 | $body.= "Connection: Close\r\n\r\n"; 394 | $body.= $data; 395 | 396 | return $this->_write($socket, $body); 397 | } 398 | 399 | } 400 | 401 | --------------------------------------------------------------------------------