首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 微信開發:通過原始 XML 判斷用戶行為(訂閱/取消/發消息/點擊菜單)

     編輯:PHP 綜合知識

前言

微信將所有用戶交互(發送消息、關注/取消關注、點擊菜單、掃碼等)統一以 XML 格式 POST 到開發者 URL。關鍵在於解析 MsgType 與 Event 字段。本文通過實際 XML 示例,詳細講解如何精準判斷用戶行為,並附帶日誌記錄代碼,助力調試與審計。

核心判斷邏輯

public function responseMsg() {
    global $db;
    $postStr = $GLOBALS['HTTP_RAW_POST_DATA'];
    $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
    
    // 關鍵:記錄原始 XML 到文件,便於離線分析
    $this->writeLog('msg.txt', $postStr . "\n", 'a+');
    
    $msgType = $postObj->MsgType;  // 核心字段
    
    switch ($msgType) {
        case 'text':      // 用戶發送文本消息
            $this->handleText($postObj);
            break;
        case 'image':     // 用戶發送圖片
            $this->handleImage($postObj);
            break;
        case 'voice':     // 用戶發送語音
            $this->handleVoice($postObj);
            break;
        case 'event':     // 事件推送(關注、取消關注、菜單點擊等)
            $this->handleEvent($postObj);
            break;
        default:
            // 其他類型:video, location, link 等
            break;
    }
}

實際 XML 示例解析

1. 用戶發送文本消息

<xml>
  <ToUserName><![CDATA[gh_a64528aca3b]]></ToUserName>
  <FromUserName><![CDATA[oSgH_jveTxJSlFK_6QSiGeVaSyk]]></FromUserName>
  <CreateTime>1394705044</CreateTime>
  <MsgType><![CDATA[text]]></MsgType>
  <Content><![CDATA[ha]]></Content>
  <MsgId>5990212551746353512</MsgId>
</xml>

判斷:MsgType = text,獲取 Content 內容進行關鍵詞匹配或 NLP 處理。

2. 用戶關注(訂閱)公眾號

<xml>
  <ToUserName><![CDATA[gh_a5218aca3b]]></ToUserName>
  <FromUserName><![CDATA[oSgH_jveTxJSlFK_6QSiGeVyk]]></FromUserName>
  <CreateTime>1394706271</CreateTime>
  <MsgType><![CDATA[event]]></MsgType>
  <Event><![CDATA[subscribe]]></Event>
  <EventKey><![CDATA[]]></EventKey>
</xml>

判斷:MsgType = event 且 Event = subscribe。此時必須在 5 秒內回復歡迎語,否則微信會提示「該公眾號暫時無法提供服務」。

  • 掃帶參數二維碼關注:EventKey 為 qrscene_場景值,可用於渠道統計。
  • 普通關注:EventKey 為空。

3. 用戶取消關注

<xml>
  <MsgType><![CDATA[event]]></MsgType>
  <Event><![CDATA[unsubscribe]]></Event>
</xml>

判斷:Event = unsubscribe。無法直接回復消息,只能在業務層標記用戶狀態、清理綁定關係、記錄流失數據。

4. 用戶點擊自定義菜單

<xml>
  <MsgType><![CDATA[event]]></MsgType>
  <Event><![CDATA[CLICK]]></Event>
  <EventKey><![CDATA[MENU_KEY_1]]></EventKey>
</xml>

判斷:Event = CLICK,EventKey 為菜單創建時設置的 key,根據 key 分發業務邏輯。

5. 用戶點擊跳轉鏈接菜單

<xml>
  <MsgType><![CDATA[event]]></MsgType>
  <Event><![CDATA[VIEW]]></Event>
  <EventKey><![CDATA[https://example.com/page]]></EventKey>
  <MenuId>123456</MenuId>
</xml>

判斷:Event = VIEW,微信直接在內置瀏覽器打開鏈接,不會推送到開發者 URL(舊版本會推送,新版本不再推送)。

事件處理分發示例

private function handleEvent($postObj) {
    $event = strtolower($postObj->Event);
    $fromUsername = $postObj->FromUserName;
    $toUsername   = $postObj->ToUserName;
    $time         = time();
    
    $textTpl = "<xml><ToUserName><![CDATA[%s]]></ToUserName>...</xml>";
    
    switch ($event) {
        case 'subscribe':
            // 歡迎語 + 可選:獲取用戶信息入庫
            $content = '歡迎關注!我是您的智能助手。';
            // 如果是帶參數二維碼
            if (strpos($postObj->EventKey, 'qrscene_') === 0) {
                $sceneId = str_replace('qrscene_', '', $postObj->EventKey);
                $content .= "(來自渠道:{$sceneId})";
            }
            echo sprintf($textTpl, $fromUsername, $toUsername, $time, 'text', $content);
            break;
            
        case 'unsubscribe':
            // 標記用戶取消關注,不可回復消息
            $this->markUserUnsubscribed($fromUsername);
            echo 'success';
            break;
            
        case 'click':
            $key = $postObj->EventKey;
            $content = $this->handleMenuClick($key);  // 根據 key 返回內容
            echo sprintf($textTpl, $fromUsername, $toUsername, $time, 'text', $content);
            break;
            
        case 'scan':
            // 掃碼事件(已關注用戶掃帶參數二維碼)
            $sceneId = $postObj->EventKey;
            $content = "掃碼事件觸發,場景值:{$sceneId}";
            echo sprintf($textTpl, $fromUsername, $toUsername, $time, 'text', $content);
            break;
            
        default:
            echo 'success';
    }
}

日誌記錄工具函數

/**
 * 寫入日誌文件
 * @param string $filename 文件路徑
 * @param string $data 內容
 * @param string $mode 模式:a+ 追加寫入並讀取
 */
function writeLog($filename, $data, $mode = 'a+') {
    @touch($filename);
    $handle = @fopen($filename, $mode);
    if ($handle) {
        flock($handle, LOCK_EX);  // 獨佔鎖,防止併發寫入亂序
        fwrite($handle, $data);
        ftruncate($handle, strlen($data));  // 僅在 rb+ 模式下需要
        fclose($handle);
    }
}

/**
 * 文件打開模式速查:
 * 'r'  唯讀,指針至頭
 * 'r+' 讀寫,指針至頭
 * 'w'  唯寫,截斷/創建
 * 'w+' 讀寫,截斷/創建
 * 'a'  追加寫,指針至尾
 * 'a+' 追加讀寫,指針至尾
 */

最佳實踐建議

  1. 異步處理:耗時操作(入庫、外部 API 調用)放入隊列異步執行,主進程 5 秒內返回 success 或空字符串,避免微信重複推送。
  2. 去重機制:微信可能重複推送同一消息(MsgId 相同),用 Redis 記錄已處理 MsgId 並設置過期時間。
  3. 安全防護:啟用 libxml_disable_entity_loader(true) 防 XXE,驗證簽名確保消息來源可信。
  4. 監控告警:監控回調失敗率、響應延遲、異常峰值,及時發現服務器故障或微信接口變更。

延伸閱讀

深入掌握:PHP 微信開發技巧匯總、PHP 字符串用法總結、PHP+MySQL 數據庫操作入門 等專題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved