You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

483 lines
16 KiB

6 years ago
  1. <?php
  2. namespace App\Common\Wechat;
  3. /**
  4. * 微信自定义菜单
  5. */
  6. class WechatMenu
  7. {
  8. //高级功能->开发者模式->获取
  9. private $app_id;
  10. private $app_secret;
  11. private $access_token;
  12. private $expires_in;
  13. public function __construct($app_id, $app_secret)
  14. {
  15. $this->app_id = $app_id;
  16. $this->app_secret = $app_secret;
  17. $token = $this->get_access_token();
  18. $this->access_token = $token['access_token'];
  19. $this->expires_in = $token['expires_in'];
  20. }
  21. /**
  22. * 获取授权access_token
  23. *
  24. */
  25. public function get_access_token()
  26. {
  27. $token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->app_id}&secret={$this->app_secret}";
  28. $token_data = $this->http($token_url);
  29. return json_decode($token_data, true);
  30. }
  31. /**
  32. * 自定义菜单创建
  33. * @param string $jsonmenu
  34. */
  35. public function create_menu($jsonmenu)
  36. {
  37. $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=".$this->access_token;
  38. return $this->http($url, $jsonmenu);
  39. }
  40. /**
  41. * 查询菜单
  42. * @param $access_token 已获取的ACCESS_TOKEN
  43. */
  44. public function getmenu($access_token)
  45. {
  46. # code...
  47. $url = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=".$this->access_token;
  48. $data = file_get_contents($url);
  49. return $data;
  50. }
  51. /**
  52. * 删除菜单
  53. * @param $access_token 已获取的ACCESS_TOKEN
  54. */
  55. public function delmenu($access_token)
  56. {
  57. # code...
  58. $url = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=".$this->access_token;
  59. $data = json_decode(file_get_contents($url),true);
  60. if ($data['errcode']==0)
  61. {
  62. # code...
  63. return true;
  64. }
  65. else
  66. {
  67. return false;
  68. }
  69. }
  70. /**
  71. * 获取最新5天关注用户发过来的消息,消息id,用户fakeid,昵称,消息内容
  72. *
  73. * 返回结构:id:msgId; fakeId; nickName; content;
  74. *
  75. * @return array
  76. */
  77. public function newmesg()
  78. {
  79. $url = 'https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&token='.$this->access_token.'&lang=zh_CN&count=50&rad='.rand(10000, 99999);
  80. $stream = $this->http($url);
  81. preg_match('/< type="json" id="json-msgList">(.*?)<\/>/is', $stream, $match);
  82. $json = json_decode($match[1], true);
  83. $returns = array();
  84. foreach ( $json as $val)
  85. {
  86. if ( $val['starred'] == '0')
  87. {
  88. $returns[] = $val;
  89. }
  90. }
  91. return $returns;
  92. }
  93. /**
  94. * 设置标记
  95. *
  96. * @param integer $msgId 消息标记
  97. * @return boolean
  98. */
  99. public function start($msgId)
  100. {
  101. $url = 'https://mp.weixin.qq.com/cgi-bin/setstarmessage?t=ajax-setstarmessage&rad='.rand(10000, 99999);
  102. $post = 'msgid='.$msgId.'&value=1&token='.$this->access_token.'&ajax=1';
  103. $stream = $this->http($url, $post);
  104. // 是不是设置成功
  105. $html = preg_replace("/^.*\{/is", "{", $stream);
  106. $json = json_decode($html, true);
  107. return (boolean)$json['msg'] == 'sys ok';
  108. }
  109. /**
  110. * 发送消息
  111. *
  112. * 结构 $param = array(fakeId, content, msgId);
  113. * @param array $param
  114. * @return boolean
  115. */
  116. public function sendmesg($param)
  117. {
  118. $url = 'https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response';
  119. $post = 'error=false&tofakeid='.$param['fakeId'].'&type=1&content='.$param['content'].'&quickreplyid='.$param['msgId'].'&token='.$this->access_token.'&ajax=1';
  120. $stream = $this->http($url, $post);
  121. $this->start($param['msgId']);
  122. // 是不是设置成功
  123. $html = preg_replace("/^.*\{/is", "{", $stream);
  124. $json = json_decode($html, true);
  125. return (boolean)$json['msg'] == 'ok';
  126. }
  127. /**
  128. * 主动发消息结构
  129. * $param = array(fakeId, content);
  130. * @param array $param
  131. * @return [type] [description]
  132. */
  133. public function send($param)
  134. {
  135. $url = 'https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response&lang=zh_CN';
  136. //$post = 'ajax=1&appmsgid='.$param['msgid'].'&error=false&fid='.$param['msgid'].'&tofakeid='.$param['fakeId'].'&token='.$this->access_token.'&type=10';
  137. $post = 'ajax=1&content='.$param['content'].'&error=false&tofakeid='.$param['fakeId'].'&token='.$this->access_token.'&type=1';
  138. $stream = $this->html($url, $post);
  139. // 是不是设置成功
  140. $html = preg_replace("/^.*\{/is", "{", $stream);
  141. $json = json_decode($html, true);
  142. return (boolean)$json['msg'] == 'ok';
  143. }
  144. /**
  145. * 批量发送(可能需要设置超时)
  146. * $param = array(fakeIds, content);
  147. * @param array $param
  148. * @return [type] [description]
  149. */
  150. public function batSend($param)
  151. { $url = 'https://mp.weixin.qq.com/cgi-bin/masssend?t=ajax-response';
  152. $post = 'ajax=1&city=&content='.$param['content'].'&country=&error=false&groupid='.$param['groupid'].'&needcomment=0&province=&sex=0&token='.$this->access_token.'&type=1';
  153. $stream = $this->html($url, $post);
  154. // 是不是设置成功
  155. $html = preg_replace("/^.*\{/is", "{", $stream);
  156. $json = json_decode($html, true);
  157. return (boolean)$json['msg'] == 'ok';
  158. }
  159. /**
  160. * 新建图文消息
  161. */
  162. public function setNews($param, $post_data)
  163. {
  164. $url = 'https://mp.weixin.qq.com/cgi-bin/sysnotify?lang=zh_CN&f=json&begin=0&count=5';
  165. $post = 'ajax=1&token='.$this->access_token.'';
  166. $stream = $this->html($url, $post);
  167. //上传图片
  168. $url = 'https://mp.weixin.qq.com/cgi-bin/uploadmaterial?cgi=uploadmaterial&type='.$param['type'].'&token='.$this->access_token.'&t=iframe-uploadfile&lang=zh_CN&formId=1';
  169. $stream = $this->_uploadFile($url, $post_data);
  170. echo '</pre>';
  171. print_r($stream);
  172. echo '</pre>';
  173. exit;
  174. }
  175. /**
  176. * 获得用户发过来的消息(消息内容和消息类型)
  177. */
  178. public function getMsg()
  179. {
  180. $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
  181. if ($this->debug) {
  182. $this->write_log($postStr);
  183. }
  184. if (!empty($postStr)) {
  185. $this->msg = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
  186. $this->msgtype = strtolower($this->msg['MsgType']);//获取用户信息的类型
  187. $this->eventkey = strtolower($this->msg['EventKey']);//获取key值
  188. }
  189. }
  190. /**
  191. * 回复文本消息
  192. * @param string $text
  193. * @return string
  194. */
  195. public function makeText($text='')
  196. {
  197. $createtime = time();
  198. $funcflag = $this->setFlag ? 1 : 0;
  199. $textTpl = "<xml>
  200. <ToUserName><![CDATA[{$this->msg['FromUserName']}]]></ToUserName>
  201. <FromUserName><![CDATA[{$this->msg['ToUserName']}]]></FromUserName>
  202. <CreateTime>{$createtime}</CreateTime>
  203. <MsgType><![CDATA[text]]></MsgType>
  204. <Content><![CDATA[%s]]></Content>
  205. <FuncFlag>%s</FuncFlag>
  206. </xml>";
  207. return sprintf($textTpl,$text,$funcflag);
  208. }
  209. /**
  210. * 回复图文消息
  211. * @param array $newsData
  212. * @return string
  213. */
  214. public function makeNews($newsData=array())
  215. {
  216. $createtime = time();
  217. $funcflag = $this->setFlag ? 1 : 0;
  218. $newTplHeader = "<xml>
  219. <ToUserName><![CDATA[{$this->msg['FromUserName']}]]></ToUserName>
  220. <FromUserName><![CDATA[{$this->msg['ToUserName']}]]></FromUserName>
  221. <CreateTime>{$createtime}</CreateTime>
  222. <MsgType><![CDATA[news]]></MsgType>
  223. <Content><![CDATA[%s]]></Content>
  224. <ArticleCount>%s</ArticleCount><Articles>";
  225. $newTplItem = "<item>
  226. <Title><![CDATA[%s]]></Title>
  227. <Description><![CDATA[%s]]></Description>
  228. <PicUrl><![CDATA[%s]]></PicUrl>
  229. <Url><![CDATA[%s]]></Url>
  230. </item>";
  231. $newTplFoot = "</Articles>
  232. <FuncFlag>%s</FuncFlag>
  233. </xml>";
  234. $content = '';
  235. $itemsCount = count($newsData['items']);
  236. $itemsCount = $itemsCount < 10 ? $itemsCount : 10;//微信公众平台图文回复的消息一次最多10条
  237. if ($itemsCount) {
  238. foreach ($newsData['items'] as $key => $item) {
  239. $content .= sprintf($newTplItem,$item['title'],$item['description'],$item['picUrl'],$item['url']);//微信的信息数据
  240. }
  241. }
  242. $header = sprintf($newTplHeader,$newsData['content'],$itemsCount);
  243. $footer = sprintf($newTplFoot,$funcflag);
  244. return $header . $content . $footer;
  245. }
  246. /**
  247. * 回复音乐消息
  248. * @param array $newsData
  249. * @return string
  250. */
  251. public function makeMusic($newsData=array())
  252. {
  253. $createtime = time();
  254. $funcflag = $this->setFlag ? 1 : 0;
  255. $textTpl = "<xml>
  256. <ToUserName><![CDATA[{$this->msg['FromUserName']}]]></ToUserName>
  257. <FromUserName><![CDATA[{$this->msg['ToUserName']}]]></FromUserName>
  258. <CreateTime>{$createtime}</CreateTime>
  259. <MsgType><![CDATA[music]]></MsgType>
  260. <Music>
  261. <Title><![CDATA[{$newsData['title']}]]></Title>
  262. <Description><![CDATA[{$newsData['description']}]]></Description>
  263. <MusicUrl><![CDATA[{$newsData['MusicUrl']}]]></MusicUrl>
  264. <HQMusicUrl><![CDATA[{$newsData['HQMusicUrl']}]]></HQMusicUrl>
  265. </Music>
  266. <FuncFlag>%s</FuncFlag>
  267. </xml>";
  268. return sprintf($textTpl,'',$funcflag);
  269. }
  270. /**
  271. * 得到制定分组的用户列表
  272. * @param number $groupid
  273. * @param number $pagesize,每页人数
  274. * @param number $pageidx,起始位置
  275. * @return Ambigous <boolean, string, mixed>
  276. */
  277. public function getfriendlist($groupid=0,$pagesize=500,$pageidx=0)
  278. {
  279. $url = 'https://mp.weixin.qq.com/cgi-bin/contactmanagepage?token='.$this->access_token.'&t=wxm-friend&lang=zh_CN&pagesize='.$pagesize.'&pageidx='.$pageidx.'&groupid='.$groupid;
  280. $referer = "https://mp.weixin.qq.com/";
  281. $response = $this->html($url, $referer);
  282. if (preg_match('%< id="json-friendList" type="json/text">([\s\S]*?)</>%', $response, $match))
  283. {
  284. $tmp = json_decode($match[1], true);
  285. }
  286. return $tmp;
  287. }
  288. /**
  289. * 返回给用户信息
  290. *
  291. */
  292. public function reply($data)
  293. {
  294. echo $data;
  295. }
  296. /**
  297. *@param type: text 文本类型, news 图文类型
  298. *@param value_arr array(内容),array(ID)
  299. *@param o_arr array(array(标题,介绍,图片,超链接),...小于10条),array(条数,ID)
  300. */
  301. private function make_xml($type,$value_arr,$o_arr=array(0))
  302. {
  303. //=================xml header============
  304. $con="<xml>
  305. <ToUserName><![CDATA[{$this->fromUsername}]]></ToUserName>
  306. <FromUserName><![CDATA[{$this->toUsername}]]></FromUserName>
  307. <CreateTime>{$this->times}</CreateTime>
  308. <MsgType><![CDATA[{$type}]]></MsgType>";
  309. //=================type content============
  310. switch($type)
  311. {
  312. case "text" :
  313. $con.="<Content><![CDATA[{$value_arr[0]}]]></Content>
  314. <FuncFlag>{$o_arr}</FuncFlag>";
  315. break;
  316. case "news" :
  317. $con.="<ArticleCount>{$o_arr[0]}</ArticleCount>
  318. <Articles>";
  319. foreach($value_arr as $id=>$v){
  320. if($id>=$o_arr[0]) break; else null; //判断数组数不超过设置数
  321. $con.="<item>
  322. <Title><![CDATA[{$v[0]}]]></Title>
  323. <Description><![CDATA[{$v[1]}]]></Description>
  324. <PicUrl><![CDATA[{$v[2]}]]></PicUrl>
  325. <Url><![CDATA[{$v[3]}]]></Url>
  326. </item>";
  327. }
  328. $con.="</Articles>
  329. <FuncFlag>{$o_arr[1]}</FuncFlag>";
  330. break;
  331. } //end switch
  332. //=================end return============
  333. $con.="</xml>";
  334. return $con;
  335. }
  336. //获取关注者列表
  337. public function get_user_list($next_openid = null)
  338. {
  339. $url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=".$this->access_token."&next_openid=".$next_openid;
  340. $res = $this->http($url);
  341. return json_decode($res, true);
  342. }
  343. //获取用户基本信息
  344. public function get_user_info($openid)
  345. {
  346. $url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=".$this->access_token."&openid=".$openid."&lang=zh_CN";
  347. $res = $this->http($url);
  348. return json_decode($res, true);
  349. }
  350. //发送客服消息,已实现发送文本,其他类型可扩展
  351. public function send_custom_message($touser, $type, $data)
  352. {
  353. $msg = array('touser' =>$touser);
  354. switch($type)
  355. {
  356. case 'text':
  357. $msg['msgtype'] = 'text';
  358. $msg['text'] = array('content'=> urlencode($data));
  359. break;
  360. }
  361. $url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=".$this->access_token;
  362. return $this->http($url, urldecode(json_encode($msg)));
  363. }
  364. //生成参数二维码
  365. public function create_qrcode($scene_type, $scene_id)
  366. {
  367. switch($scene_type)
  368. {
  369. case 'QR_LIMIT_SCENE': //永久
  370. $data = '{"action_name": "QR_LIMIT_SCENE", "action_info": {"scene": {"scene_id": '.$scene_id.'}}}';
  371. break;
  372. case 'QR_SCENE': //临时
  373. $data = '{"expire_seconds": 1800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": '.$scene_id.'}}}';
  374. break;
  375. }
  376. $url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=".$this->access_token;
  377. $res = $this->http($url, $data);
  378. $result = json_decode($res, true);
  379. return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=".urlencode($result["ticket"]);
  380. }
  381. //创建分组
  382. public function create_group($name)
  383. {
  384. $data = '{"group": {"name": "'.$name.'"}}';
  385. $url = "https://api.weixin.qq.com/cgi-bin/groups/create?access_token=".$this->access_token;
  386. $res = $this->http($url, $data);
  387. return json_decode($res, true);
  388. }
  389. //移动用户分组
  390. public function update_group($openid, $to_groupid)
  391. {
  392. $data = '{"openid":"'.$openid.'","to_groupid":'.$to_groupid.'}';
  393. $url = "https://api.weixin.qq.com/cgi-bin/groups/members/update?access_token=".$this->access_token;
  394. $res = $this->http($url, $data);
  395. return json_decode($res, true);
  396. }
  397. //上传多媒体文件
  398. public function upload_media($type, $file)
  399. {
  400. $data = array("media" => "@".dirname(__FILE__).'\\'.$file);
  401. $url = "http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=".$this->access_token."&type=".$type;
  402. $res = $this->http($url, $data);
  403. return json_decode($res, true);
  404. }
  405. private function checkSignature()
  406. {
  407. $signature = $_GET["signature"];
  408. $timestamp = $_GET["timestamp"];
  409. $nonce = $_GET["nonce"];
  410. $token = 'weixin';
  411. $tmpArr = array($token, $timestamp, $nonce);
  412. sort($tmpArr);
  413. $tmpStr = implode( $tmpArr );
  414. $tmpStr = sha1( $tmpStr );
  415. if( $tmpStr == $signature )
  416. {
  417. return true;
  418. }
  419. else
  420. {
  421. return false;
  422. }
  423. }
  424. // cURL函数简单封装
  425. public function http($url, $data = null)
  426. {
  427. $curl = curl_init();
  428. curl_setopt($curl, CURLOPT_URL, $url);
  429. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
  430. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
  431. if (!empty($data))
  432. {
  433. curl_setopt($curl, CURLOPT_POST, 1);
  434. curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
  435. }
  436. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  437. $output = curl_exec($curl);
  438. curl_close($curl);
  439. return $output;
  440. }
  441. }