1: <?php
2:
3: namespace tschiemer\Aspsms\Http;
4: use \tschiemer\Aspsms as Aspsms;
5:
6: if ( ! function_exists('curl_init'))
7: {
8: throw new \Exception('CURL extension required for \tschiemer\Aspsms\HttpClient');
9: }
10:
11: /**
12: * Driver for HTTP based services.
13: *
14: * @version 1.1.0
15: * @package aspsms
16: * @license LGPL v3 http://www.gnu.org/licenses/lgpl-3.0.txt
17: * @copyright 2013 Philip Tschiemer, <tschiemer@filou.se>
18: * @link https://github.com/tschiemer/aspsms-php
19: */
20: class HttpClient extends Aspsms\AbstractClient
21: {
22: /**
23: * Request base url
24: *
25: * @var string
26: */
27: var $baseUrl = 'https://webservice.aspsms.com/aspsmsx2.asmx/';
28:
29: /**
30: * HTTP method to use
31: *
32: * @var string
33: */
34: var $method = 'POST';
35:
36: /**
37: * List of CURL options to use.
38: *
39: * @var array
40: */
41: var $curlOpt = array(
42: CURLOPT_USERAGENT => 'aspsms-php v1 http:1',
43: CURLOPT_SSL_VERIFYPEER => FALSE
44: );
45:
46:
47: /**
48: * Request configuration
49: *
50: * Foreach request:
51: * 'service' := actual service name to use
52: * 'param' := list of fields and default settings to use
53: *
54: * @var array[]
55: */
56: var $requests = array(
57: 'getVersion' => array(
58: 'service' => 'VersionInfo'
59: ),
60: 'getCredits' => array(
61: 'service' => 'CheckCredits',
62: 'param' => array(
63: 'UserKey' => '',
64: 'Password' => ''
65: )),
66: 'getStatusCodeDescription' => array(
67: 'service' => 'GetStatusCodeDescription',
68: 'param' => array(
69: 'StatusCode' => ''
70: )),
71:
72: 'sendText' => array(
73: 'service' => 'SendUnicodeSMS',
74: 'param' => array(
75: 'UserKey' => '',
76: 'Password' => '',
77: 'Recipients'=> '',
78: 'Originator'=> '',
79: 'MessageText' => '',
80: 'DeferredDeliveryTime' => '',
81: 'FlashingSMS'=> '',
82: 'TimeZone' => '',
83: 'URLBufferedMessageNotification' => '',
84: 'URLDeliveryNotification' => '',
85: 'URLNonDeliveryNotification' => '',
86: 'AffiliateId' => ''
87: )),
88: 'sendWapPush' => array(
89: 'service' => 'SimpleWAPPush',
90: 'param' => array(
91: 'UserKey' => '',
92: 'Password' => '',
93: 'Recipients'=> '',
94: 'Originator'=> '',
95: 'WapDescription' => '',
96: 'WapURL' => '',
97: 'DeferredDeliveryTime' => '',
98: 'FlashingSMS'=> '',
99: 'TimeZone' => '',
100: 'URLBufferedMessageNotification' => '',
101: 'URLDeliveryNotification' => '',
102: 'URLNonDeliveryNotification' => '',
103: 'AffiliateId' => ''
104: )),
105: 'sendToken' => array(
106: 'service' => 'SendTokenSMS',
107: 'param' => array(
108: 'UserKey' => '',
109: 'Password' => '',
110: 'Recipients'=> '',
111: 'Originator'=> '',
112: 'MessageData'=>'',
113: 'TokenReference'=>'',
114: 'TokenValidity'=>'5',
115: 'TokenMask' => '',
116: 'VerificationCode' => '',
117: 'TokenCaseSensitive' => '0',
118: 'URLBufferedMessageNotification' => '',
119: 'URLDeliveryNotification' => '',
120: 'URLNonDeliveryNotification' => '',
121: 'AffiliateId' => ''
122: )),
123: 'verifyToken' => array(
124: 'service' => 'VerifyToken',
125: 'param' => array(
126: 'UserKey' => '',
127: 'Password' => '',
128: 'PhoneNumber'=> '',
129: 'TokenReference'=>'',
130: 'VerificationCode' => '',
131: )),
132:
133: 'getDeliveryStatus' => array(
134: 'service' => 'InquireDeliveryNotifications',
135: 'param' => array(
136: 'UserKey' => '',
137: 'Password' => '',
138: 'TransactionReferenceNumbers'=> ''
139: )),
140:
141: 'checkOriginator' => array(
142: 'service' => 'CheckOriginatorAuthorization',
143: 'param' => array(
144: 'UserKey' => '',
145: 'Password' => '',
146: 'Originator'=> ''
147: )),
148: 'sendOriginatorCode' => array(
149: 'service' => 'SendOriginatorUnlockCode',
150: 'param' => array(
151: 'UserKey' => '',
152: 'Password' => '',
153: 'Originator'=> ''
154: )),
155: 'unlockOriginator' => array(
156: 'service' => 'UnlockOriginator',
157: 'param' => array(
158: 'UserKey' => '',
159: 'Password' => '',
160: 'Originator'=> '',
161: 'OriginatorUnlockCode'=>'',
162: 'AffiliateId'=> ''
163: ))
164: );
165:
166:
167: /**
168: * Instantiate and configure HttpClient.
169: * Possible options (to be passed in assoc array):
170: *
171: * "method" optional, default "GET" "GET" | "POST"
172: * "baseUrl" optional, default as defined service base URL to use for requests
173: * "curl" optional associative array with options to pass to CURL
174: *
175: * @see HttpClient::$method
176: * @see HttpClient::$baseUrl
177: * @see HttpClient::$curlOpt
178: *
179: * @param array $options Associative array
180: * @throws AspsmsException
181: */
182: public function __construct($options = array()) {
183:
184: if (isset($options['method']))
185: {
186: $method = strtoupper($options['method']);
187: switch($method)
188: {
189: case 'GET':
190: case 'POST':
191: $this->method = $method;
192: break;
193:
194: default:
195: throw new Aspsms\ServiceException('Invalid method type for HttpClient: '.$method);
196: }
197: }
198:
199: if (isset($options['baseUrl']))
200: {
201: $this->baseUrl = $options['baseUrl'];
202: }
203:
204: if (isset($options['curl']))
205: {
206: foreach($options['curl'] as $k => $v)
207: {
208: $this->curlOpt[$k] = $v;
209: }
210: }
211: }
212:
213: /**
214: * Send given request.
215: *
216: * @param \Aspsms\Request $request
217: * @throws AspsmsException
218: * @see AbstractClient::getResponse()
219: */
220: public function send($request)
221: {
222: // Set internal request
223: $this->request = $request;
224:
225: // friendly shortcut
226: $requestName = $request->getRequestName();
227:
228:
229: $cfg =& $this->requests[$requestName];
230:
231: // friendly shortcut
232: $serviceName = $cfg['service'];
233:
234: // Initialize new response object
235: $this->response = new Aspsms\Response($request);
236:
237: // Prepare parameters
238: if (isset($cfg['param']))
239: {
240: $param_pre = $request->extractArray($cfg['param']);
241: }
242: else
243: {
244: $param_pre = array();
245: }
246:
247: $param = array();
248: foreach($param_pre as $k => $v)
249: {
250: // @todo key dependent encoding?
251: $param[] = urlencode($k) . '=' . urlencode($v);
252: }
253: $param = implode('&',$param);
254:
255: // Set Service URL
256: $url = $this->baseUrl . $serviceName . '?';
257:
258: // Get a copy of curl options
259: $curlOpt = $this->curlOpt;
260:
261: // Adapt settings according to used method
262: if ($this->method == 'GET')
263: {
264: $url .= $param;
265: }
266: else
267: {
268: $curlOpt[CURLOPT_POSTFIELDS] = $param;
269: }
270:
271:
272: // attempt to perform request
273: $ch = curl_init($url);
274:
275: curl_setopt_array($ch, $curlOpt);
276: curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); // just always set this.
277: $result = curl_exec($ch);
278:
279: if (curl_errno($ch) != 0)
280: {
281: $errstr = curl_error($ch);
282: throw new Aspsms\ServiceExceptionn('CURL request failed: '. $errstr);
283: }
284:
285: curl_close($ch);
286:
287: if (is_string($result) and preg_match('/<\?xml version="((?:\d|\.)+)" encoding="([a-zA-Z0-9_-]+)"\?>/',$result,$m))
288: {
289: $dom = new \DOMDocument($m[1],$m[2]);
290: $dom->loadXML($result);
291: $this->response->result = $dom->textContent;
292: }
293: else
294: {
295: throw new Aspsms\ServiceExceptionon('Invalid non-XML response given.');
296: }
297:
298:
299: // Result post-processing
300: if (method_exists($this, 'post_'.$serviceName))
301: {
302: $this->{'post_'.$serviceName}();
303: }
304: else
305: {
306: $this->post_default();
307: }
308: }
309:
310: /**
311: * Default Post-Processor (applied if no specific PP to be used)
312: */
313: public function post_default()
314: {
315: if (preg_match('/^StatusCode\:(\d+)$/',$this->response->result,$m))
316: {
317: $this->response->result = intval($m[1]) == Aspsms\Response::STAT_OK;
318: $this->response->statusCode($m[1]);
319: }
320: else
321: {
322: $this->response->result = FALSE;
323: }
324: }
325:
326: /**
327: * Post-Processing for CheckCredits
328: */
329: public function post_CheckCredits()
330: {
331: if (preg_match('/^Credits:((?:\d|\.)+)$/',$this->response->result,$m))
332: {
333: $this->response->result = floatval($m[1]);
334: $this->response->statusCode(Aspsms\Response::STAT_OK);
335: }
336: else
337: {
338: $this->post_default();
339: }
340: }
341:
342:
343: /**
344: * Post-Processing for GetStatusCodeDescription
345: *
346: * If invalid status code given, returns status code as description
347: */
348: public function post_GetStatusCodeDescription()
349: {
350: if ($this->request->get('StatusCode') != $this->response->result)
351: {
352: $this->response->statusCode(Aspsms\Response::STAT_OK);
353: }
354: }
355:
356: public function post_CheckOriginatorAuthorization()
357: {
358: $result_str = $this->response->result;
359:
360: switch($result_str)
361: {
362: case 'StatusCode:31':
363: $this->response->result = TRUE;
364: $this->response->statusCode(31);
365: break;
366: case 'StatusCode:30':
367: $this->response->result = FALSE;
368: $this->response->statusCode(30);
369: break;
370: default:
371: $this->response->result = NULL;
372: if (preg_match('/^StatusCode\:(\d+)$/',$result_str,$m))
373: {
374: $this->response->statusCode($m[1]);
375: }
376: else
377: {
378: $this->response->statusCode(0);
379: }
380: }
381: }
382:
383: /**
384: * Post-Processing for InquireDeliveryNotifications
385: */
386: public function post_InquireDeliveryNotifications()
387: {
388: $result_str = $this->response->result;
389:
390: if (preg_match('/^StatusCode\:(\d+)$/',$result_str,$m))
391: {
392: $this->response->result = FALSE;
393: $this->response->statusCode($m[1]);
394: return;
395: }
396:
397: if (strlen($result_str) == 0)
398: {
399: $this->response->result = array();
400: return;
401: }
402:
403: // Get keys to be used for status fields
404: $keys = $this->request->get('DeliveryStatusFields');
405: if (empty($keys))
406: {
407: $keys = range(0,6);
408: }
409: $nr = $keys[0];
410:
411: // Select only last result for each tracking number
412: $index_by_nr = (boolean)$this->request->get('DeliveryStatusIndexing');
413:
414: // Create list of results
415: $all_list = explode("\n",$result_str);
416:
417: $list = array();
418: foreach($all_list as $one)
419: {
420: $tmp = array_combine($keys, explode(';',$one));
421:
422: if ($index_by_nr)
423: {
424: $list[$tmp[$nr]] = $tmp;
425: }
426: else
427: {
428: $list[] = $tmp;
429: }
430: }
431:
432: $this->response->result = $list;
433: }
434:
435: /**
436: * Post-Processing for VersionInfo
437: *
438: * Creates an associative array with complete response, service version and build number.
439: */
440: public function post_VersionInfo()
441: {
442: $result_str = $this->response->result;
443:
444: if (preg_match('/^StatusCode\:(\d+)$/',$result_str,$m))
445: {
446: $this->response->result = FALSE;
447: $this->response->statusCode($m[1]);
448: return;
449: }
450:
451:
452: if (preg_match('/^([^ ]+)/',$result_str,$m))
453: {
454: $v = $m[1];
455: }
456: else
457: {
458: $v = '';
459: }
460:
461: if (preg_match('/build:((?:\d|\.)+)/',$result_str,$m))
462: {
463: $b = $m[1];
464: }
465: else
466: {
467: $b = '';
468: }
469:
470: $this->response->result = array(
471: 'all' => $result_str,
472: 'version' => $v,
473: 'build' => $b
474: );
475: }
476: }