1

親切にしてください、これは私の最初の質問で、私の英語はあまり上手ではありません。

Nexmo API からの標準メッセージを処理するのに問題はありません。標準メッセージと同じように (つまり、1 つのブロックで) 長い SMS を受信したいと考えています。

標準 SMS で Nexmo から受信したデータの例:

    $_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-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 サーバーでホストされている場合は機能しません。SMS の各部分で $_SESSION 配列がリセットされているようです...

それで、それを適切に処理する方法について何か考えがありますか?(そして醜い一時SQLテーブルなしで)。メッセージを完全に受信するまで、メッセージの前の部分を取得するにはどうすればよいですか?

4

1 に答える 1

3

セッションを使用して一時的な SMS パーツを保存することは、セッションを識別する各 HTTP 要求でクライアントとサーバーの間でキーの交換が行われる場合にのみ機能します。

PHP では、セッションを作成$_SESSIONしてファイル内に値を保存すると、このデータを保存するためにサーバー上に作成されます (DB セッション ハンドラーを使用している場合を除きます。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.';

ここで a を定義したUNIQUE KEYので、REPLACE INTO節を使用してメッセージ部分の重複を回避できます (Nexmo クライアントが同じ部分を 2 回送信しようとした場合)。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関数を使用して、1 つのクエリですべての部分を取得できます。

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']);
于 2014-11-26T14:37:51.913 に答える