微信消息接口的目录为application\weixin\controller\Index.php。它是微信的开发者接口,用于收发用户发送给公众号的消息并自动回复。
微信消息接口的实现代码如下。
1 <?php
2 namespace app\weixin\controller;
3 use think\Controller;
4 use think\Db;
5
6 define("TOKEN", "fangbei");
7 class Index extends Controller
8 {
9 public function index{
10 if (!isset($_GET['echostr'])) {
11 $this->responseMsg;
12 }else{
13 $this->valid;
14 }
15 }
16
17 // 验证签名
18 public function valid
19 {
20 $echoStr = $_GET["echostr"];
21 $signature = $_GET["signature"];
22 $timestamp = $_GET["timestamp"];
23 $nonce = $_GET["nonce"];
24 $token = TOKEN;
25 $tmpArr = array($token, $timestamp, $nonce);
26 sort($tmpArr);
27 $tmpStr = implode($tmpArr);
28 $tmpStr = sha1($tmpStr);
29 if($tmpStr == $signature){
30 echo $echoStr;
31 exit;
32 }
33 }
34
35 // 响应
36 public function responseMsg
37 {
38 $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
39 if (!empty($postStr)){
40 $this->logger("R ".$postStr);
41 $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_
NOCDATA);
42 $RX_TYPE = trim($postObj->MsgType);
43
44 if (($postObj->MsgType == "event") && ($postObj->Event == "subscribe" ||
$postObj->Event == "unsubscribe" || $postObj->Event == "TEMPLATE-
SENDJOBFINISH")){
45 // 过滤关注和取消关注事件
46 }else{
47 // 更新互动记录
48 Db::name('user')->where('openid',strval($postObj->FromUserName))->
setField('heartbeat', time);
49 }
50 // 消息类型分离
51 switch ($RX_TYPE)
52 {
53 case "event":
54 $result = $this->receiveEvent($postObj);
55 break;
56 case "text":
57 $result = $this->receiveText($postObj);
58 break;
59 default:
60 $result = "unknown msg type: ".$RX_TYPE;
61 break;
62 }
63 $this->logger("T ".$result);
64 echo $result;
65 }else {
66 echo "";
67 exit;
68 }
69 }
70
71
72 // 接收事件消息
73 private function receiveEvent($object)
74 {
75 $weixin = new \weixin\Wxapi;
76 $openid = strval($object->FromUserName);
77 $content = "";
78
79 switch ($object->Event)
80 {
81 case "subscribe":
82 $info = $weixin->get_user_info($openid);
83 $municipalities = array("北京", "上海", "天津", "重庆", "香港",
"澳门");
84 $sexes = array("", "男", "女");
85 $data = array;
86 $data['openid'] = $openid;
87 $data['nickname'] = str_replace("'", "", $info['nickname']);
88 $data['sex'] = $sexes[$info['sex']];
89 $data['country'] = $info['country'];
90 $data['province'] = $info['province'];
91 $data['city'] = (in_array($info['province'], $municipalities))
?$info['province'] : $info['city'];
92 $data['scene'] = (isset($object->EventKey) && (stripos(strval
($object->EventKey),"qrscene_")))?str_replace("qrscene_","",$object
->EventKey):"0";
93
94 $data['headimgurl'] = $info['headimgurl'];
95 $data['subscribe'] = $info['subscribe_time'];
96 $data['heartbeat'] = time;
97 $data['remark'] = $info['remark'];
98 $data['score'] = 1;
99 $data['tagid'] = $info['tagid_list'];
100 Db::name('user')->insert($data);
101 $content = "欢迎关注,".$info['nickname'];
102 break;
103 case "unsubscribe":
104 db('user')->where('openid',$openid)->delete;
105 break;
106 case "CLICK":
107 switch ($object->EventKey)
108 {
109 default:
110 $content = "点击菜单:".$object->EventKey;
111 break;
112 }
113 break;
114 default:
115 $content = "";
116 break;
117 }
118 if(is_array($content)){
119 $result = $this->transmitNews($object, $content);
120 }else{
121 $result = $this->transmitText($object, $content);
122 }
123
124 return $result;
125 }
126
127 // 接收文本消息
128 private function receiveText($object)
129 {
130 $keyword = trim($object->Content);
131 $openid = strval($object->FromUserName);
132 $content = "";
133
134 if (strstr($keyword, "文本")){
135 $content = "这是个文本消息\n".$openid;
136 }else{
137 $content = date("Y-m-d H:i:s",time)."\n".$openid."技术支持 方倍工作室";
138 }
139
140 if(is_array($content)){
141 $result = $this->transmitNews($object, $content);
142 }else{
143 $result = $this->transmitText($object, $content);
144 }
145 return $result;
146 }
147
148 // 回复文本消息
149 private function transmitText($object, $content)
150 {
151 if (!isset($content) || empty($content)){
152 return "";
153 }
154 $xmlTpl = "<xml>
155 <ToUserName><![CDATA[%s]]></ToUserName>
156 <FromUserName><![CDATA[%s]]></FromUserName>
157 <CreateTime>%s</CreateTime>
158 <MsgType><![CDATA[text]]></MsgType>
159 <Content><![CDATA[%s]]></Content>
160 </xml>";
161 $result = sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time,
$content);
162 return $result;
163 }
164
165 // 回复图文消息
166 private function transmitNews($object, $newsArray)
167 {
168 if(!is_array($newsArray)){
169 return "";
170 }
171 $itemTpl = " <item>
172 <Title><![CDATA[%s]]></Title>
173 <Description><![CDATA[%s]]></Description>
174 <PicUrl><![CDATA[%s]]></PicUrl>
175 <Url><![CDATA[%s]]></Url>
176 </item>
177 ";
178 $item_str = "";
179 foreach ($newsArray as $item){
180 $item_str .= sprintf($itemTpl, $item['Title'], $item['Description'],
$item['PicUrl'], $item['Url']);
181 }
182 $xmlTpl = "<xml>
183 <ToUserName><![CDATA[%s]]></ToUserName>
184 <FromUserName><![CDATA[%s]]></FromUserName>
185 <CreateTime>%s</CreateTime>
186 <MsgType><![CDATA[news]]></MsgType>
187 <ArticleCount>%s</ArticleCount>
188 <Articles>
189 $item_str</Articles>
190 </xml>";
191
192 $result = sprintf($xmlTpl, $object->FromUserName, $object->ToUserName,
time, count($newsArray));
193 return $result;
194 }
195
196 // 日志记录
197 private function logger($log_content)
198 {
199 if(isset($_SERVER['HTTP_APPNAME'])){ // SAE
200 sae_set_display_errors(false);
201 sae_debug($log_content);
202 sae_set_display_errors(true);
203 }else if($_SERVER['REMOTE_ADDR'] != "127.0.0.2"){ // LOCAL
204 $max_size = 1000000;
205 $log_filename = "log.xml";
206 if(file_exists($log_filename) and (abs(filesize($log_filename)) > $max_size))
{unlink($log_filename);}
207 file_put_contents($log_filename, date('H:i:s')." ".$log_content."\
r\n", FILE_APPEND);
208 }
209 }
210 }
上述接口方法中,实现了微信的Token验证,事件、菜单和文本消息的接收,以及文本、图文消息的回复。
根据上述接口,配置微信开发者接口时,其接口为http://www.doucube.com/weixin/index/index,可以简化为http://www.doucube.com/weixin,Token则为fangbei。设置成功后的效果如图25-5所示。
图25-5 开发者接口配置成功
