mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-04-23 20:32:14 +09:00
Refactor SMS classes to use a common spec format
This commit is contained in:
parent
af89fc432a
commit
582374295d
4 changed files with 272 additions and 160 deletions
|
|
@ -12,6 +12,11 @@ abstract class Base implements \Rhymix\Framework\Drivers\SMSInterface
|
|||
*/
|
||||
protected $_config = null;
|
||||
|
||||
/**
|
||||
* The driver specification is stored here.
|
||||
*/
|
||||
protected static $_spec = array();
|
||||
|
||||
/**
|
||||
* Direct invocation of the constructor is not permitted.
|
||||
*/
|
||||
|
|
@ -21,7 +26,7 @@ abstract class Base implements \Rhymix\Framework\Drivers\SMSInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the current mail driver, using the given settings.
|
||||
* Create a new instance of the current SMS driver, using the given settings.
|
||||
*
|
||||
* @param array $config
|
||||
* @return object
|
||||
|
|
@ -32,7 +37,7 @@ abstract class Base implements \Rhymix\Framework\Drivers\SMSInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the human-readable name of this mail driver.
|
||||
* Get the human-readable name of this SMS driver.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
|
@ -42,7 +47,7 @@ abstract class Base implements \Rhymix\Framework\Drivers\SMSInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the list of configuration fields required by this mail driver.
|
||||
* Get the list of configuration fields required by this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
|
@ -52,7 +57,7 @@ abstract class Base implements \Rhymix\Framework\Drivers\SMSInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the list of API types supported by this mail driver.
|
||||
* Get the list of API types supported by this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
|
@ -62,7 +67,17 @@ abstract class Base implements \Rhymix\Framework\Drivers\SMSInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if the current mail driver is supported on this server.
|
||||
* Get the spec for this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAPISpec()
|
||||
{
|
||||
return static::$_spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current SMS driver is supported on this server.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
|
|
@ -78,10 +93,10 @@ abstract class Base implements \Rhymix\Framework\Drivers\SMSInterface
|
|||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @param object $message
|
||||
* @param array $messages
|
||||
* @return bool
|
||||
*/
|
||||
public function send(\Rhymix\Framework\SMS $message)
|
||||
public function send(array $messages)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,29 @@ namespace Rhymix\Framework\Drivers\SMS;
|
|||
class CoolSMS extends Base implements \Rhymix\Framework\Drivers\SMSInterface
|
||||
{
|
||||
/**
|
||||
* Maximum length of an SMS.
|
||||
* API specifications.
|
||||
*/
|
||||
protected $_maxlength_sms = 90;
|
||||
|
||||
/**
|
||||
* Maximum length of an LMS.
|
||||
*/
|
||||
protected $_maxlength_lms = 2000;
|
||||
protected static $_spec = array(
|
||||
'max_recipients' => 1000,
|
||||
'sms_max_length' => 90,
|
||||
'sms_max_length_in_charset' => 'CP949',
|
||||
'lms_supported' => true,
|
||||
'lms_supported_country_codes' => array(0, 82),
|
||||
'lms_max_length' => 2000,
|
||||
'lms_max_length_in_charset' => 'CP949',
|
||||
'lms_subject_supported' => true,
|
||||
'lms_subject_max_length' => 40,
|
||||
'mms_supported' => true,
|
||||
'mms_supported_country_codes' => array(0, 82),
|
||||
'mms_max_length' => 2000,
|
||||
'mms_max_length_in_charset' => 'CP949',
|
||||
'mms_subject_supported' => true,
|
||||
'mms_subject_max_length' => 40,
|
||||
'image_allowed_types' => array('jpg', 'gif', 'png'),
|
||||
'image_max_dimensions' => array(2048, 2048),
|
||||
'image_max_filesize' => 300000,
|
||||
'delay_supported' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the list of configuration fields required by this mail driver.
|
||||
|
|
@ -27,143 +42,52 @@ class CoolSMS extends Base implements \Rhymix\Framework\Drivers\SMSInterface
|
|||
return array('api_key', 'api_secret', 'sender_key');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of API types supported by this mail driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAPITypes()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message.
|
||||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @param object $message
|
||||
* @param array $messages
|
||||
* @return bool
|
||||
*/
|
||||
public function send(\Rhymix\Framework\SMS $message)
|
||||
public function send(array $messages)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Initialize the sender.
|
||||
$sender = new \Nurigo\Api\Message($this->_config['api_key'], $this->_config['api_secret']);
|
||||
$status = true;
|
||||
|
||||
// Get the list of recipients.
|
||||
$recipients = $message->getRecipientsGroupedByCountry();
|
||||
|
||||
// Group the recipients by country code.
|
||||
foreach ($recipients as $country => $country_recipients)
|
||||
foreach ($messages as $i => $message)
|
||||
{
|
||||
// Merge recipients into groups of 1000.
|
||||
$country_recipients = array_map(function($chunk) {
|
||||
return implode(',', $chunk);
|
||||
}, array_chunk($country_recipients, 1000));
|
||||
|
||||
// Send to each set of merged recipients.
|
||||
foreach ($country_recipients as $recipient_number)
|
||||
$options = new \stdClass;
|
||||
$options->type = $message->type;
|
||||
$options->from = $message->from;
|
||||
$options->to = implode(',', $message->to);
|
||||
$options->text = $message->content ?: 'SMS';
|
||||
$options->charset = 'utf8';
|
||||
if ($message->delay && $message->delay > time())
|
||||
{
|
||||
// Populate the options object.
|
||||
$options = new \stdClass;
|
||||
$options->from = $message->getFrom();
|
||||
$options->to = $recipient_number;
|
||||
$options->charset = 'utf8';
|
||||
|
||||
// Determine when to send this message.
|
||||
if ($datetime = $message->getDelay())
|
||||
{
|
||||
if ($datetime > time())
|
||||
{
|
||||
$options->datetime = gmdate('YmdHis', $datetime + (3600 * 9));
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the message type based on the length.
|
||||
$content_full = $message->getContent();
|
||||
$detected_type = $message->checkLength($content_full, $this->_maxlength_sms) ? 'SMS' : 'LMS';
|
||||
$options->type = $detected_type;
|
||||
|
||||
// If the message has a subject, it must be an LMS.
|
||||
if ($subject = $message->getSubject())
|
||||
{
|
||||
$options->subject = $subject;
|
||||
$options->type = 'LMS';
|
||||
}
|
||||
|
||||
// If the message has an attachment, it must be an MMS.
|
||||
if ($attachments = $message->getAttachments())
|
||||
{
|
||||
$options->type = 'MMS';
|
||||
}
|
||||
|
||||
// If the recipient is not a Korean number, force SMS.
|
||||
if ($message->isForceSMS() || ($country > 0 && $country != 82))
|
||||
{
|
||||
unset($options->subject);
|
||||
$attachments = array();
|
||||
$options->country = $country;
|
||||
$options->type = 'SMS';
|
||||
$message->forceSMS();
|
||||
}
|
||||
|
||||
// Split the message if necessary.
|
||||
if ($options->type === 'SMS' && $detected_type !== 'SMS')
|
||||
{
|
||||
$content_split = $message->splitMessage($content_full, $this->_maxlength_sms);
|
||||
}
|
||||
elseif ($options->type !== 'SMS' && !$message->checkLength($content_full, $this->_maxlength_lms))
|
||||
{
|
||||
$content_split = $message->splitMessage($content_full, $this->_maxlength_lms);
|
||||
}
|
||||
else
|
||||
{
|
||||
$content_split = array($content_full);
|
||||
}
|
||||
|
||||
// Send all parts of the split message.
|
||||
$message_count = max(count($content_split), count($attachments));
|
||||
$last_content = 'MMS';
|
||||
for ($i = 1; $i <= $message_count; $i++)
|
||||
{
|
||||
// Get the message content.
|
||||
if ($content = array_shift($content_split))
|
||||
{
|
||||
$options->text = $last_content = $content;
|
||||
}
|
||||
else
|
||||
{
|
||||
$options->text = $last_content ?: 'MMS';
|
||||
}
|
||||
|
||||
// Get the attachment.
|
||||
if ($attachment = array_shift($attachments))
|
||||
{
|
||||
$options->image = $attachment->local_filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($options->image);
|
||||
}
|
||||
|
||||
// Determine the best message type for this combination of content and attachment.
|
||||
if (!$message->isForceSMS())
|
||||
{
|
||||
$options->type = $attachment ? 'MMS' : ($message->checkLength($content, $this->_maxlength_sms) ? 'SMS' : 'LMS');
|
||||
}
|
||||
|
||||
// Send the current part of the message.
|
||||
$result = $sender->send($options);
|
||||
if (!$result->success_count)
|
||||
{
|
||||
$error_codes = implode(', ', $result->error_list ?: array('Unknown'));
|
||||
$message->errors[] = 'Error (' . $error_codes . ') while sending message ' . $i . ' of ' . $message_count . ' to ' . $options->to;
|
||||
$status = false;
|
||||
}
|
||||
}
|
||||
$options->datetime = gmdate('YmdHis', $message->delay + (3600 * 9));
|
||||
}
|
||||
if ($message->country && $message->country != 82)
|
||||
{
|
||||
$options->country = $message->country;
|
||||
}
|
||||
if ($message->subject)
|
||||
{
|
||||
$options->subject = $message->subject;
|
||||
}
|
||||
if ($message->image)
|
||||
{
|
||||
$options->image = $message->image;
|
||||
}
|
||||
|
||||
$result = $sender->send($options);
|
||||
if (!$result->success_count)
|
||||
{
|
||||
$error_codes = implode(', ', $result->error_list ?: array('Unknown'));
|
||||
$message->errors[] = 'Error (' . $error_codes . ') while sending message ' . $i . ' of ' . count($messages) . ' to ' . $options->to;
|
||||
$status = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,13 @@ interface SMSInterface
|
|||
*/
|
||||
public static function getAPITypes();
|
||||
|
||||
/**
|
||||
* Get the spec for this SMS driver.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAPISpec();
|
||||
|
||||
/**
|
||||
* Check if the current SMS driver is supported on this server.
|
||||
*
|
||||
|
|
@ -50,8 +57,8 @@ interface SMSInterface
|
|||
*
|
||||
* This method returns true on success and false on failure.
|
||||
*
|
||||
* @param object $message
|
||||
* @param array $messages
|
||||
* @return bool
|
||||
*/
|
||||
public function send(\Rhymix\Framework\SMS $message);
|
||||
public function send(array $messages);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -467,7 +467,16 @@ class SMS
|
|||
{
|
||||
if ($this->driver)
|
||||
{
|
||||
$this->sent = $this->driver->send($this) ? true : false;
|
||||
$messages = $this->_formatSpec($this->driver->getAPISpec());
|
||||
if (count($messages))
|
||||
{
|
||||
$this->sent = $this->driver->send($messages) ? true : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->errors[] = 'No recipients selected';
|
||||
$this->sent = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -477,7 +486,7 @@ class SMS
|
|||
}
|
||||
catch(\Exception $e)
|
||||
{
|
||||
$this->errors[] = $e->getMessage();
|
||||
$this->errors[] = class_basename($e) . ': ' . $e->getMessage();
|
||||
$this->sent = false;
|
||||
}
|
||||
|
||||
|
|
@ -521,43 +530,200 @@ class SMS
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if a message is no longer than the given length.
|
||||
* Format the current message according to an API spec.
|
||||
*
|
||||
* This is useful when checking whether a message can fit into a single SMS.
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $maxlength
|
||||
* @param string $measure_in_charset (optional)
|
||||
* @return
|
||||
* @param array $spec API specifications
|
||||
* @return array
|
||||
*/
|
||||
public function checkLength($message, $maxlength, $measure_in_charset = 'CP949')
|
||||
protected function _formatSpec(array $spec)
|
||||
{
|
||||
$message = @iconv('UTF-8', $measure_in_charset . '//IGNORE', $message);
|
||||
return strlen($message) <= $maxlength;
|
||||
// Initialize the return array.
|
||||
$result = array();
|
||||
|
||||
// Get the list of recipients.
|
||||
$recipients = $this->getRecipientsGroupedByCountry();
|
||||
|
||||
// Group the recipients by country code.
|
||||
foreach ($recipients as $country_code => $country_recipients)
|
||||
{
|
||||
// Merge recipients into groups.
|
||||
if ($spec['max_recipients'] > 1)
|
||||
{
|
||||
$country_recipients = array_chunk($country_recipients, $spec['max_recipients']);
|
||||
}
|
||||
|
||||
// Send to each set of merged recipients.
|
||||
foreach ($country_recipients as $recipient_numbers)
|
||||
{
|
||||
// Populate the item.
|
||||
$item = new \stdClass;
|
||||
$item->type = 'SMS';
|
||||
$item->from = $this->getFrom();
|
||||
$item->to = $recipient_numbers;
|
||||
$item->country = $country_code;
|
||||
if ($spec['delay_supported'])
|
||||
{
|
||||
$item->delay = $this->getDelay() ?: 0;
|
||||
}
|
||||
|
||||
// Get message content.
|
||||
$subject = $this->getSubject();
|
||||
$content = $this->getContent();
|
||||
$attachments = $attachments = $this->getAttachments();
|
||||
$last_content = 'MMS';
|
||||
|
||||
// Determine the message type.
|
||||
if (!$this->isForceSMS() && ($spec['lms_supported'] || $spec['mms_supported']))
|
||||
{
|
||||
// Check attachments, subject, and message length.
|
||||
if ($spec['mms_supported'] && count($attachments))
|
||||
{
|
||||
$item->type = 'MMS';
|
||||
}
|
||||
elseif ($spec['lms_supported'] && $subject)
|
||||
{
|
||||
$item->subject = $subject;
|
||||
$item->type = 'LMS';
|
||||
}
|
||||
elseif ($spec['lms_supported'] && $this->_getLengthInCharset($content, $spec['sms_max_length_in_charset']) > $spec['sms_max_length'])
|
||||
{
|
||||
$item->type = 'LMS';
|
||||
}
|
||||
else
|
||||
{
|
||||
$item->type = 'SMS';
|
||||
}
|
||||
|
||||
// Check the country code.
|
||||
if ($item->type === 'MMS' && is_array($spec['mms_supported_country_codes']) && !in_array($country_code, $spec['mms_supported_country_codes']))
|
||||
{
|
||||
$item->type = 'LMS';
|
||||
}
|
||||
if ($item->type === 'LMS' && is_array($spec['lms_supported_country_codes']) && !in_array($country_code, $spec['lms_supported_country_codes']))
|
||||
{
|
||||
$item->type = 'SMS';
|
||||
}
|
||||
}
|
||||
|
||||
// Remove subject and attachments if the message type is SMS.
|
||||
if ($item->type === 'SMS')
|
||||
{
|
||||
if ($item->subject)
|
||||
{
|
||||
$content = $item->subject . "\n" . $content;
|
||||
unset($item->subject);
|
||||
}
|
||||
$attachments = array();
|
||||
}
|
||||
|
||||
// If message subject is not supported, prepend it to the content instead.
|
||||
if ($item->subject && !$spec[strtolower($item->type) . '_subject_supported'])
|
||||
{
|
||||
$content = $item->subject . "\n" . $content;
|
||||
unset($item->subject);
|
||||
}
|
||||
elseif ($item->subject && $this->_getLengthInCharset($item->subject, $spec[strtolower($item->type) . '_max_length_in_charset']) > $spec[strtolower($item->type) . '_subject_max_length'])
|
||||
{
|
||||
$subject_parts = $this->_splitString($item->subject, $spec[strtolower($item->type) . '_subject_max_length'], $spec[strtolower($item->type) . '_max_length_in_charset']);
|
||||
$subject_short = array_shift($subject_parts);
|
||||
$subject_remainder = utf8_trim(substr($item->subject, strlen($subject_short)));
|
||||
$item->subject = $subject_short;
|
||||
$content = $subject_remainder . "\n" . $content;
|
||||
}
|
||||
|
||||
// Split the content if necessary.
|
||||
if (($item->type === 'SMS' && $this->allow_split_sms) || ($item->type !== 'SMS' && $this->allow_split_lms))
|
||||
{
|
||||
if ($this->_getLengthInCharset($content, $spec[strtolower($item->type) . '_max_length_in_charset']) > $spec[strtolower($item->type) . '_max_length'])
|
||||
{
|
||||
$content_parts = $this->_splitString($content, $spec[strtolower($item->type) . '_max_length'], $spec[strtolower($item->type) . '_max_length_in_charset']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$content_parts = array($content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$content_parts = array($content);
|
||||
}
|
||||
|
||||
// Generate a message for each part of the content and attachments.
|
||||
$message_count = max(count($content_parts), count($attachments));
|
||||
for ($i = 1; $i <= $message_count; $i++)
|
||||
{
|
||||
// Get the message content.
|
||||
if ($content_part = array_shift($content_parts))
|
||||
{
|
||||
$item->content = $last_content = $content_part;
|
||||
}
|
||||
else
|
||||
{
|
||||
$item->content = $last_content ?: 'MMS';
|
||||
}
|
||||
|
||||
// Get the attachment.
|
||||
if ($attachment = array_shift($attachments))
|
||||
{
|
||||
$item->image = $attachment->local_filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($item->image);
|
||||
}
|
||||
|
||||
// Clone the item to make a part.
|
||||
$cloneitem = clone $item;
|
||||
|
||||
// Determine the best message type for this part.
|
||||
if ($cloneitem->type !== 'SMS')
|
||||
{
|
||||
$cloneitem->type = $attachment ? 'MMS' : ($this->_getLengthInCharset($content_part, $spec['sms_max_length_in_charset']) > $spec['sms_max_length'] ? 'LMS' : 'SMS');
|
||||
}
|
||||
|
||||
// Add the cloned part to the result array.
|
||||
$result[] = $cloneitem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the message parts.
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a message into several short messages.
|
||||
* Get the length of a string in another character set.
|
||||
*
|
||||
* This is useful when sending a long message as a series of SMS.
|
||||
* @param string $str String to measure
|
||||
* @param string $charset Character set to measure length
|
||||
* @return
|
||||
*/
|
||||
protected function _getLengthInCharset($str, $charset)
|
||||
{
|
||||
$str = @iconv('UTF-8', $charset . '//IGNORE', $str);
|
||||
return strlen($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a string into several short chunks.
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $maxlength
|
||||
* @param string $measure_in_charset (optional)
|
||||
* @param string $str String to split
|
||||
* @param int $max_length Maximum length of a chunk
|
||||
* @param string $charset Character set to measure length
|
||||
* @return array
|
||||
*/
|
||||
public function splitMessage($message, $maxlength, $measure_in_charset = 'CP949')
|
||||
protected function _splitString($str, $max_length, $charset)
|
||||
{
|
||||
$message = utf8_trim(utf8_normalize_spaces($message, true));
|
||||
$chars = preg_split('//u', $message, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$str = utf8_trim(utf8_normalize_spaces($str, true));
|
||||
$chars = preg_split('//u', $str, -1, \PREG_SPLIT_NO_EMPTY);
|
||||
$result = array();
|
||||
$current_entry = '';
|
||||
$current_length = 0;
|
||||
|
||||
foreach ($chars as $char)
|
||||
{
|
||||
$char_length = strlen(@iconv('UTF-8', $measure_in_charset . '//IGNORE', $char));
|
||||
if ($current_length + $char_length > $maxlength)
|
||||
$char_length = strlen(@iconv('UTF-8', $charset . '//IGNORE', $char));
|
||||
if (($current_length + $char_length > $max_length) || ($current_length + $char_length > $max_length - 7 && ctype_space($char)))
|
||||
{
|
||||
$result[] = $current_entry;
|
||||
$current_entry = $char;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue