будь добр, это мой первый вопрос, и мой английский не очень хорош.
У меня нет проблем при работе со стандартными сообщениями от Nexmo API, и я хочу получать длинные SMS, как и со стандартными (т. Е. В одном блоке).
Пример данных, полученных от Nexmo для стандартного SMS:
$_GET['msisdn'] ==> "33612345678" // "from"$_GET['to'] ==> "33687654321"$_GET['messageId'] ==> "02000000478EBE09"$_GET['text'] ==> "Hello world!"$_GET['type'] ==> "unicode"$_GET['keyword'] ==> "HELLO"$_GET['message-timestamp'] ==> "2014-11-25 14:06:58"
Длинный: (Nexmo отправил его по частям)
$_GET['msisdn'] ==> "33612345678" // "from"$_GET['to'] ==> "33687654321"$_GET['messageId'] ==> "02000000478EBE09"$_GET['text'] ==> "the first part of a too long text..."$_GET['type'] ==> "unicode"$_GET['keyword'] ==> "THE"$_GET['message-timestamp'] ==> "2014-11-25 12:06:58"$_GET['concat'] ==> "true"$_GET['concat-ref'] ==> "108" // unique identifier for long SMS text
$_GET['concat-total'] ==> "4" // or more, or less...
$_GET['concat-part'] ==> "1" // index of the part, start at 1
Подробнее о документации Nexmo: Вот
Итак, я начал с библиотеки, найденной на github (Nexmo-PHP Пб) и сделал это:
(довольно некрасиво, но это для целей теста)
public function inboundText( $data=null ){
if(!$data) $data = $_GET;
if(!isset($data['text'], $data['msisdn'], $data['to']))
return false;
if(isset($data['concat']) && $data['concat'])
{
session_start();
if ($data['concat-part'] > 1) // first part ?
{
if ($data['concat-total'] == $data['concat-part']) // last part ?
{
// last part ! stock the data in the text and unset now useless $_SESSION entry!
$data['text'] = $_SESSION[(string)$data['concat-ref']] . $data['text'];
unset($_SESSION[(string)$data['concat-ref']]);
}
else // not the first or the last, so concat !
{
// concat the new part in the entry named after the concat-ref
$_SESSION[(string)$data['concat-ref']] .= $data['text'];
return false;
}
}
else // first part ! so creat a $_SESSION entry for that! (named after concat-ref)
{
$_SESSION[(string)$data['concat-ref']] = $data['text'];
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;
}
Он отлично работает с локальным тестом, но не когда он размещен на моем сервере heroku, кажется, что массив $ _SESSION сбрасывается в каждой части SMS-сообщения …
Итак, у вас есть идея, как правильно с этим справиться? (и без уродливой временной таблицы SQL).
Как я могу получить предыдущую часть сообщения, пока не получу его полностью?
Использование сеанса для хранения временных частей SMS будет работать только в том случае, если между клиентом и сервером происходит обмен ключами с каждым HTTP-запросом, который идентифицирует сеанс.
В PHP, когда вы создаете сеанс и сохраняете значение внутри $_SESSION
на сервере создается файл для хранения этих данных (если только вы не используете обработчик сеанса БД, в этом случае данные сеанса хранятся в базе данных). На эти данные ссылается идентификатор (например, PHPSESSID=66dda288eb1947843c2341b4e470fa28
), который обычно предоставляется клиенту в виде файла cookie. Когда клиент возвращается при следующем HTTP-запросе, идентификатор возвращается на сервер в виде значения cookie, и сервер использует его для ссылки на те же данные сеанса.
Хотя ваш сервер может предоставлять cookie клиенту Nexmo при подключении к URL-адресу вашей конечной точки, Nexmo, вероятно, не хранит cookie и не возвращает его при следующем запросе. (Это предположение с моей стороны, но я думаю, что оно безопасное, хотя я не могу объяснить, почему оно работает на вашей локальной машине, но не на Heroku. В любом случае, это легко проверить — просто проверьте, предоставляет ли клиент Nexmo любой $_COOKIE
значение при последующих запросах).
Итог: если клиент Nexmo не использует куки для сохранения состояния между запросами, вы не можете использовать сеансы для сохранения временных частей SMS.
В любом случае, лучшим вариантом (поскольку временные сохраненные части сообщения сохранятся в случае перезагрузки сервера) будет сохранение каждой временной части в небольшой таблице базы данных. (Не волнуйтесь, это красивая временная таблица SQL.;) Вот пример использования MySQL:
CREATE TABLE `response_concat_tbl` (
`concat-ref` SMALLINT NOT NULL DEFAULT '0',
`concat-total` TINYINT UNSIGNED NOT NULL DEFAULT '0',
`concat-part` TINYINT UNSIGNED NOT NULL DEFAULT '0',
`text` VARCHAR(160) NOT NULL DEFAULT '',
`added_datetime` DATETIME DEFAULT NULL,
UNIQUE KEY `concat-ref` (`concat-ref`, `concat-part`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='A place to temporarily store multipart SMS messages before they are combined and saved as a whole response.';
Здесь я определил UNIQUE KEY
так что мы можем избежать дублирования частей сообщения с REPLACE INTO
пункт (в случае, если клиент Nexmo пытается отправить одну и ту же часть дважды). added_datetime
может использоваться для очистки потерянных сообщений в будущем (в случае, если окончательная часть сообщения не будет получена).
Теперь давайте вставим пример данных:
REPLACE INTO response_concat_tbl (`concat-ref`,`concat-total`,`concat-part`,text,added_datetime) VALUES ('101','4','1','This is',NOW());
REPLACE INTO response_concat_tbl (`concat-ref`,`concat-total`,`concat-part`,text,added_datetime) VALUES ('101','4','2','a multipart',NOW());
REPLACE INTO response_concat_tbl (`concat-ref`,`concat-total`,`concat-part`,text,added_datetime) VALUES ('101','4','4','for you!',NOW());
REPLACE INTO response_concat_tbl (`concat-ref`,`concat-total`,`concat-part`,text,added_datetime) VALUES ('101','4','3','sms message',NOW());
Теперь мы можем использовать MySQL GROUP_CONCAT
функция, чтобы захватить все части в одном запросе.
SET SESSION group_concat_max_len = 1000000;
SELECT GROUP_CONCAT(text ORDER BY `concat-part` ASC SEPARATOR ' ') AS text
FROM response_concat_tbl
WHERE `concat-ref` = '101'
GROUP BY `concat-ref`;
Мы устанавливаем group_concat_max_len
таким образом, общая длина строки может быть длиннее 1024 символов по умолчанию (хотя это уже много сообщений). Вот результат:
+-------------------------------------------------------------+
| GROUP_CONCAT(text ORDER BY `concat-part` ASC SEPARATOR ' ') |
+-------------------------------------------------------------+
| This is a multipart sms message for you! |
+-------------------------------------------------------------+
Если вы не используете MySQL, вам может потребоваться выполнить немного больше работы (некоторая проверка на дублирование, а затем цикл) без REPLACE INTO
а также GROUP_CONCAT
,
Вот полный рабочий пример использования этой техники:
class SMS
{
static public function processMultipart($sms)
{
$db =& DB::getInstance();
if (isset($sms['concat']) && $sms['concat']) {
// This sms is part of a multipart message, save it temporarily to the db.
$sms = array_map('trim', $sms);
$db->query("REPLACE INTO response_concat_tbl (
`concat-ref`,
`concat-total`,
`concat-part`,
`text`,
`added_datetime`
) VALUES (
'" . $db->escapeString($sms['concat-ref']) . "',
'" . $db->escapeString($sms['concat-total']) . "',
'" . $db->escapeString($sms['concat-part']) . "',
'" . $db->escapeString($sms['text']) . "',
NOW()
)
");
if ($sms['concat-total'] > $sms['concat-part']) {
// Not all the parts have been received; return false to signal the fact we don't have a complete message yet.
return false;
}
// Otherwise, it means the last part has just been received. Concatonate all the parts and return it.
// Increase the max length returned by MySQL's GROUP_CONCAT function.
$db->query("SET SESSION group_concat_max_len = 32000");
// Group the sms responses by concat-ref and return them as a concatonated string.
$qid = $db->query("SELECT GROUP_CONCAT(text ORDER BY `concat-part` ASC SEPARATOR ' ')
FROM response_concat_tbl
WHERE `concat-ref` = '" . $db->escapeString($sms['concat-ref']) . "'
GROUP BY `concat-ref`
");
list($sms['text']) = $db->fetch_row($qid);
// Delete the temporary records.
$db->query("DELETE FROM response_concat_tbl
WHERE `concat-ref` = '" . $db->escapeString($sms['concat-ref']) . "'
");
}
// If this is not a multipart message, the original sms data is returned. If it is a multipart message, we're returning the fully-concatonated message.
return $sms;
}
}
А вот как использовать эту функцию:
// False is returned here if we need to wait for additional parts to arrive. Otherwise, $sms is populated with the final, usable data.
if (false === $sms = SMS::processMultipart($_GET)) {
header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK', true, 200);
die('Waiting for additional message parts');
}
// Do something with $sms['text'], e.g.,
SMSResponse::save($sms['text']);
Других решений пока нет …