1: <?php
2:
3: namespace tschiemer\Aspsms;
4:
5: /**
6: * Simple interface for aspsms maintaining common data and states
7: *
8: * @version 1.1.0
9: * @package aspsms
10: * @license LGPL v3 http://www.gnu.org/licenses/lgpl-3.0.txt
11: * @copyright 2013 Philip Tschiemer, <tschiemer@filou.se>
12: * @link https://github.com/tschiemer/aspsms-php
13: */
14: abstract class AbstractSimpleClient
15: {
16: /**
17: * @var Request
18: * @access protected
19: */
20: var $currentRequest = NULL;
21:
22: /**
23: * @var Request
24: * @access protected
25: */
26: var $lastRequest = NULL;
27:
28: /**
29: * @var Response
30: * @access protected
31: */
32: var $lastResponse = NULL;
33:
34: /**
35: * @var string
36: * @access protected
37: */
38: var $userkey = '';
39:
40: /**
41: * @var string
42: * @access protected
43: */
44: var $password = '';
45:
46: /**
47: * @var string
48: * @access protected
49: */
50: var $originator = '';
51:
52: /**
53: * @var string
54: * @access protected
55: */
56: var $affiliateId = '';
57:
58: /**
59: * @var array
60: * @access protected
61: */
62: var $urls = array(
63: 'URLDeliveryNotification' => '',
64: 'URLNonDeliveryNotification' => '',
65: 'URLBufferedMessageNotification' => ''
66: );
67:
68:
69: /**
70: * Contructor
71: *
72: * Possible settings:
73: *
74: * 'userkey' => usekey (required)
75: * 'password' => password (required)
76: * 'originator'=> originator (required)
77: * 'affiliateid'=> affiliate id (optional)
78: * 'urls' => callback urls, assoc array of urls (a.s. setCallbackURL())
79: *
80: * @see AbstractSimpleClient::setCallbackURL()
81: *
82: * @param array $options Associative array of generic settings
83: */
84: public function __construct($options = array())
85: {
86: foreach($options as $key => $value)
87: {
88: switch(strtolower($key))
89: {
90: case 'userkey':
91: $this->userkey = strval($value);
92: break;
93:
94: case 'password':
95: $this->password = strval($value);
96: break;
97:
98: case 'originator':
99: $this->originator = strval($value);
100: break;
101:
102: case 'affiliateid':
103: $this->affiliateId = strval($value);
104: break;
105:
106: case 'urls':
107: foreach($values as $key => $default)
108: {
109: $this->setCallbackURL($key,$default);
110: }
111: break;
112: }
113: }
114: }
115:
116: /**
117: * @return Request
118: */
119: public function getLastRequest()
120: {
121: return $this->lastRequest;
122: }
123:
124: /**
125: * @return Response
126: */
127: public function getLastResponse()
128: {
129: return $this->lastResponse;
130: }
131:
132: public function getLastStatusCode()
133: {
134: if ( empty($this->lastResponse))
135: {
136: return NULL;
137: }
138: return $this->lastResponse->statusCode();
139: }
140:
141: /***********************************************
142: * Set default/common settings
143: */
144:
145: /**
146: * Sets default authentication details.
147: *
148: * @param string $userkey
149: * @param string $password
150: * @return \Aspsms\AbstractSimpleClient
151: */
152: public function setAuth($userkey,$password)
153: {
154: $this->userkey = strval($userkey);
155: $this->password = strval($password);
156:
157: return $this;
158: }
159:
160: /**
161: * Gets default originator.
162: *
163: * @return string
164: */
165: public function getOriginator()
166: {
167: return $this->originator;
168: }
169:
170: /**
171: * Sets default originator.
172: *
173: * @param string $originator
174: * @return \Aspsms\AbstractSimpleClient
175: */
176: public function setOriginator($originator)
177: {
178: $this->originator = strval($originator);
179:
180: return $this;
181: }
182:
183: /**
184: * Gets default affiliate id.
185: *
186: * @return string
187: */
188: public function getAffiliateId()
189: {
190: return $this->affiliateId;
191: }
192:
193: /**
194: * Sets default affiliate id.
195: *
196: * @param string $affiliateId
197: * @return \Aspsms\AbstractSimpleClient
198: */
199: public function setAffiliateId($affiliateId = '')
200: {
201: $this->affiliateId = strval($affiliateId);
202:
203: return $this;
204: }
205:
206: /**
207: * Get current default notification URL.
208: *
209: * @param NULL|string $type
210: * @return string|array Returns string/url IFF <$type> given, array with all urls otherwise.
211: */
212: public function getCallbackURL($type = NULL)
213: {
214: if (isset($this->urls[$type]))
215: {
216: return $this->urls[$type];
217: }
218: return $this->urls;
219: }
220:
221: /**
222: * Sets default callback urls
223: *
224: * 1. simple:
225: * SMS.URLNonDeliveryNotification = "http://www.mysite.com/sms/notdelivered.asp?ID="
226: * When the TransactionReferenceNumber is e.g. 3152, the URL will be loaded like this:
227: * http://www.mysite.com/sms/notdelivered.asp?ID=3152
228: *
229: * 2. detailed:
230: * http://www.yourhost.com/Delivered.asp?SCTS=<SCTS>&DSCTS=<DSCTS>&RSN=<RSN>&DST=<DST>&TRN=<TRN>
231: *
232: * <RCPNT> (Recipient, Mobilenumber)
233: * <SCTS> (Servicecenter Timestamp, Submissiondate)
234: * <DSCTS> (Delivery Servicecenter Timestamp, Notificationdate)
235: * <RSN> (Reasoncode)
236: * <DST> (Deliverystatus)
237: * <TRN> (Transactionreferencenummer)
238: *
239: * @param string $type 'success'/'URLDeliveryNotification', 'error'/'URLNonDeliveryNotification', 'buffered'/'URLBufferedMessageNotification'
240: * @param string $url
241: * @return \Aspsms\AbstractSimpleClient
242: */
243: public function setCallbackURL($type, $url)
244: {
245: // map the simpler labels to the correct indices
246: switch($type)
247: {
248: case 'success': $type = 'URLDeliveryNotification';
249: break;
250:
251: case 'error': $type = 'URLNonDeliveryNotification';
252: break;
253:
254: case 'buffered': $type = 'URLBufferedMessageNotification';
255: break;
256: }
257:
258: if (isset($this->urls[$type]))
259: {
260: $this->urls[$type] = strval($url);
261: }
262: return $this;
263: }
264:
265:
266: /********************************************************
267: * Requests
268: */
269:
270: /**
271: * Request: what's the current balance?
272: *
273: * @return float
274: */
275: public function getCreditBalance()
276: {
277: return $this->send(array(
278: 'RequestName' => 'getCredits'
279: ));
280: }
281:
282:
283: /**
284: * Request: Is (numeric) originator valid, can it be used respectively?
285: *
286: * @param NULL|string $originator
287: * @return boolean
288: */
289: public function checkOriginator($originator = NULL)
290: {
291: if ($originator === NULL)
292: {
293: $originator = $this->originator;
294: }
295:
296: return $this->send(array(
297: 'RequestName' => 'checkOriginator',
298: 'Originator' => $originator
299: ));
300: }
301:
302: /**
303: * Request: request a code to unlock numeric originator. An SMS with the code is sent
304: * to the given (or set) originator.
305: *
306: * @param string $originator Must be numeric
307: * @return type
308: */
309: public function requestOriginatorUnlockCode($originator = NULL)
310: {
311: if ($originator === NULL)
312: {
313: $originator = $this->originator;
314: }
315:
316: return $this->send(array(
317: 'RequestName' => 'sendOriginatorCode',
318: 'Originator' => $originator
319: ));
320: }
321:
322: /**
323: * Request: Attempt to unlock (numeric) originator with code.
324: *
325: * @see requestOriginatorUnlockCode()
326: * @param type $code
327: * @param type $originator
328: */
329: public function unlockOriginator($code, $originator = NULL)
330: {
331: if ($originator === NULL)
332: {
333: $originator = $this->originator;
334: }
335:
336: return $this->send(array(
337: 'RequestName' => 'unlockOriginator',
338: 'Originator' => $originator
339: ));
340: }
341:
342:
343: /**
344: * Request: get delivery status of tracking
345: *
346: * Returns array of delivery statuses.
347: *
348: * If $index_by_nr == TRUE formatted as (only last the last result foreach ref-nr):
349: * array(
350: * 'REF-NR-1' => array($keys[0] => 'REF-NR-1', $keys[1] => '..', .. , $keys[6] => '..'),
351: * 'REF-NR-2' => array($keys[0] => 'REF-NR-2', $keys[1] => '..', .. , $keys[6] => '..'),
352: * 'REF-NR-3' => array($keys[0] => 'REF-NR-3', $keys[1] => '..', .. , $keys[6] => '..')
353: * ..
354: * )
355: *
356: * Else (with possible ref-nr duplicates):
357: *
358: * array(
359: * array($keys[0] => 'REF-NR-1', $keys[1] => '..', .. , $keys[6] => '..'),
360: * array($keys[0] => 'REF-NR-1', $keys[1] => '..', .. , $keys[6] => '..'),
361: * array($keys[0] => 'REF-NR-2', $keys[1] => '..', .. , $keys[6] => '..')
362: * ..
363: * )
364: *
365: *
366: * @param string|array $trackingNumbers
367: * @param boolean $index_by_nr Index result set by reference number (TRUE)? or just return complete list of results (FALSE)?
368: * @param array $keys Delivery Status field names to use
369: */
370: public function getDeliveryStatus($trackingNumbers, $index_by_nr=TRUE, $keys=array())
371: {
372: if (is_array($keys) and count($keys) != 7)
373: {
374: $keys = array(
375: 'nr','status','submissionDate','deliveryDate','reason','other','more'
376: );
377: }
378: return $this->send(array(
379: 'RequestName' => 'getDeliveryStatus',
380: 'TransactionReferenceNumbers' => $trackingNumbers,
381: 'DeliveryStatusIndexing' => $index_by_nr,
382: 'DeliveryStatusFields' => $keys
383: ));
384: }
385:
386: /**
387: * Request: send Text sms
388: *
389: *
390: * String Format:
391: * ("<RECIPIENT_NR>" + {":<TRACKING_NR>"} ";" .. )+
392: * Eg:
393: * 00417777777
394: * 00417777777;00417777777;004177777777
395: * 00417777777:84612004;00417777777:74183874783
396: *
397: * Array Format:
398: * <TRACKING_NR> => <RECIPIENT_NR>
399: *
400: * @param array,string $recipients
401: * @param string $text
402: * @return boolean Request success? (not delivery success)
403: * @see \Aspsms\AbstractSimpleClient::getDeliveryStatus()
404: */
405: public function sendText($recipients,$text)
406: {
407: return $this->send(array(
408: 'RequestName' => 'sendText',
409: 'Recipients' => $recipients,
410: 'MessageText' => $text
411: ));
412: }
413:
414: /**
415: * Request: send WAP push
416: *
417: * String Format:
418: * ("<RECIPIENT_NR>" + {":<TRACKING_NR>"} ";" .. )+
419: * Eg:
420: * 00417777777
421: * 00417777777;00417777777;004177777777
422: * 00417777777:84612004;00417777777:74183874783
423: *
424: * Array Format:
425: * <TRACKING_NR> => <RECIPIENT_NR>
426: *
427: * @param array, string $recipients
428: * @param string $url
429: * @param string $description
430: * @return string request success? (not delivery success))
431: * @see \Aspsms\AbstractSimpleClient::getDeliveryStatus()
432: */
433: public function sendWapPush($recipients,$url,$description='')
434: {
435: return $this->send(array(
436: 'RequestName' => 'sendWapPush',
437: 'Recipients' => $recipients,
438: 'WapDescription' => $description,
439: 'WapURL' => $url
440: ));
441: }
442:
443: /*******************************************
444: * Request option helpers
445: */
446:
447: /**
448: * Use flashing SMS?
449: *
450: * @param boolean $on_off
451: * @return \Aspsms\AbstractSimpleClient
452: */
453: public function flash($on_off = TRUE)
454: {
455: return $this->set('FlashingSMS',$on_off);
456: }
457:
458: /**
459: * Delay delivery of SMS by <$seconds>
460: *
461: * @param int $seconds
462: * @param int|\DateTimeZone $timezone, if int: offset to GMT
463: * @return \Aspsms\AbstractSimpleClient
464: */
465: public function deferTime($seconds,$timezone=0)
466: {
467: return $this->set(array(
468: 'DeferredDeliveryTime' => time() + $seconds,
469: 'TimeZone' => $timezone
470: ));
471: }
472:
473: /**
474: * Set approximate delivery of SMS to <$date>
475: *
476: * @param string|\DateTime $date
477: * @param int|\DateTimeZone $timezone IFF int: offset to GMT
478: * @return \Aspsms\AbstractSimpleClient
479: */
480: public function deferUntil($date,$timezone=0)
481: {
482: return $this->set(array(
483: 'DeferredDeliveryTime' => $date,
484: 'TimeZone' => $timezone
485: ));
486: }
487:
488: /**
489: * Sets callback URLS.
490: *
491: * Example calls:
492: *
493: * $client->callbacks(array('success'=>'http://...','error'=>'http://..'));
494: *
495: * $client->callbacks('success' => 'http://..');
496: *
497: * @param array,string $urls
498: * @param NULL,string $to
499: * @return \Aspsms\AbstractSimpleClient
500: * @see AbstractSimpleClient::setCallbackURL()
501: */
502: public function callbacks($urls = array(), $to = NULL)
503: {
504: if ($urls === FALSE or is_string($urls) and $to === NULL)
505: {
506: $url = ! $urls ? strval($urls) : '';
507:
508: return $this->set(array(
509: 'URLDeliveryNotification' => $url,
510: 'URLNonDeliveryNotification' => $url,
511: 'URLBufferedMessageNotification' => $url
512: ));
513: }
514:
515: if (is_string($urls) and is_string($to))
516: {
517: $urls = array($urls => $to);
518: }
519:
520: $set = array();
521: foreach($urls as $v)
522: {
523: switch($v)
524: {
525: case 'success':
526: case 'URLDeliveryNotification':
527: $set['URLDeliveryNotification'] = $v;
528: break;
529:
530: case 'error':
531: case 'URLNonDeliveryNotification':
532: $set['URLNonDeliveryNotification'] = $v;
533: break;
534:
535: case 'buffered':
536: case 'URLBufferedMessageNotification':
537: $set['URLBufferedMessageNotification'] = $v;
538: break;
539: }
540: }
541:
542: return $this->set($set);
543: }
544:
545:
546: /******************************************
547: * Core request handling
548: */
549:
550:
551: /**
552: * Sets any request option for current/next request.
553: *
554: * @param string|array $key_or_array
555: * @param NULL|mixed $value
556: * @return \Aspsms\Soap\v2\SimpleClient
557: * @throws ServiceException
558: */
559: public function set($key_or_array = NULL, $value = NULL)
560: {
561: // Initialize message data if not done yet.
562: if ($this->currentRequest === NULL)
563: {
564: // set default request parameters (typically used).
565: $this->currentRequest = new Request(array(
566: 'UserKey' => $this->userkey,
567: 'Password' => $this->password,
568: 'Originator' => $this->originator,
569: 'AffiliateId' => $this->affiliateId,
570: 'URLDeliveryNotification' => $this->urls['URLDeliveryNotification'],
571: 'URLNonDeliveryNotification' => $this->urls['URLNonDeliveryNotification'],
572: 'URLBufferedMessageNotification' => $this->urls['URLBufferedMessageNotification']
573: ));
574: }
575:
576: // If is string, bring into array form
577: if (is_string($key_or_array))
578: {
579: $key_or_array = array($key_or_array => $value);
580: }
581:
582: // Now set all fields
583: foreach($key_or_array as $k => $v)
584: {
585: $this->currentRequest->set($k, $v);
586: }
587:
588: return $this;
589: }
590:
591: /**
592: * Clear any message settings set through <set()> for current/next request.
593: *
594: * @see set()
595: * @return \Aspsms\AbstractSimpleClient
596: */
597: public function clear()
598: {
599: $this->currentRequest = NULL;
600:
601: return $this;
602: }
603:
604:
605: /**
606: * Get driver to actually submit request.
607: *
608: * @return AbstractClient Description
609: * @access protected
610: */
611: abstract public function driver(&$request);
612:
613: /**
614: *
615: * @param array $options
616: * @return mixed
617: * @throws ServiceException
618: * @see \Aspsms\AbstractClient
619: */
620: public function send($options = array())
621: {
622: $this->set($options);
623:
624: if ($this->currentRequest->getRequestName() == NULL)
625: {
626: throw new ServiceException('RequestName of request not defined, please use a given method or define properly yourself.');
627: }
628:
629: $request = $this->lastRequest = $this->currentRequest;
630:
631: // get driver to use
632: $driver = $this->driver($request);
633:
634: // Sanity check
635: if ( ! $driver->canProcess($request))
636: {
637: throw new ServiceException('Driver can not process request '.$request->getRequestName());
638: }
639:
640: // send request
641: $driver->send($request);
642:
643: // retrieve response
644: $this->lastResponse = $driver->getResponse();
645:
646: // clear current request
647: $this->currentRequest = NULL;
648:
649:
650: return $this->lastResponse->result();
651: }
652: }