讀古今文學網 > 微信公眾平台開發:從零基礎到ThinkPHP5高性能框架實踐 > 3.3.4 消息交互原理分析 >

3.3.4 消息交互原理分析

下面結合3.3.3節的代碼來分析微信公眾平台的消息交互原理。下面的代碼基於微信公眾平台官方示例代碼修改完善而成。


 1 <?php
 2 /*
 3     方倍工作室 http:// www.fangbei.org/
 4     CopyRight 2013 www.doucube.com  All Rights Reserved
 5 */
 6 
 7 define("TOKEN", "weixin");
 8 $wechatObj = new wechatCallbackapiTest;
 9 if (isset($_GET['echostr'])) {
10     $wechatObj->valid;
11 }else{
12     $wechatObj->responseMsg;
13 }
14 
15 class wechatCallbackapiTest
16 {
17     public function valid
18     {
19         $echoStr = $_GET["echostr"];
20         if($this->checkSignature){
21             echo $echoStr;
22             exit;
23         }
24     }
25 
26     private function checkSignature
27     {
28         $signature = $_GET["signature"];
29         $timestamp = $_GET["timestamp"];
30         $nonce = $_GET["nonce"];
31 
32         $token = TOKEN;
33         $tmpArr = array($token, $timestamp, $nonce);
34         sort($tmpArr);
35         $tmpStr = implode( $tmpArr );
36         $tmpStr = sha1( $tmpStr );
37 
38         if( $tmpStr == $signature ){
39             return true;
40         }else{
41             return false;
42         }
43     }
44 
45     public function responseMsg
46     {
47         $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
48 
49         if (!empty($postStr)){
50             $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_
               NOCDATA);
51             $fromUsername = $postObj->FromUserName;
52             $toUsername = $postObj->ToUserName;
53             $keyword = trim($postObj->Content);
54             $time = time;
55             $textTpl = "<xml>
56                         <ToUserName><![CDATA[%s]]></ToUserName>
57                         <FromUserName><![CDATA[%s]]></FromUserName>
58                         <CreateTime>%s</CreateTime>
59                         <MsgType><![CDATA[%s]]></MsgType>
60                         <Content><![CDATA[%s]]></Content>
61                         <FuncFlag>0</FuncFlag>
62                         </xml>";
63             if($keyword == "?" || $keyword == "?")
64             {
65                 $msgType = "text";
66                 $content = date("Y-m-d H:i:s",time);
67                 $result = sprintf($textTpl, $fromUsername, $toUsername, $time, 
                   $msgType, $content);
68                 echo $result;
69             }
70         }else{
71             echo "";
72             exit;
73         }
74     }
75 }
76 ?>
  

首先看一下代碼的結構。

第2~5行是註釋部分。

第7行使用define函數定義常量,常量名稱為TOKEN,常量的值為weixin,這個值就是在啟用開發模式時填寫的Token。

第15~75行定義了一個類wechatCallbackapiTest,並在類中定義了3個方法valid、checkSignature和responseMsg。

第8~13行為程序執行語句。第8行實例化了一個類對象。在第9行中,判斷是否有GET請求有echostr變量,如果有,則執行valid方法,否則執行responseMsg方法。

下面分析微信消息交互流程。

提交URL和Token申請驗證的時候,微信服務器將發送GET請求到填寫的URL上,並且帶上4個參數(signature、timestamp、nonce、echostr)。GET請求類似如下。


signature=6e35c6f3d3279338781047dbffd09426b9ecdee3&echostr=5979420653038092664&t
imestamp=1392001400&nonce=1392192345
  

上述請求的參數說明如表3-1所示。

表3-1 請求校驗參數說明

這個GET請求是包含echostr變量的,所以執行valid方法。在該方法中,又調用了校驗簽名方法checkSignature。如果簽名校驗為真,則原樣輸出變量$echoStr的值。

加密/校驗流程如下。

1)將token、timestamp、nonce等3個參數進行字典序排序,見第33~34行。

2)將3個參數字符串拼接成一個字符串進行sha1加密,見第35~36行。

3)開發者獲得加密後的字符串可與signature對比,標識該請求來源於微信,見第38~42行。

發送問號的時候,微信服務器也會帶上前面3個參數(signature、timestamp、nonce)訪問開發者設置的URL,同時還會將消息的XML數據包POST到URL上。XML格式類似如下。


<xml>
    <ToUserName><![CDATA[gh_ba6050bc0be7]]></ToUserName>
    <FromUserName><![CDATA[oDeOAjgSJUX10wvImSRMSwmyQAyA]]></FromUserName>
    <CreateTime>1392043637</CreateTime>
    <MsgType><![CDATA[text]]></MsgType>
    <Content><![CDATA[?]]></Content>
    <MsgId>5978781895719912033</MsgId>
</xml>
  

而消息請求不包含echostr變量,所以將執行響應消息方法responseMsg。

響應消息方法首先接收上述原始POST數據,見第47行。

然後它將數據載入對像中,對像名為SimpleXMLElement,LIBXML_NOCDATA表示將CDATA合併為文本節點,代碼中第50行實現此功能。

第51~54行取得XML類對象的值,並賦給新的變量,注意發送方變為接收方,接收方變為發送方。

第55~62行構造要回復的XML數據包。

第63行判斷發送過來的關鍵字是不是問號。

第64~65行設置回復的消息類型為text,內容為當前年月日時分秒。

第66~67行封裝回復的XML數據包,並且向微信服務器輸出。XML格式如下。


<xml>
    <ToUserName><![CDATA[oDeOAjgSJUX10wvImSRMSwmyQAyA]]></ToUserName>
    <FromUserName><![CDATA[gh_ba6050bc0be7]]></FromUserName>
    <CreateTime>1392043638</CreateTime>
    <MsgType><![CDATA[text]]></MsgType>
    <Content><![CDATA[2014-01-05 11:43:23]]></Content>
</xml>
  

這樣用戶就會收到回復的消息,效果如圖3-26所示。