echo 輸出$GLOBALS['HTTP_RAW_POST_DATA'] 而非 $_POST?$_POST 只能解析 application/x-www-form-urlencoded 或 multipart/form-data 格式。微信推送的是 XML 數據包 (Content-Type: text/xml 或 application/xml),PHP 不會自動填充 $_POST。
$GLOBALS['HTTP_RAW_POST_DATA'] (PHP 5.6+ 棄用) 或 file_get_contents('php://input') (推薦) 可獲取原始 POST 數據體。
simplexml_load_string() 關鍵參數詳解$xmlObj = simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOCDATA);
SimpleXMLElement:返回對象類型,支援屬性式訪問 $obj->Content。LIBXML_NOCDATA:將 CDATA 節點內容直接作為文本節點,避免得到 SimpleXMLElement 對象需再轉字符串。<?php
define('TOKEN', 'weixin');
$wechat = new Wechat();
$wechat->valid();
class Wechat {
public function valid() {
$echoStr = $_GET['echostr'] ?? '';
// 首次驗證:原樣返回 echostr
if ($this->checkSignature() && $echoStr) {
echo $echoStr;
exit;
}
// 正常消息處理
$this->responseMsg();
}
private function checkSignature(): bool {
$signature = $_GET['signature'] ?? '';
$timestamp = $_GET['timestamp'] ?? '';
$nonce = $_GET['nonce'] ?? '';
$token = TOKEN;
$tmpArr = [$token, $timestamp, $nonce];
sort($tmpArr, SORT_STRING);
$tmpStr = implode($tmpArr);
$tmpStr = sha1($tmpStr);
return $tmpStr === $signature;
}
public function responseMsg() {
// 1. 獲取原始 XML
$postStr = file_get_contents('php://input'); // 推薦方式
if (empty($postStr)) {
exit('');
}
// 2. 解析 XML
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
if (!$postObj) {
error_log('XML 解析失敗: ' . $postStr);
exit('');
}
$fromUser = (string)$postObj->FromUserName;
$toUser = (string)$postObj->ToUserName;
$msgType = strtolower((string)$postObj->MsgType);
$content = trim((string)$postObj->Content);
$time = time();
// 3. 回復模板
$template = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>";
// 4. 業務邏輯
if ($msgType !== 'text') {
$replyType = 'text';
$replyContent = '我只能處理文本消息哦~';
} elseif (empty($content)) {
$replyType = 'text';
$replyContent = '請輸入一些內容吧!';
} else {
$replyType = 'text';
$replyContent = '你說了:' . $content;
}
// 5. 輸出回復
echo sprintf($template, $fromUser, $toUser, $time, $replyType, $replyContent);
}
}
?>
<?php
// ... valid() 與 checkSignature() 同前 ...
public function responseMsg() {
$postStr = file_get_contents('php://input');
if (empty($postStr)) exit('');
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$fromUser = (string)$postObj->FromUserName;
$toUser = (string)$postObj->ToUserName;
$msgType = strtolower((string)$postObj->MsgType);
$time = time();
$template = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>";
if ($msgType !== 'image') {
$replyType = 'text';
$replyContent = '請發送圖片消息。';
} else {
$picUrl = (string)$postObj->PicUrl;
$mediaId = (string)$postObj->MediaId;
if (empty($picUrl)) {
$replyContent = '未獲取到圖片鏈接。';
} else {
$replyContent = "收到圖片!\n鏈接:{$picUrl}\nMediaId:{$mediaId}";
}
$replyType = 'text';
}
echo sprintf($template, $fromUser, $toUser, $time, $replyType, $replyContent);
}
?>
| 消息類型 | 關鍵節點 |
|---|---|
| 文本 | Content |
| 圖片 | PicUrl, MediaId |
| 語音 | MediaId, Format, Recognition (語音識別結果) |
| 視頻 | MediaId, ThumbMediaId |
| 地理位置 | Location_X, Location_Y, Scale, Label |
| 鏈接 | Title, Description, Url |
| 事件 | Event (subscribe/unsubscribe/CLICK/VIEW/SCAN/LOCATION...), EventKey |
checkSignature() 必須在處理前調用,防止偽造請求。WXBizMsgCrypt 解密/加密。MsgId 防止重複處理。掌握 XML 解析、回復模板、業務分發這三大核心,即可快速構建微信公眾號自動回復機器人,進階可接入圖靈/NLP/客服系統實現智能對話。