Chamilo is a learning management system focused on ease of use and accessibility
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
chamilo-lms/plugin/clockworksms/vendor/clockworksms_api.php

643 lines
18 KiB

<?php
/**
* Clockwork PHP API
*
* @package Clockwork
* @copyright Mediaburst Ltd 2012
* @license ISC
* @link http://www.clockworksms.com
* @version 1.3.0
*/
if ( !class_exists('ClockworkException') ) {
require_once('exception.php');
}
/**
* Main Clockwork API Class
*
* @package Clockwork
* @since 1.0
*/
class Clockwork {
/*
* Version of this class
*/
const VERSION = '1.3.1';
/**
* All Clockwork API calls start with BASE_URL
* @author Martin Steel
*/
const API_BASE_URL = 'api.clockworksms.com/xml/';
/**
* string to append to API_BASE_URL to check authentication
* @author Martin Steel
*/
const API_AUTH_METHOD = 'authenticate';
/**
* string to append to API_BASE_URL for sending SMS
* @author Martin Steel
*/
const API_SMS_METHOD = 'sms';
/**
* string to append to API_BASE_URL for checking message credit
* @author Martin Steel
*/
const API_CREDIT_METHOD = 'credit';
/**
* string to append to API_BASE_URL for checking account balance
* @author Martin Steel
*/
const API_BALANCE_METHOD = 'balance';
/**
* Clockwork API Key
*
* @var string
* @author Martin Steel
*/
public $key;
/**
* Use SSL when making HTTP requests
*
* If this is not set, SSL will be used where PHP supports it
*
* @var bool
* @author Martin Steel
*/
public $ssl;
/**
* Proxy server hostname (Optional)
*
* @var string
* @author Martin Steel
*/
public $proxy_host;
/**
* Proxy server port (Optional)
*
* @var integer
* @author Martin Steel
*/
public $proxy_port;
/**
* From address used on text messages
*
* @var string (11 characters or 12 numbers)
* @author Martin Steel
*/
public $from;
/**
* Allow long SMS messages (Cost up to 3 credits)
*
* @var bool
* @author Martin Steel
*/
public $long;
/**
* Truncate message text if it is too long
*
* @var bool
* @author Martin Steel
*/
public $truncate;
/**
* Enables various logging of messages when true.
*
* @var bool
* @author Martin Steel
*/
public $log;
/**
* What Clockwork should do if you send an invalid character
*
* Possible values:
* 'error' - Return an error (Messasge is not sent)
* 'remove' - Remove the invalid character(s)
* 'replace' - Replace invalid characters where possible, remove others
* @author Martin Steel
*/
public $invalid_char_action;
/**
* Create a new instance of the Clockwork wrapper
*
* @param string key Your Clockwork API Key
* @param array options Optional parameters for sending SMS
* @author Martin Steel
*/
public function __construct($key, array $options = array()) {
if (empty($key)) {
throw new ClockworkException("Key can't be blank");
} else {
$this->key = $key;
}
$this->ssl = (array_key_exists('ssl', $options)) ? $options['ssl'] : null;
$this->proxy_host = (array_key_exists('proxy_host', $options)) ? $options['proxy_host'] : null;
$this->proxy_port = (array_key_exists('proxy_port', $options)) ? $options['proxy_port'] : null;
$this->from = (array_key_exists('from', $options)) ? $options['from'] : null;
$this->long = (array_key_exists('long', $options)) ? $options['long'] : null;
$this->truncate = (array_key_exists('truncate', $options)) ? $options['truncate'] : null;
$this->invalid_char_action = (array_key_exists('invalid_char_action', $options)) ? $options['invalid_char_action'] : null;
$this->log = (array_key_exists('log', $options)) ? $options['log'] : false;
}
/**
* Send some text messages
*
*
* @author Martin Steel
*/
public function send(array $sms) {
if (!is_array($sms)) {
throw new ClockworkException("sms parameter must be an array");
}
$single_message = $this->is_assoc($sms);
if ($single_message) {
$sms = array($sms);
}
$req_doc = new DOMDocument('1.0', 'UTF-8');
$root = $req_doc->createElement('Message');
$req_doc->appendChild($root);
$user_node = $req_doc->createElement('Key');
$user_node->appendChild($req_doc->createTextNode($this->key));
$root->appendChild($user_node);
for ($i = 0; $i < count($sms); $i++) {
$single = $sms[$i];
$sms_node = $req_doc->createElement('SMS');
// Phone number
$sms_node->appendChild($req_doc->createElement('To', $single['to']));
// Message text
$content_node = $req_doc->createElement('Content');
$content_node->appendChild($req_doc->createTextNode($single['message']));
$sms_node->appendChild($content_node);
// From
if (array_key_exists('from', $single) || isset($this->from)) {
$from_node = $req_doc->createElement('From');
$from_node->appendChild($req_doc->createTextNode(array_key_exists('from', $single) ? $single['from'] : $this->from));
$sms_node->appendChild($from_node);
}
// Client ID
if (array_key_exists('client_id', $single)) {
$client_id_node = $req_doc->createElement('ClientID');
$client_id_node->appendChild($req_doc->createTextNode($single['client_id']));
$sms_node->appendChild($client_id_node);
}
// Long
if (array_key_exists('long', $single) || isset($this->long)) {
$long = array_key_exists('long', $single) ? $single['long'] : $this->long;
$long_node = $req_doc->createElement('Long');
$long_node->appendChild($req_doc->createTextNode($long ? 1 : 0));
$sms_node->appendChild($long_node);
}
// Truncate
if (array_key_exists('truncate', $single) || isset($this->truncate)) {
$truncate = array_key_exists('truncate', $single) ? $single['truncate'] : $this->truncate;
$trunc_node = $req_doc->createElement('Truncate');
$trunc_node->appendChild($req_doc->createTextNode($truncate ? 1 : 0));
$sms_node->appendChild($trunc_node);
}
// Invalid Char Action
if (array_key_exists('invalid_char_action', $single) || isset($this->invalid_char_action)) {
$action = array_key_exists('invalid_char_action', $single) ? $single['invalid_char_action'] : $this->invalid_char_action;
switch (strtolower($action)) {
case 'error':
$sms_node->appendChild($req_doc->createElement('InvalidCharAction', 1));
break;
case 'remove':
$sms_node->appendChild($req_doc->createElement('InvalidCharAction', 2));
break;
case 'replace':
$sms_node->appendChild($req_doc->createElement('InvalidCharAction', 3));
break;
default:
break;
}
}
// Wrapper ID
$sms_node->appendChild($req_doc->createElement('WrapperID', $i));
$root->appendChild($sms_node);
}
$req_xml = $req_doc->saveXML();
$resp_xml = $this->postToClockwork(self::API_SMS_METHOD, $req_xml);
$resp_doc = new DOMDocument();
$resp_doc->loadXML($resp_xml);
$response = array();
$err_no = null;
$err_desc = null;
foreach($resp_doc->documentElement->childNodes AS $doc_child) {
switch(strtolower($doc_child->nodeName)) {
case 'sms_resp':
$resp = array();
$wrapper_id = null;
foreach($doc_child->childNodes AS $resp_node) {
switch(strtolower($resp_node->nodeName)) {
case 'messageid':
$resp['id'] = $resp_node->nodeValue;
break;
case 'errno':
$resp['error_code'] = $resp_node->nodeValue;
break;
case 'errdesc':
$resp['error_message'] = $resp_node->nodeValue;
break;
case 'wrapperid':
$wrapper_id = $resp_node->nodeValue;
break;
}
}
if( array_key_exists('error_code', $resp ) )
{
$resp['success'] = 0;
} else {
$resp['success'] = 1;
}
$resp['sms'] = $sms[$wrapper_id];
array_push($response, $resp);
break;
case 'errno':
$err_no = $doc_child->nodeValue;
break;
case 'errdesc':
$err_desc = $doc_child->nodeValue;
break;
}
}
if (isset($err_no)) {
throw new ClockworkException($err_desc, $err_no);
}
if ($single_message) {
return $response[0];
} else {
return $response;
}
}
/**
* Check how many SMS credits you have available
*
* @return integer SMS credits remaining
* @deprecated Use checkBalance() instead
* @author Martin Steel
*/
public function checkCredit() {
// Create XML doc for request
$req_doc = new DOMDocument('1.0', 'UTF-8');
$root = $req_doc->createElement('Credit');
$req_doc->appendChild($root);
$root->appendChild($req_doc->createElement('Key', $this->key));
$req_xml = $req_doc->saveXML();
// POST XML to Clockwork
$resp_xml = $this->postToClockwork(self::API_CREDIT_METHOD, $req_xml);
// Create XML doc for response
$resp_doc = new DOMDocument();
$resp_doc->loadXML($resp_xml);
// Parse the response to find credit value
$credit;
$err_no = null;
$err_desc = null;
foreach ($resp_doc->documentElement->childNodes AS $doc_child) {
switch ($doc_child->nodeName) {
case "Credit":
$credit = $doc_child->nodeValue;
break;
case "ErrNo":
$err_no = $doc_child->nodeValue;
break;
case "ErrDesc":
$err_desc = $doc_child->nodeValue;
break;
default:
break;
}
}
if (isset($err_no)) {
throw new ClockworkException($err_desc, $err_no);
}
return $credit;
}
/**
* Check your account balance
*
* @return array Array of account balance:
* @author Martin Steel
*/
public function checkBalance() {
// Create XML doc for request
$req_doc = new DOMDocument('1.0', 'UTF-8');
$root = $req_doc->createElement('Balance');
$req_doc->appendChild($root);
$root->appendChild($req_doc->createElement('Key', $this->key));
$req_xml = $req_doc->saveXML();
// POST XML to Clockwork
$resp_xml = $this->postToClockwork(self::API_BALANCE_METHOD, $req_xml);
// Create XML doc for response
$resp_doc = new DOMDocument();
$resp_doc->loadXML($resp_xml);
// Parse the response to find balance value
$balance = null;
$err_no = null;
$err_desc = null;
foreach ($resp_doc->documentElement->childNodes as $doc_child) {
switch ($doc_child->nodeName) {
case "Balance":
$balance = number_format(floatval($doc_child->nodeValue), 2);
break;
case "Currency":
foreach ($doc_child->childNodes as $resp_node) {
switch ($resp_node->tagName) {
case "Symbol":
$symbol = $resp_node->nodeValue;
break;
case "Code":
$code = $resp_node->nodeValue;
break;
}
}
break;
case "ErrNo":
$err_no = $doc_child->nodeValue;
break;
case "ErrDesc":
$err_desc = $doc_child->nodeValue;
break;
default:
break;
}
}
if (isset($err_no)) {
throw new ClockworkException($err_desc, $err_no);
}
return array( 'symbol' => $symbol, 'balance' => $balance, 'code' => $code );
}
/**
* Check whether the API Key is valid
*
* @return bool True indicates a valid key
* @author Martin Steel
*/
public function checkKey() {
// Create XML doc for request
$req_doc = new DOMDocument('1.0', 'UTF-8');
$root = $req_doc->createElement('Authenticate');
$req_doc->appendChild($root);
$root->appendChild($req_doc->createElement('Key', $this->key));
$req_xml = $req_doc->saveXML();
// POST XML to Clockwork
$resp_xml = $this->postToClockwork(self::API_AUTH_METHOD, $req_xml);
// Create XML doc for response
$resp_doc = new DOMDocument();
$resp_doc->loadXML($resp_xml);
// Parse the response to see if authenticated
$cust_id;
$err_no = null;
$err_desc = null;
foreach ($resp_doc->documentElement->childNodes AS $doc_child) {
switch ($doc_child->nodeName) {
case "CustID":
$cust_id = $doc_child->nodeValue;
break;
case "ErrNo":
$err_no = $doc_child->nodeValue;
break;
case "ErrDesc":
$err_desc = $doc_child->nodeValue;
break;
default:
break;
}
}
if (isset($err_no)) {
throw new ClockworkException($err_desc, $err_no);
}
return isset($cust_id);
}
/**
* Make an HTTP POST to Clockwork
*
* @param string method Clockwork method to call (sms/credit)
* @param string data Content of HTTP POST
*
* @return string Response from Clockwork
* @author Martin Steel
*/
protected function postToClockwork($method, $data) {
if ($this->log) {
$this->logXML("API $method Request XML", $data);
}
if( isset( $this->ssl ) ) {
$ssl = $this->ssl;
} else {
$ssl = $this->sslSupport();
}
$url = $ssl ? 'https://' : 'http://';
$url .= self::API_BASE_URL . $method;
$response = $this->xmlPost($url, $data);
if ($this->log) {
$this->logXML("API $method Response XML", $response);
}
return $response;
}
/**
* Make a HTTP POST
*
* cURL will be used if available, otherwise tries the PHP stream functions
*
* @param string url URL to send to
* @param string data Data to POST
* @return string Response returned by server
* @author Martin Steel
*/
protected function xmlPost($url, $data) {
if(extension_loaded('curl')) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-Type: text/xml"));
curl_setopt($ch, CURLOPT_USERAGENT, 'Clockwork PHP Wrapper/1.0' . self::VERSION);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
if (isset($this->proxy_host) && isset($this->proxy_port)) {
curl_setopt($ch, CURLOPT_PROXY, $this->proxy_host);
curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxy_port);
}
$response = curl_exec($ch);
$info = curl_getinfo($ch);
if ($response === false || $info['http_code'] != 200) {
throw new Exception('HTTP Error calling Clockwork API - HTTP Status: ' . $info['http_code'] . ' - cURL Erorr: ' . curl_error($ch));
} elseif (curl_errno($ch) > 0) {
throw new Exception('HTTP Error calling Clockwork API - cURL Error: ' . curl_error($ch));
}
curl_close($ch);
return $response;
} elseif (function_exists('stream_get_contents')) {
// Enable error Track Errors
$track = ini_get('track_errors');
ini_set('track_errors',true);
$params = array('http' => array(
'method' => 'POST',
'header' => "Content-Type: text/xml\r\nUser-Agent: mediaburst PHP Wrapper/" . self::VERSION . "\r\n",
'content' => $data
));
if (isset($this->proxy_host) && isset($this->proxy_port)) {
$params['http']['proxy'] = 'tcp://'.$this->proxy_host . ':' . $this->proxy_port;
$params['http']['request_fulluri'] = True;
}
$ctx = stream_context_create($params);
$fp = @fopen($url, 'rb', false, $ctx);
if (!$fp) {
ini_set('track_errors',$track);
throw new Exception("HTTP Error calling Clockwork API - fopen Error: $php_errormsg");
}
$response = @stream_get_contents($fp);
if ($response === false) {
ini_set('track_errors',$track);
throw new Exception("HTTP Error calling Clockwork API - stream Error: $php_errormsg");
}
ini_set('track_errors',$track);
return $response;
} else {
throw new Exception("Clockwork requires PHP5 with cURL or HTTP stream support");
}
}
/**
* Does the server/HTTP wrapper support SSL
*
* This is a best guess effort, some servers have weird setups where even
* though cURL is compiled with SSL support is still fails to make
* any requests.
*
* @return bool True if SSL is supported
* @author Martin Steel
*/
protected function sslSupport() {
$ssl = false;
// See if PHP is compiled with cURL
if (extension_loaded('curl')) {
$version = curl_version();
$ssl = ($version['features'] & CURL_VERSION_SSL) ? true : false;
} elseif (extension_loaded('openssl')) {
$ssl = true;
}
return $ssl;
}
/**
* Log some XML, tidily if possible, in the PHP error log
*
* @param string log_msg The log message to prepend to the XML
* @param string xml An XML formatted string
*
* @return void
* @author Martin Steel
*/
protected function logXML($log_msg, $xml) {
// Tidy if possible
if (class_exists('tidy')) {
$tidy = new tidy;
$config = array(
'indent' => true,
'input-xml' => true,
'output-xml' => true,
'wrap' => 200
);
$tidy->parseString($xml, $config, 'utf8');
$tidy->cleanRepair();
$xml = $tidy;
}
// Output
error_log("Clockwork $log_msg: $xml");
}
/**
* Check if an array is associative
*
* @param array $array Array to check
* @return bool
* @author Martin Steel
*/
protected function is_assoc($array) {
return (bool)count(array_filter(array_keys($array), 'is_string'));
}
/**
* Check if a number is a valid MSISDN
*
* @param string $val Value to check
* @return bool True if valid MSISDN
* @author James Inman
* @since 1.3.0
* @todo Take an optional country code and check that the number starts with it
*/
public static function is_valid_msisdn($val) {
return preg_match( '/^[1-9][0-9]{7,12}$/', $val );
}
}