下面結合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所示。