├── 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 |
--------------------------------------------------------------------------------