功能模組
- Token 驗證:驗證微信伺服器簽名,通過後回覆 `echostr` 完成接入驗證。
- 訊息解析:將 POST 過來的 XML 轉為陣列,支援文字、圖片、語音、視頻、地理位置、連結等類型。
- 回覆組裝:支援文字、圖文、音樂、轉發客服等回覆格式,自動加入 CDATA 區塊。
- XML 雙向轉換:陣列 ↔ XML,處理特殊字元與巢狀結構。
完整類程式碼
class Wechat {
private $data = [];
public function __construct($token) {
$this->auth($token) || exit('Invalid signature');
if (IS_GET) { echo $_GET['echostr']; exit; }
$xml = file_get_contents('php://input');
$xmlObj = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
if (!$xmlObj) exit('Invalid XML');
foreach ($xmlObj as $k => $v) $this->data[$k] = strval($v);
}
public function getRequest() { return $this->data; }
// 統一回覆入口
public function response($content, $type = 'text', $flag = 0) {
$this->data = [
'ToUserName' => $this->data['FromUserName'],
'FromUserName' => $this->data['ToUserName'],
'CreateTime' => time(),
'MsgType' => $type,
];
$this->$type($content);
$this->data['FuncFlag'] = $flag;
echo $this->toXml($this->data);
exit;
}
// 各類型回覆組裝
private function text($c) { $this->data['Content'] = $c; }
private function music($m) { list($this->data['Music']['Title'], $this->data['Music']['Description'], $this->data['Music']['MusicUrl'], $this->data['Music']['HQMusicUrl']) = $m; }
private function news($n) {
$articles = [];
foreach ($n as $k => $v) {
if ($k >= 10) break; // 微信限制 10 條
list($articles[$k]['Title'], $articles[$k]['Description'], $articles[$k]['PicUrl'], $articles[$k]['Url']) = $v;
}
$this->data['ArticleCount'] = count($articles);
$this->data['Articles'] = $articles;
}
private function transfer_customer_service($c) { $this->data['Content'] = ''; }
// 陣列轉 XML (含 CDATA 處理)
private function toXml($data, $item = 'item') {
$xml = new SimpleXMLElement('');
$this->arrayToXml($xml, $data, $item);
return $xml->asXML();
}
private function arrayToXml($xml, $data, $item) {
foreach ($data as $k => $v) {
is_numeric($k) && $k = $item;
if (is_array($v) || is_object($v)) {
$child = $xml->addChild($k);
$this->arrayToXml($child, $v, $item);
} else {
$child = $xml->addChild($k);
$dom = dom_import_simplexml($child);
$dom->appendChild($dom->ownerDocument->createCDATASection($v));
}
}
}
// Token 驗證
private function auth($token) {
$sig = $_GET['signature']; $ts = $_GET['timestamp']; $nc = $_GET['nonce'];
$tmp = [$token, $ts, $nc];
sort($tmp, SORT_STRING);
return sha1(implode($tmp)) === $sig;
}
}
使用範例
// index.php 入口
require 'Wechat.class.php';
$wc = new Wechat('YOUR_TOKEN');
// 簡單關鍵字回覆
$req = $wc->getRequest();
if ($req['MsgType'] === 'text') {
switch ($req['Content']) {
case 'help':
$wc->response("歡迎使用!回覆 'news' 看圖文。");
break;
case 'news':
$wc->response([
['標題1', '描述1', 'https://pic.url/1.jpg', 'https://link.url/1'],
['標題2', '描述2', 'https://pic.url/2.jpg', 'https://link.url/2'],
], 'news');
break;
default:
$wc->response('收到:' . $req['Content']);
}
}
進階應用建議
- 結合 **快取** (Redis/Memcached) 儲存 `access_token`、`jsapi_ticket`。
- 使用 **隊列** (Redis/RabbitMQ) 處理耗時任務 (如發送模板訊息)。
- 介接 **客服系統**:收到使用者訊息先轉發客服 (`transfer_customer_service`),再由人工或機器人介入。
- 記錄 **Log**:所有請求/回覆 XML 寫入日誌,便於除錯與稽核。