【问题标题】:How to handle long SMS text from Nexmo API properly?如何正确处理来自 Nexmo API 的长短信?
【发布时间】:2014-11-25 14:47:41
【问题描述】:

请客气,这是我的第一个问题,我的英语不是很好。

我可以毫无问题地处理来自 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 文档的更多信息:Here

所以,我从 github (Nexmo-PHP-lib) 上的一个库开始,然后这样做: (很丑,但它是为了测试目的)

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 数组似乎在短信的每个部分都被重置...

那么,您知道如何正确处理它吗? (并且没有丑陋的临时 SQL 表)。 在我完全收到之前,如何检索邮件的前一部分?

【问题讨论】:

    标签: php heroku sms vonage


    【解决方案1】:

    只有在客户端和服务器之间通过每个标识会话的 HTTP 请求交换密钥时,使用会话来存储临时 sms 部分才有效。

    在 PHP 中,当您创建会话并将值存储在 $_SESSION 中时,会在服务器上创建一个文件来存储此数据(除非您使用 DB 会话处理程序,在这种情况下会话数据存储在数据库)。此数据使用标识符(例如,PHPSESSID=66dda288eb1947843c2341b4e470fa28)引用,该标识符通常作为 cookie 提供给客户端。当客户端在下一个 HTTP 请求返回时,标识符作为 cookie 值返回给服务器,服务器使用它来引用相同的会话数据。

    虽然您的服务器在连接到您的端点 URL 时可能会向 Nexmo 客户端提供 cookie,但 Nexmo 可能不会存储 cookie 并在其下一个请求中返回它。 (这是我的一个假设,但我认为这是一个安全的假设——尽管我无法解释为什么它可以在您的本地计算机上运行,​​但在 Heroku 上却不行。无论如何,它很容易测试——只需检查 Nexmo 客户端是否提供后续请求期间的任何 $_COOKIE 值)。

    底线:如果 Nexmo 客户端不使用 cookie 来保存请求之间的状态,则不能使用会话来保存临时 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 INTOGROUP_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']);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-27
      相关资源
      最近更新 更多