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.
430 lines
11 KiB
430 lines
11 KiB
<?php
|
|
|
|
/**
|
|
* Class NexmoMessage handles the methods and properties of sending an SMS message.
|
|
*
|
|
* Usage: $var = new NexoMessage ( $account_key, $account_password );
|
|
* Methods:
|
|
* sendText ( $to, $from, $message, $unicode = null )
|
|
* sendBinary ( $to, $from, $body, $udh )
|
|
* pushWap ( $to, $from, $title, $url, $validity = 172800000 )
|
|
* displayOverview( $nexmo_response=null )
|
|
*
|
|
* inboundText ( $data=null )
|
|
* reply ( $text )
|
|
*
|
|
*
|
|
*/
|
|
|
|
class NexmoMessage {
|
|
|
|
// Nexmo account credentials
|
|
private $nx_key = '';
|
|
private $nx_secret = '';
|
|
|
|
/**
|
|
* @var string Nexmo server URI
|
|
*
|
|
* We're sticking with the JSON interface here since json
|
|
* parsing is built into PHP and requires no extensions.
|
|
* This will also keep any debugging to a minimum due to
|
|
* not worrying about which parser is being used.
|
|
*/
|
|
var $nx_uri = 'https://rest.nexmo.com/sms/json';
|
|
|
|
|
|
/**
|
|
* @var array The most recent parsed Nexmo response.
|
|
*/
|
|
private $nexmo_response = '';
|
|
|
|
|
|
/**
|
|
* @var bool If recieved an inbound message
|
|
*/
|
|
var $inbound_message = false;
|
|
|
|
|
|
// Current message
|
|
public $to = '';
|
|
public $from = '';
|
|
public $text = '';
|
|
public $network = '';
|
|
public $message_id = '';
|
|
|
|
// A few options
|
|
public $ssl_verify = false; // Verify Nexmo SSL before sending any message
|
|
|
|
|
|
function NexmoMessage ($api_key, $api_secret) {
|
|
$this->nx_key = $api_key;
|
|
$this->nx_secret = $api_secret;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Prepare new text message.
|
|
*
|
|
* If $unicode is not provided we will try to detect the
|
|
* message type. Otherwise set to TRUE if you require
|
|
* unicode characters.
|
|
*/
|
|
function sendText ( $to, $from, $message, $unicode=null ) {
|
|
|
|
// Making sure strings are UTF-8 encoded
|
|
if ( !is_numeric($from) && !mb_check_encoding($from, 'UTF-8') ) {
|
|
trigger_error('$from needs to be a valid UTF-8 encoded string');
|
|
return false;
|
|
}
|
|
|
|
if ( !mb_check_encoding($message, 'UTF-8') ) {
|
|
trigger_error('$message needs to be a valid UTF-8 encoded string');
|
|
return false;
|
|
}
|
|
|
|
if ($unicode === null) {
|
|
$containsUnicode = max(array_map('ord', str_split($message))) > 127;
|
|
} else {
|
|
$containsUnicode = (bool)$unicode;
|
|
}
|
|
|
|
// Make sure $from is valid
|
|
$from = $this->validateOriginator($from);
|
|
|
|
// URL Encode
|
|
$from = urlencode( $from );
|
|
$message = urlencode( $message );
|
|
|
|
// Send away!
|
|
$post = array(
|
|
'from' => $from,
|
|
'to' => $to,
|
|
'text' => $message,
|
|
'type' => $containsUnicode ? 'unicode' : 'text'
|
|
);
|
|
return $this->sendRequest ( $post );
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Prepare new WAP message.
|
|
*/
|
|
function sendBinary ( $to, $from, $body, $udh ) {
|
|
|
|
//Binary messages must be hex encoded
|
|
$body = bin2hex ( $body );
|
|
$udh = bin2hex ( $udh );
|
|
|
|
// Make sure $from is valid
|
|
$from = $this->validateOriginator($from);
|
|
|
|
// Send away!
|
|
$post = array(
|
|
'from' => $from,
|
|
'to' => $to,
|
|
'type' => 'binary',
|
|
'body' => $body,
|
|
'udh' => $udh
|
|
);
|
|
return $this->sendRequest ( $post );
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Prepare new binary message.
|
|
*/
|
|
function pushWap ( $to, $from, $title, $url, $validity = 172800000 ) {
|
|
|
|
// Making sure $title and $url are UTF-8 encoded
|
|
if ( !mb_check_encoding($title, 'UTF-8') || !mb_check_encoding($url, 'UTF-8') ) {
|
|
trigger_error('$title and $udh need to be valid UTF-8 encoded strings');
|
|
return false;
|
|
}
|
|
|
|
// Make sure $from is valid
|
|
$from = $this->validateOriginator($from);
|
|
|
|
// Send away!
|
|
$post = array(
|
|
'from' => $from,
|
|
'to' => $to,
|
|
'type' => 'wappush',
|
|
'url' => $url,
|
|
'title' => $title,
|
|
'validity' => $validity
|
|
);
|
|
return $this->sendRequest ( $post );
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Prepare and send a new message.
|
|
*/
|
|
private function sendRequest ( $data ) {
|
|
// Build the post data
|
|
$data = array_merge($data, array('username' => $this->nx_key, 'password' => $this->nx_secret));
|
|
$post = '';
|
|
foreach($data as $k => $v){
|
|
$post .= "&$k=$v";
|
|
}
|
|
|
|
// If available, use CURL
|
|
if (function_exists('curl_version')) {
|
|
|
|
$to_nexmo = curl_init( $this->nx_uri );
|
|
curl_setopt( $to_nexmo, CURLOPT_POST, true );
|
|
curl_setopt( $to_nexmo, CURLOPT_RETURNTRANSFER, true );
|
|
curl_setopt( $to_nexmo, CURLOPT_POSTFIELDS, $post );
|
|
|
|
if (!$this->ssl_verify) {
|
|
curl_setopt( $to_nexmo, CURLOPT_SSL_VERIFYPEER, false);
|
|
}
|
|
|
|
$from_nexmo = curl_exec( $to_nexmo );
|
|
curl_close ( $to_nexmo );
|
|
|
|
} elseif (ini_get('allow_url_fopen')) {
|
|
// No CURL available so try the awesome file_get_contents
|
|
|
|
$opts = array('http' =>
|
|
array(
|
|
'method' => 'POST',
|
|
'header' => 'Content-type: application/x-www-form-urlencoded',
|
|
'content' => $post
|
|
)
|
|
);
|
|
$context = stream_context_create($opts);
|
|
$from_nexmo = file_get_contents($this->nx_uri, false, $context);
|
|
|
|
} else {
|
|
// No way of sending a HTTP post :(
|
|
return false;
|
|
}
|
|
|
|
|
|
return $this->nexmoParse( $from_nexmo );
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Recursively normalise any key names in an object, removing unwanted characters
|
|
*/
|
|
private function normaliseKeys ($obj) {
|
|
// Determine is working with a class or araay
|
|
if ($obj instanceof stdClass) {
|
|
$new_obj = new stdClass();
|
|
$is_obj = true;
|
|
} else {
|
|
$new_obj = array();
|
|
$is_obj = false;
|
|
}
|
|
|
|
|
|
foreach($obj as $key => $val){
|
|
// If we come across another class/array, normalise it
|
|
if ($val instanceof stdClass || is_array($val)) {
|
|
$val = $this->normaliseKeys($val);
|
|
}
|
|
|
|
// Replace any unwanted characters in they key name
|
|
if ($is_obj) {
|
|
$new_obj->{str_replace('-', '', $key)} = $val;
|
|
} else {
|
|
$new_obj[str_replace('-', '', $key)] = $val;
|
|
}
|
|
}
|
|
|
|
return $new_obj;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse server response.
|
|
*/
|
|
private function nexmoParse ( $from_nexmo ) {
|
|
$response = json_decode($from_nexmo);
|
|
|
|
// Copy the response data into an object, removing any '-' characters from the key
|
|
$response_obj = $this->normaliseKeys($response);
|
|
|
|
if ($response_obj) {
|
|
$this->nexmo_response = $response_obj;
|
|
|
|
// Find the total cost of this message
|
|
$response_obj->cost = $total_cost = 0;
|
|
if (is_array($response_obj->messages)) {
|
|
foreach ($response_obj->messages as $msg) {
|
|
if (property_exists($msg, "messageprice")) {
|
|
$total_cost = $total_cost + (float)$msg->messageprice;
|
|
}
|
|
}
|
|
|
|
$response_obj->cost = $total_cost;
|
|
}
|
|
|
|
return $response_obj;
|
|
|
|
} else {
|
|
// A malformed response
|
|
$this->nexmo_response = array();
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate an originator string
|
|
*
|
|
* If the originator ('from' field) is invalid, some networks may reject the network
|
|
* whilst stinging you with the financial cost! While this cannot correct them, it
|
|
* will try its best to correctly format them.
|
|
*/
|
|
private function validateOriginator($inp){
|
|
// Remove any invalid characters
|
|
$ret = preg_replace('/[^a-zA-Z0-9]/', '', (string)$inp);
|
|
|
|
if(preg_match('/[a-zA-Z]/', $inp)){
|
|
|
|
// Alphanumeric format so make sure it's < 11 chars
|
|
$ret = substr($ret, 0, 11);
|
|
|
|
} else {
|
|
|
|
// Numerical, remove any prepending '00'
|
|
if(substr($ret, 0, 2) == '00'){
|
|
$ret = substr($ret, 2);
|
|
$ret = substr($ret, 0, 15);
|
|
}
|
|
}
|
|
|
|
return (string)$ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Display a brief overview of a sent message.
|
|
* Useful for debugging and quick-start purposes.
|
|
*/
|
|
public function displayOverview( $nexmo_response=null ){
|
|
$info = (!$nexmo_response) ? $this->nexmo_response : $nexmo_response;
|
|
|
|
if (!$nexmo_response ) return 'Cannot display an overview of this response';
|
|
|
|
// How many messages were sent?
|
|
if ( $info->messagecount > 1 ) {
|
|
|
|
$status = 'Your message was sent in ' . $info->messagecount . ' parts';
|
|
|
|
} elseif ( $info->messagecount == 1) {
|
|
|
|
$status = 'Your message was sent';
|
|
|
|
} else {
|
|
|
|
return 'There was an error sending your message';
|
|
}
|
|
|
|
// Build an array of each message status and ID
|
|
if (!is_array($info->messages)) $info->messages = array();
|
|
$message_status = array();
|
|
foreach ( $info->messages as $message ) {
|
|
$tmp = array('id'=>'', 'status'=>0);
|
|
|
|
if ( $message->status != 0) {
|
|
$tmp['status'] = $message->errortext;
|
|
} else {
|
|
$tmp['status'] = 'OK';
|
|
$tmp['id'] = $message->messageid;
|
|
}
|
|
|
|
$message_status[] = $tmp;
|
|
}
|
|
|
|
|
|
// Build the output
|
|
if (isset($_SERVER['HTTP_HOST'])) {
|
|
// HTML output
|
|
$ret = '<table><tr><td colspan="2">'.$status.'</td></tr>';
|
|
$ret .= '<tr><th>Status</th><th>Message ID</th></tr>';
|
|
foreach ($message_status as $mstat) {
|
|
$ret .= '<tr><td>'.$mstat['status'].'</td><td>'.$mstat['id'].'</td></tr>';
|
|
}
|
|
$ret .= '</table>';
|
|
|
|
} else {
|
|
|
|
// CLI output
|
|
$ret = "$status:\n";
|
|
|
|
// Get the sizes for the table
|
|
$out_sizes = array('id'=>strlen('Message ID'), 'status'=>strlen('Status'));
|
|
foreach ($message_status as $mstat) {
|
|
if ($out_sizes['id'] < strlen($mstat['id'])) {
|
|
$out_sizes['id'] = strlen($mstat['id']);
|
|
}
|
|
if ($out_sizes['status'] < strlen($mstat['status'])) {
|
|
$out_sizes['status'] = strlen($mstat['status']);
|
|
}
|
|
}
|
|
|
|
$ret .= ' '.str_pad('Status', $out_sizes['status'], ' ').' ';
|
|
$ret .= str_pad('Message ID', $out_sizes['id'], ' ')."\n";
|
|
foreach ($message_status as $mstat) {
|
|
$ret .= ' '.str_pad($mstat['status'], $out_sizes['status'], ' ').' ';
|
|
$ret .= str_pad($mstat['id'], $out_sizes['id'], ' ')."\n";
|
|
}
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* Inbound text methods
|
|
*/
|
|
|
|
|
|
/**
|
|
* Check for any inbound messages, using $_GET by default.
|
|
*
|
|
* This will set the current message to the inbound
|
|
* message allowing for a future reply() call.
|
|
*/
|
|
public function inboundText( $data=null ){
|
|
if(!$data) $data = $_GET;
|
|
|
|
if(!isset($data['text'], $data['msisdn'], $data['to'])) return false;
|
|
|
|
// Get the relevant data
|
|
$this->to = $data['to'];
|
|
$this->from = $data['msisdn'];
|
|
$this->text = $data['text'];
|
|
$this->network = (isset($data['network-code'])) ? $data['network-code'] : '';
|
|
$this->message_id = $data['messageId'];
|
|
|
|
// Flag that we have an inbound message
|
|
$this->inbound_message = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Reply the current message if one is set.
|
|
*/
|
|
public function reply ($message) {
|
|
// Make sure we actually have a text to reply to
|
|
if (!$this->inbound_message) {
|
|
return false;
|
|
}
|
|
|
|
return $this->sendText($this->from, $this->to, $message);
|
|
}
|
|
|
|
}
|
|
?>
|
|
|