看到那么多人瀏覽,沒有回復,肯定是自己寫的太爛了,現在在做畢業論文,做完了,一定重新寫一下,微信公共平台大家還是很感興趣的,嘿嘿。期待留言指教!!!
大家好,我很少原創,只因太菜,最近在研究微信api,說到這先講一個笑話:
話說西蜀劉備去世之后,托孤阿斗於諸葛孔明,孔明為報知遇之恩,興師北伐中原。臨行之前對阿斗說:“陛下聰慧機智,才藝超群,但缺威信”。阿斗回到:“這好辦,讓我注冊一個微信,昵稱:諸葛亮”。
還是扯正經的,微信api這個相信關注這篇博客的同志們都已經很熟悉了,這里只是和大家一起探討一些官方api以外的想法。騰訊宣布開放的公共平台api,其實還是不夠開放,至少目前是這樣,像一些比較強大和需要的api就沒有對外開放,比如:訂閱(這個騰訊自家的dnspod就有內部接口,看着很好用)、獲取關注用戶fakeid ,主動發送消息。這里嘗試通過模擬調用網頁版的官方公共平台管理系統去封裝實現盡可能多的功能。
先放出我的參考網上其他牛人資料封裝的類庫 https://github.com/ligboy/Wechat-php
先說一下思路,首先在訂閱事件的回復中加入一個身份識別瑪,比如openid的16位的md5散列,附在歡迎回復內容的最后,其實不會顯得不美觀和突兀。
1 2 3 case "subscribe": 4 $subscribeusersModel = D("Subscribeusers"); //thinkphp中數據庫模型 5 $condition['openid'] = $this->wechatObj->getRevFrom(); //得到關注用戶的openid 6 $data = $subscribeusersModel->where($condition)->select(); 7 if (!$data) 8 { 9 $subscribeusersModel->add($condition); 10 } 11 elseif($data[0]['unsubscribed']=="1") 12 { 13 $subscribeusersModel->where($condition)->save(array('unsubscribed'=>0,'subscribetime'=>time())); 14 } 15 else 16 { 17 18 } 19 //傳說中的識別碼 20 $indentifyText = substr(md5($this->wechatObj->getRevFrom()), 0, 16); 21 $this->wechatObj->text("歡迎您關注福大人,我們會用心為您服務。\n 福大人工作室"."\n".$indentifyText)->reply(); 22 break;
然后通過一個定期定期執行的任務去檢索數據庫中字段fakeid為空的訂閱用戶的列表,同時通過整合的網頁微信公共平台接口去獲取當前未分組用戶列表和他們在最近一小時的聊天紀錄內容,然后通過foreach檢索某個openid的是否匹配某個用戶的消息,如果匹配,則更新該openid用戶的fakeid為當前fakeid,並把當前用戶移動到一個確定分組內。
實現代碼:

1 /** 2 * 得到制定分組的用戶列表 3 * @param number $groupid 4 * @return Ambigous <boolean, string, mixed> 5 */ 6 public function getfriendlist($groupid=0) 7 { 8 $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/contactmanagepage?token=$this->webtoken&t=wxm-friend&pagesize=100&groupid=$groupid"; 9 $referer = $this->protocol."://mp.weixin.qq.com/"; 10 $response = $this->get($url, $referer); 11 $tmp = ""; 12 if (preg_match('%<script id="json-friendList" type="json/text">([\s\S]*?)</script>%', $response, $match)) { 13 $tmp = json_decode($match[1], true); 14 } 15 return empty($tmp)?false:$tmp; 16 17 } 18 19 20 /** 21 * 獲取用戶的fakeid 22 * @param callback $callback 處理匹配結果的回調函數 23 */ 24 public function getfakeid($callback) 25 { 26 $subscribeusersModel = D("Subscribeusers"); 27 $data = $subscribeusersModel->where(' `fakeid` IS NULL and `unsubscribed`=0')->select(); 28 if (!is_array($data)) 29 { 30 die("none data"); 31 } 32 $unfriendList = $this->getfriendlist(0); 33 if (!$unfriendList){ 34 die("none friendlist"); 35 } 36 $requestArray = array(); 37 foreach ($unfriendList as $key => $value) 38 { 39 // $requestArray[$key]['postfields']['createtime'] = time()-60000; 40 $requestArray[$key]['postfields']['fromfakeid'] = $value['fakeId']; 41 $requestArray[$key]['postfields']['opcode'] = 1; 42 $requestArray[$key]['postfields']['token'] = $this->webtoken; 43 $requestArray[$key]['postfields']['ajax'] = 1; 44 $requestArray[$key]['referer'] = $this->protocol."://mp.weixin.qq.com/"; 45 $requestArray[$key]['cookiefilepath'] = $this->cookiefilepath; 46 $requestArray[$key]['method'] = "post"; 47 $requestArray[$key]['url'] = $this->protocol."://mp.weixin.qq.com/cgi-bin/singlemsgpage?t=ajax-single-getnewmsg"; 48 } 49 // $callback = ''; 50 $rollingCurlObj = new Rollingcurl(); 51 $rollingCurlObj->setOtherCallbackArg(array('data'=>$data, 'wechatObj'=>$this)); 52 $response = $rollingCurlObj->setCallback($callback)->request($requestArray); 53 dump($response); 54 } 55 56 /** 57 * 將用戶放入制定的分組 58 * @param array $fakeidsList 59 * @param string $groupid 60 * @return boolean 放入是否成功 61 */ 62 public function putIntoGroup($fakeidsList, $groupid) 63 { 64 $fakeidsListString = ""; 65 if(is_array($fakeidsList)) 66 { 67 foreach ($fakeidsList as $value) 68 { 69 $fakeidsListString .= $value."|"; 70 } 71 } 72 else 73 { 74 $fakeidsListString = $fakeidsList; 75 } 76 $postfields['contacttype'] = $groupid; 77 $postfields['tofakeidlist'] = $fakeidsListString; 78 $postfields['token'] = $this->webtoken; 79 $postfields['ajax'] = 1; 80 $referer = $this->protocol."://mp.weixin.qq.com/"; 81 $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/modifycontacts?action=modifycontacts&t=ajax-putinto-group"; 82 $response = $this->post($url, $postfields, $referer); 83 $tmp = json_decode($response, true); 84 $result = $tmp['ret']=="0"&&!empty($tmp)?true:false; 85 return $result; 86 }
最下面附上全部代碼:

1 <?php 2 /** 3 * 微信公共平台整合庫 4 * @author Ligboy (ligboy@gmail.com) 5 * @license 本庫的很多思路來自於網上的其他熱心人士的貢獻,大家任意使用,我本人放棄所有權利,如果您心情好,給我留個署名也行。 6 * 7 */ 8 class Wechat { 9 /* 配置參數 */ 10 /** 11 * 12 * @var array 13 * @example array('token'=>'微信接口密鑰','account'=>'微信公共平台賬號','password'=>'微信公共平台密碼','webtoken'=>"微信公共平台網頁url的token"); 14 */ 15 private $wechatOptions=array('token'=>'rqerwer','account'=>'ligboy@gmail.com','password'=>'wwwwww'); // 16 private $cookiefilepath = ""; //以文件形式保存cookie的保存目錄,肯定是可寫的 17 public $webtoken = '742432903'; 18 private $webtokenStoragefile = ""; //微信公共平台的token存儲文件,就是公共平后台網頁的token 19 public $debug = true; //調試開關 20 public $protocol = "https"; //使用協議類型 http or https 21 22 /* 靜態常量 */ 23 const MSGTYPE_TEXT = 'text'; 24 const MSGTYPE_IMAGE = 'image'; 25 const MSGTYPE_LOCATION = 'location'; 26 const MSGTYPE_LINK = 'link'; 27 const MSGTYPE_EVENT = 'event'; 28 const MSGTYPE_MUSIC = 'music'; 29 const MSGTYPE_NEWS = 'news'; 30 const MSGTYPE_VOICE = 'voice'; 31 32 /* 私有參數 */ 33 private $_msg; 34 private $_funcflag = false; 35 private $_receive; 36 private $_logcallback; 37 private $_token; 38 private $_cookies; 39 private $_tmp1; 40 private $_tmp2; 41 42 43 44 /** 45 * 初始化工作 46 * @param array $option array('token'=>'微信接口密鑰','account'=>'微信公共平台賬號','password'=>'微信公共平台密碼'); 47 */ 48 function __construct($option=array()) 49 { 50 if (!empty($option)) 51 { 52 $this->wechatOptions = array_merge($this->wechatOptions, $option); 53 } 54 $this->setCookiefilepath("./app/Runtime", "wechatcookies".md5($this->wechatOptions['account']).".txt"); 55 if ($this->webtokenStoragefile) { 56 $this->webtoken = (string)file_get_contents($this->webtokenStoragefile); 57 } 58 59 60 } 61 62 /** 63 * 驗證請求簽名操作 64 * @return boolean 65 */ 66 private function checkSignature() 67 { 68 $signature = $_GET["signature"]; 69 $timestamp = $_GET["timestamp"]; 70 $nonce = $_GET["nonce"]; 71 72 $token = $this->wechatOptions['token']; 73 $tmpArr = array($token, $timestamp, $nonce); 74 sort($tmpArr); 75 $tmpStr = implode( $tmpArr ); 76 $tmpStr = sha1( $tmpStr ); 77 78 if( $tmpStr == $signature ) 79 { 80 return true; 81 } 82 else 83 { 84 return false; 85 } 86 } 87 88 /** 89 * 驗證當前請求是否有效 90 * @param bool $return 是否返回 91 */ 92 public function valid($return=false) 93 { 94 $echoStr = isset($_GET["echostr"]) ? $_GET["echostr"]: ''; 95 if ($return) 96 { 97 if ($echoStr) 98 { 99 if ($this->checkSignature()) 100 { 101 return $echoStr; 102 } 103 else 104 { 105 return false; 106 } 107 } else 108 return $this->checkSignature(); 109 } 110 else 111 { 112 if ($echoStr) 113 { 114 if ($this->checkSignature()) 115 { 116 die($echoStr); 117 } 118 else 119 { 120 die('no access'); 121 } 122 } 123 else 124 { 125 if ($this->checkSignature()) 126 { 127 return true; 128 } 129 else 130 { 131 die('no access'); 132 } 133 } 134 } 135 return false; 136 } 137 138 139 /** 140 * 設置發送消息 141 * @param array $msg 消息數組 142 * @param bool $append 是否在原消息數組追加 143 */ 144 public function Message($msg = '',$append = false){ 145 if (is_null($msg)) { 146 $this->_msg =array(); 147 }elseif (is_array($msg)) { 148 if ($append) 149 $this->_msg = array_merge($this->_msg,$msg); 150 else 151 $this->_msg = $msg; 152 return $this->_msg; 153 } else { 154 return $this->_msg; 155 } 156 } 157 158 public function setFuncFlag($flag) { 159 $this->_funcflag = $flag; 160 return $this; 161 } 162 163 private function log($log){ 164 if ($this->debug && function_exists($this->_logcallback)) { 165 if (is_array($log)) $log = print_r($log,true); 166 return call_user_func($this->_logcallback,$log); 167 } 168 } 169 170 /** 171 * 獲取微信服務器發來的信息 172 * @return mixed 173 */ 174 public function getRev() 175 { 176 $postStr = file_get_contents("php://input"); 177 $this->log($postStr); 178 if (!empty($postStr)) { 179 $this->_receive = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); 180 181 //debug 調試記錄回復信息 182 if ($this->debug){file_put_contents($this->debugpath."./rev.txt","\n---".date('Y-m-d H:i:s')."\n".print_r($this->_receive, true),FILE_APPEND);} 183 184 } 185 return $this; 186 } 187 188 /** 189 * 獲取消息發送者 190 * @return string or boolean 191 */ 192 public function getRevFrom() { 193 if ($this->_receive) 194 return $this->_receive['FromUserName']; 195 else 196 return false; 197 } 198 199 /** 200 * 獲取消息接受者 201 * @return string or boolean 202 */ 203 public function getRevTo() { 204 if ($this->_receive) 205 return $this->_receive['ToUserName']; 206 else 207 return false; 208 } 209 210 /** 211 * 獲取接收消息的類型 212 */ 213 public function getRevType() { 214 if (isset($this->_receive['MsgType'])) 215 return $this->_receive['MsgType']; 216 else 217 return false; 218 } 219 220 /** 221 * 獲取消息ID 222 */ 223 public function getRevID() { 224 if (isset($this->_receive['MsgId'])) 225 return $this->_receive['MsgId']; 226 else 227 return false; 228 } 229 230 /** 231 * 獲取消息發送時間 232 */ 233 public function getRevCtime() { 234 if (isset($this->_receive['CreateTime'])) 235 return $this->_receive['CreateTime']; 236 else 237 return false; 238 } 239 240 /** 241 * 獲取接收消息內容正文 242 */ 243 public function getRevContent(){ 244 if (isset($this->_receive['Content'])) 245 return $this->_receive['Content']; 246 else 247 return false; 248 } 249 250 /** 251 * 獲取接收消息圖片 252 */ 253 public function getRevPic(){ 254 if (isset($this->_receive['PicUrl'])) 255 return $this->_receive['PicUrl']; 256 else 257 return false; 258 } 259 260 /** 261 * 獲取接收消息鏈接 262 */ 263 public function getRevLink(){ 264 if (isset($this->_receive['Url'])){ 265 return array( 266 'url'=>$this->_receive['Url'], 267 'title'=>$this->_receive['Title'], 268 'description'=>$this->_receive['Description'] 269 ); 270 } else 271 return false; 272 } 273 274 /** 275 * 獲取接收地理位置 276 * @return array('x'=>'','y'=>'','scale'=>'','label'=>'') 277 */ 278 public function getRevGeo(){ 279 if (isset($this->_receive['Location_X'])){ 280 return array( 281 'x'=>$this->_receive['Location_X'], 282 'y'=>$this->_receive['Location_Y'], 283 'scale'=>$this->_receive['Scale'], 284 'label'=>$this->_receive['Label'] 285 ); 286 } else 287 return false; 288 } 289 290 /** 291 * 獲取接收事件推送 292 * @return array 成功返回事件數組,失敗返回false 293 */ 294 public function getRevEvent(){ 295 if (isset($this->_receive['Event'])){ 296 return array( 297 'event'=>$this->_receive['Event'], 298 'key'=>$this->_receive['EventKey'], 299 ); 300 } else 301 return false; 302 } 303 304 /** 305 * 獲取接收語言推送 306 * @return 307 */ 308 public function getRevVoice(){ 309 if (isset($this->_receive['MediaId'])){ 310 return array( 311 'mediaid'=>$this->_receive['MediaId'], 312 'format'=>$this->_receive['Format'], 313 ); 314 } else 315 return false; 316 } 317 318 public static function xmlSafeStr($str) 319 { 320 return '<![CDATA['.preg_replace("/[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]/",'',$str).']]>'; 321 } 322 323 /** 324 * 數據XML編碼 325 * @param mixed $data 數據 326 * @return string 327 */ 328 public static function data_to_xml($data) { 329 $xml = ''; 330 foreach ($data as $key => $val) { 331 is_numeric($key) && $key = "item id=\"$key\""; 332 $xml .= "<$key>"; 333 $xml .= ( is_array($val) || is_object($val)) ? self::data_to_xml($val) : self::xmlSafeStr($val); 334 list($key, ) = explode(' ', $key); 335 $xml .= "</$key>"; 336 } 337 return $xml; 338 } 339 340 /** 341 * XML編碼 342 * @param mixed $data 數據 343 * @param string $root 根節點名 344 * @param string $item 數字索引的子節點名 345 * @param string $attr 根節點屬性 346 * @param string $id 數字索引子節點key轉換的屬性名 347 * @param string $encoding 數據編碼 348 * @return string 349 */ 350 public function xml_encode($data, $root='xml', $item='item', $attr='', $id='id', $encoding='utf-8') { 351 if(is_array($attr)){ 352 $_attr = array(); 353 foreach ($attr as $key => $value) { 354 $_attr[] = "{$key}=\"{$value}\""; 355 } 356 $attr = implode(' ', $_attr); 357 } 358 $attr = trim($attr); 359 $attr = empty($attr) ? '' : " {$attr}"; 360 $xml .= "<{$root}{$attr}>"; 361 $xml .= self::data_to_xml($data, $item, $id); 362 $xml .= "</{$root}>"; 363 return $xml; 364 } 365 366 /** 367 * 設置回復消息 368 * Examle: $obj->text('hello')->reply(); 369 * @param string $text 370 */ 371 public function text($text='') 372 { 373 $FuncFlag = $this->_funcflag ? 1 : 0; 374 $msg = array( 375 'ToUserName' => $this->getRevFrom(), 376 'FromUserName'=>$this->getRevTo(), 377 'MsgType'=>self::MSGTYPE_TEXT, 378 'Content'=>$text, 379 'CreateTime'=>time(), 380 'FuncFlag'=>$FuncFlag 381 ); 382 $this->Message($msg); 383 return $this; 384 } 385 386 /** 387 * 設置回復音樂 388 * @param string $title 389 * @param string $desc 390 * @param string $musicurl 391 * @param string $hgmusicurl 392 */ 393 public function music($title,$desc,$musicurl,$hgmusicurl='') { 394 $FuncFlag = $this->_funcflag ? 1 : 0; 395 $msg = array( 396 'ToUserName' => $this->getRevFrom(), 397 'FromUserName'=>$this->getRevTo(), 398 'CreateTime'=>time(), 399 'MsgType'=>self::MSGTYPE_MUSIC, 400 'Music'=>array( 401 'Title'=>$title, 402 'Description'=>$desc, 403 'MusicUrl'=>$musicurl, 404 'HQMusicUrl'=>$hgmusicurl 405 ), 406 'FuncFlag'=>$FuncFlag 407 ); 408 $this->Message($msg); 409 return $this; 410 } 411 412 /** 413 * 設置回復圖文 414 * @param array $newsData 415 * @example 數組結構: 416 * array( 417 * [0]=>array( 418 * 'Title'=>'msg title', 419 * 'Description'=>'summary text', 420 * 'PicUrl'=>'http://www.domain.com/1.jpg', 421 * 'Url'=>'http://www.domain.com/1.html' 422 * ), 423 * [1]=>.... 424 * ) 425 */ 426 public function news($newsData=array()) 427 { 428 $FuncFlag = $this->_funcflag ? 1 : 0; 429 $count = count($newsData); 430 431 $msg = array( 432 'ToUserName' => $this->getRevFrom(), 433 'FromUserName'=>$this->getRevTo(), 434 'MsgType'=>self::MSGTYPE_NEWS, 435 'CreateTime'=>time(), 436 'ArticleCount'=>$count, 437 'Articles'=>$newsData, 438 'FuncFlag'=>$FuncFlag 439 ); 440 $this->Message($msg); 441 return $this; 442 } 443 444 /** 445 * 446 * 向微信服務器回復消息 447 * Example: $this->text('msg tips')->reply(); 448 * @param string $msg 要發送的信息, 默認取$this->_msg 449 * @param bool $return 是否返回信息而輸出 默認:false 450 */ 451 public function reply($msg=array(),$return = false) 452 { 453 if (empty($msg)) 454 { 455 $msg = $this->_msg; 456 } 457 $xmldata= $this->xml_encode($msg); 458 $this->log($xmldata); 459 if ($return) 460 { 461 return $xmldata; 462 } 463 else 464 { 465 echo $xmldata; 466 } 467 //debug 調試記錄回復信息 468 if ($this->debug){file_put_contents($this->debugpath."./reply.txt","\n---".date('Y-m-d H:i:s')."\n".$xmldata,FILE_APPEND);} 469 } 470 471 472 /** 473 * 登錄微信公共平台,獲取並保存cookie、webtoken到指定文件 474 * @return mixed 成功則返回true,失敗則返回失敗 475 */ 476 public function login(){ 477 $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN"; 478 $postfields["username"] = $this->wechatOptions['account']; 479 $postfields["pwd"] = md5($this->wechatOptions['password']); 480 $postfields["f"] = "json"; 481 $postfieldss = "username=".urlencode($this->wechatOptions['account'])."&pwd=".urlencode(md5($this->wechatOptions['password']))."&f=json"; 482 $response = $this->post($url, $postfields, $this->protocol."://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN"); 483 $result = json_decode($response, true); 484 if ($result['ErrCode']=="65201"||$result['ErrCode']=="65202"||$result['ErrCode']=="0") 485 { 486 preg_match('/&token=([\d]+)/i', $result['ErrMsg'],$match); 487 file_put_contents($this->webtokenStoragefile, $match[1]); 488 $this->webtoken = $match[1]; 489 return true; 490 } 491 else 492 { 493 unlink($this->cookiefilepath); 494 return false; 495 // return $result['ErrCode']; 496 } 497 } 498 499 /** 500 * 讀取緩存的cookies文件 501 * @param string $filename 文件名 502 * @param string $content 文件內容 503 * @return [type] [description] 504 */ 505 public function readFileCookies(){ 506 507 if(file_exists($this->cookiefilepath)){ 508 $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/getregions?id=1054&t=ajax-getregions&lang=zh_CN&token=".$this->webtoken; 509 $response = $this->get($url, $this->protocol."://mp.weixin.qq.com/"); 510 $result = json_decode($response,true); 511 if($result['num']) 512 { 513 return true; 514 } 515 else 516 { 517 return true===$this->login(); 518 } 519 } 520 else 521 { 522 return true===$this->login(); 523 } 524 } 525 526 /** 527 * 驗證cookie的有效性 528 * @return boolean 529 */ 530 public function checkValid() 531 { 532 $postfields = array(); 533 $url = $this->protocol.":https://mp.weixin.qq.com/cgi-bin/getregions?id=1054&t=ajax-getregions&lang=zh_CN&token=".$this->webtoken; 534 //判斷cookie是否為空,為空的話自動執行登錄 535 if (file_exists($this->cookiefilepath)) 536 { 537 $response = $this->get($url, $this->protocol."://mp.weixin.qq.com/cgi-bin/userinfopage?t=wxm-setting&token=383506232&lang=zh_CN"); 538 $result = json_decode($response,1); 539 if(isset($result['num'])){ 540 return true; 541 }else{ 542 return false; 543 } 544 } 545 else 546 { 547 return false; 548 } 549 } 550 551 /** 552 * 主動單條發消息 553 * @param string $id 用戶的fakeid 554 * @param string $content 發送的內容 555 * @return boolean 返回發送結果:成功返回:1,登錄問題返回:-1,其他原因返回:0 556 */ 557 public function send($fakeid,$content) 558 { 559 //判斷cookie是否為空,為空的話自動執行登錄 560 if (file_exists($this->cookiefilepath)||true===$this->login()) 561 { 562 $postfields = array(); 563 $postfields['tofakeid'] = $fakeid; 564 $postfields['type'] = 1; 565 $postfields['error']= "false"; 566 $postfields['token']= $this->webtoken; 567 $postfields['content'] = $content; 568 $postfields['ajax'] = 1; 569 $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; 570 $response = $this->post($url, $postfields, $this->protocol."://mp.weixin.qq.com/"); 571 $tmp = json_decode($response,true); 572 //判斷發送結果的邏輯部分 573 if ('ok'==$tmp["msg"]) { 574 return 1; 575 } 576 elseif ($tmp['ret']=="-2000") 577 { 578 return -1; 579 } 580 else 581 { 582 return false; 583 } 584 } 585 else //登錄失敗返回false 586 { 587 return false; 588 } 589 } 590 591 /** 592 * 主動批量發送,目前暫支持文本方式 593 * @param array $fakeidGroup 接受微信fakeid集合數組 594 * @param [type] $content [description] 595 * @return mixed 如果所有都發送失敗,返回false,否則,返回一個數組分別記錄成功的列表 596 * 這里需要注意請求耗時問題,目前采用curl並發性請求,並發請求數10個 597 */ 598 public function batSend($fakeidGroup,$content) 599 { 600 $result = array(); 601 $successCount = 0; 602 $requestArray = array(); 603 foreach ($fakeidGroup as $key =>$value) 604 { 605 $postfields = array(); 606 $postfields['tofakeid'] = $value; 607 $postfields['type'] = 1; 608 $postfields['error']= "false"; 609 $postfields['token']= $this->webtoken; 610 $postfields['content'] = $content; 611 $postfields['ajax'] = 1; 612 $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response"; 613 $requestArray[] = array('url'=>$url,'method'=>'post','postfields'=>$postfields,'referer'=>$this->protocol."://mp.weixin.qq.com/",'cookiefilepath'=>$this->cookiefilepath); 614 } 615 //DEBUG: 616 // dump($requestArray); 617 $callback = function ($result, $key){ 618 $tmp = json_decode($result,true); 619 //判斷發送結果的邏輯部分 620 if ('ok'==$tmp["msg"]) { 621 return 1; 622 } 623 elseif ($tmp['ret']=="-2000") 624 { 625 return -1; 626 } 627 else 628 { 629 return 0; 630 } 631 }; 632 $rollingCurlObj = new Rollingcurl(); 633 $response = $rollingCurlObj->setCallback($callback)->request($requestArray); 634 return $response; 635 } 636 637 /** 638 * 獲取用戶的信息 639 * @param string $fakeid 用戶的fakeid 640 * @return mixed 如果成功獲取返回數據數組,登錄問題返回false,其他未知問題返回true, 641 */ 642 public function getContactInfo($fakeid) 643 { 644 if (file_exists($this->cookiefilepath)||true===$this->login()) 645 { 646 $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/getcontactinfo?t=ajax-getcontactinfo&lang=zh_CN&fakeid=".$fakeid; 647 $response = $this->get($url, $this->protocol."://mp.weixin.qq.com/"); 648 $result = json_decode($response,1); 649 if($result['FakeId']){ 650 return $result; 651 } 652 elseif ($result['ret']) 653 { 654 return false; 655 } 656 else 657 { 658 return true; 659 } 660 } 661 else 662 { 663 return false; 664 } 665 } 666 667 668 public function getfriendlist($groupid=0) 669 { 670 $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/contactmanagepage?token=$this->webtoken&t=wxm-friend&pagesize=100&groupid=$groupid"; 671 $referer = $this->protocol."://mp.weixin.qq.com/"; 672 $response = $this->get($url, $referer); 673 $tmp = ""; 674 if (preg_match('%<script id="json-friendList" type="json/text">([\s\S]*?)</script>%', $response, $match)) { 675 $tmp = json_decode($match[1], true); 676 } 677 return empty($tmp)?false:$tmp; 678 679 } 680 681 682 public function getfakeid($callback) 683 { 684 $subscribeusersModel = D("Subscribeusers"); 685 $data = $subscribeusersModel->where(' `fakeid` IS NULL and `unsubscribed`=0')->select(); 686 if (!is_array($data)) 687 { 688 die("none data"); 689 } 690 $unfriendList = $this->getfriendlist(0); 691 if (!$unfriendList){ 692 die("none friendlist"); 693 } 694 $requestArray = array(); 695 foreach ($unfriendList as $key => $value) 696 { 697 // $requestArray[$key]['postfields']['createtime'] = time()-60000; 698 $requestArray[$key]['postfields']['fromfakeid'] = $value['fakeId']; 699 $requestArray[$key]['postfields']['opcode'] = 1; 700 // $requestArray[$key]['postfields']['lastmsgid'] = 0; 701 // $requestArray[$key]['lastmsgfromfakeid']['2399396672'] = 2399396672; 702 $requestArray[$key]['postfields']['token'] = $this->webtoken; 703 $requestArray[$key]['postfields']['ajax'] = 1; 704 $requestArray[$key]['referer'] = $this->protocol."://mp.weixin.qq.com/"; 705 $requestArray[$key]['cookiefilepath'] = $this->cookiefilepath; 706 $requestArray[$key]['method'] = "post"; 707 $requestArray[$key]['url'] = $this->protocol."://mp.weixin.qq.com/cgi-bin/singlemsgpage?t=ajax-single-getnewmsg"; 708 } 709 // $callback = ''; 710 $rollingCurlObj = new Rollingcurl(); 711 $rollingCurlObj->setOtherCallbackArg(array('data'=>$data, 'wechatObj'=>$this)); 712 $response = $rollingCurlObj->setCallback($callback)->request($requestArray); 713 dump($response); 714 } 715 716 /** 717 * 將用戶放入制定的分組 718 * @param array $fakeidsList 719 * @param string $groupid 720 * @return boolean 放入是否成功 721 */ 722 public function putIntoGroup($fakeidsList, $groupid) 723 { 724 $fakeidsListString = ""; 725 if(is_array($fakeidsList)) 726 { 727 foreach ($fakeidsList as $value) 728 { 729 $fakeidsListString .= $value."|"; 730 } 731 } 732 else 733 { 734 $fakeidsListString = $fakeidsList; 735 } 736 $postfields['contacttype'] = $groupid; 737 $postfields['tofakeidlist'] = $fakeidsListString; 738 $postfields['token'] = $this->webtoken; 739 $postfields['ajax'] = 1; 740 $referer = $this->protocol."://mp.weixin.qq.com/"; 741 $url = $this->protocol."://mp.weixin.qq.com/cgi-bin/modifycontacts?action=modifycontacts&t=ajax-putinto-group"; 742 $response = $this->post($url, $postfields, $referer); 743 $tmp = json_decode($response, true); 744 $result = $tmp['ret']=="0"&&!empty($tmp)?true:false; 745 return $result; 746 } 747 748 749 /** 750 * @return the $wechatOptions 751 */ 752 public function getWechatOptions() { 753 return $this->wechatOptions; 754 } 755 756 /** 757 * @return the $cookiestoragemode 758 */ 759 public final function getCookiestoragemode() { 760 return $this->cookiestoragemode; 761 } 762 763 /** 764 * @return the $cookiefilepath 765 */ 766 public final function getCookiefilepath() { 767 return $this->cookiefilepath; 768 } 769 770 /** 771 * @return the $debug 772 */ 773 public function getDebug() { 774 return $this->debug; 775 } 776 777 /** 778 * 設置webtoken的保存文件路徑和文件名 779 * @return the $webtokenStoragefile 780 */ 781 public function getWebtokenStoragefile() { 782 return $this->webtokenStoragefile; 783 } 784 785 /** 786 * 設置webtoken的保存文件路徑和文件名 787 * @param string $webtokenStoragefile 788 */ 789 public function setWebtokenStoragefile($webtokenStoragefile) { 790 $this->webtokenStoragefile = $webtokenStoragefile; 791 $this->webtoken = (string)file_get_contents($this->webtokenStoragefile); 792 } 793 794 /** 795 * 設置微信配置信息 796 * @param multitype:string $wechatOptions 797 */ 798 public function setWechatOptions($wechatOptions) { 799 $this->wechatOptions = array_merge($this->wechatOptions, $wechatOptions); 800 } 801 802 /** 803 * 設置cookie文件保存位置 804 * @param string $cookiefilepath cookie保存路徑 805 * @param string $cookiefilename 默認:wechatcookies".md5($this->wechatOptions['account']).".txt" 806 */ 807 public function setCookiefilepath($cookiefilepath, $cookiefilename = "") { 808 $this->cookiefilepath = $cookiefilepath.(substr($cookiefilepath, -1, 1)=="/"?"":"/").(is_null($cookiefilename))?"wechatcookies".md5($this->wechatOptions['account']).".txt":$cookiefilename; 809 } 810 811 /** 812 * @param boolean $debug 813 */ 814 public function setDebug($debug) { 815 $this->debug = $debug; 816 } 817 818 private function post($url, $postfields, $referer) 819 { 820 $ch = curl_init($url); 821 $options = array( 822 CURLOPT_RETURNTRANSFER => true, // return web page 823 CURLOPT_HEADER => false, // don't return headers 824 CURLOPT_FOLLOWLOCATION => true, // follow redirects 825 CURLOPT_ENCODING => "", // handle all encodings 826 CURLOPT_USERAGENT => "", // who am i 827 CURLOPT_AUTOREFERER => true, // set referer on redirect 828 CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect 829 CURLOPT_TIMEOUT => 120, // timeout on response 830 CURLOPT_MAXREDIRS => 10, // stop after 10 redirects 831 CURLOPT_POST => true, // i am sending post data 832 CURLOPT_POSTFIELDS => $postfields, // this are my post vars 833 CURLOPT_SSL_VERIFYHOST => 0, // don't verify ssl 834 CURLOPT_SSL_VERIFYPEER => false, // 835 CURLOPT_COOKIEFILE =>$this->cookiefilepath, 836 CURLOPT_COOKIEJAR =>$this->cookiefilepath, 837 CURLOPT_REFERER =>$referer, 838 ); 839 curl_setopt_array($ch, $options); 840 $result = curl_exec($ch); 841 curl_close($ch); 842 return $result; 843 } 844 845 private function get($url, $referer) 846 { 847 $info = null; 848 $ch = curl_init($url); 849 $options = array( 850 CURLOPT_RETURNTRANSFER => true, // return web page 851 CURLOPT_HEADER => false, // don't return headers 852 CURLOPT_FOLLOWLOCATION => true, // follow redirects 853 CURLOPT_ENCODING => "", // handle all encodings 854 CURLOPT_USERAGENT => "", // who am i 855 CURLOPT_AUTOREFERER => true, // set referer on redirect 856 CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect 857 CURLOPT_TIMEOUT => 120, // timeout on response 858 CURLOPT_MAXREDIRS => 10, // stop after 10 redirects 859 CURLOPT_SSL_VERIFYHOST => 0, // don't verify ssl 860 CURLOPT_SSL_VERIFYPEER => false, // 861 CURLOPT_COOKIEFILE =>$this->cookiefilepath, 862 CURLOPT_COOKIEJAR =>$this->cookiefilepath, 863 CURLOPT_REFERER =>$referer, 864 ); 865 curl_setopt_array($ch, $options); 866 $result = curl_exec($ch); 867 curl_close($ch); 868 return $result; 869 } 870 } 871 872 873 /** 874 * Rolling Curl Request Class 875 * @author Ligboy (ligboy@gamil.com) 876 * @copyright 877 * @example 878 * 879 * 880 */ 881 class Rollingcurl { 882 private $limitCount = 10; //並發請求數量 883 public $returninfoswitch = false; //是否返回請求信息,開啟后單項請求返回結果為:array('info'=>請求信息, 'result'=>返回內容, 'error'=>錯誤信息) 884 885 886 //私有屬性 887 private $queue = null; 888 private $_requstItems = null; 889 private $_callback = null; 890 private $_otherCallbackArg = null; 891 private $_options = array( 892 CURLOPT_RETURNTRANSFER => true, // return web page 893 CURLOPT_HEADER => false, // don't return headers 894 CURLOPT_FOLLOWLOCATION => true, // follow redirects 895 CURLOPT_NOSIGNAL =>true, 896 CURLOPT_TIMEOUT =>true, 897 CURLOPT_ENCODING => "", // handle all encodings 898 CURLOPT_USERAGENT => "", // who am i 899 CURLOPT_AUTOREFERER => true, // set referer on redirect 900 CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect 901 CURLOPT_TIMEOUT => 120, // timeout on response 902 CURLOPT_MAXREDIRS => 10, // stop after 10 redirects 903 CURLOPT_SSL_VERIFYHOST => 0, // don't verify ssl 904 CURLOPT_SSL_VERIFYPEER => false, // 905 ); 906 907 908 /** 909 * 910 * @param array $options [可選的]公共的Curl請求參數 911 */ 912 function __construct($options = array()) { 913 $this->queue = curl_multi_init(); 914 if ($options) { 915 array_merge($this->_options, $options); 916 } 917 } 918 919 920 /** 921 * 並發行的curl方法 922 * @param unknown $requestArray 923 * @param string $callback 924 * @return multitype:multitype: 925 */ 926 function request($requestArray, $callback="") 927 { 928 $this->_requstItems = $requestArray; 929 $requestArrayKeys = array_keys($requestArray); 930 /* $requestArray = array( 931 array( 932 'url' => "", 933 'method' => "post", 934 'postfields' => array(), 935 'cookiefilepath' => "", 936 'cookiefilepath' => "", 937 'referer' => "", 938 ), 939 array( 940 'url' => "", 941 'postfields' => array(), 942 'cookiefilepath' => "", 943 'cookiefilepath' => "", 944 'referer' => "", 945 ), 946 ); */ 947 $this->queue = curl_multi_init(); 948 $map = array(); 949 for ($i=0;$i<$this->limitCount && !empty($requestArrayKeys);$i++) 950 { 951 $keyvalue = array_shift($requestArrayKeys); 952 $this->addToQueue ( $requestArray, $keyvalue, $map ); 953 954 } 955 956 $responses = array(); 957 do { 958 while (($code = curl_multi_exec($this->queue, $active)) == CURLM_CALL_MULTI_PERFORM) ; 959 960 if ($code != CURLM_OK) { break; } 961 962 // 找到剛剛完成的任務句柄 963 while ($done = curl_multi_info_read($this->queue)) { 964 // 處理當前句柄的信息、錯誤、和返回內容 965 $info = curl_getinfo($done['handle']); 966 $error = curl_error($done['handle']); 967 if (is_callable($this->_callback)) 968 { 969 $result = $this->callback ( $requestArray, $map, $done, $this->_otherCallbackArg ); 970 971 } 972 else 973 { 974 //如果callback為空,直接返回內容 975 $result = curl_multi_getcontent($done['handle']); 976 } 977 if ($this->returninfoswitch) { 978 $responses[$map[(string) $done['handle']]] = compact('info', 'error', 'result'); 979 } 980 else 981 { 982 $responses[$map[(string) $done['handle']]] = $result; 983 } 984 985 // 從隊列里移除上面完成處理的句柄 986 curl_multi_remove_handle($this->queue, $done['handle']); 987 curl_close($done['handle']); 988 if (!empty($requestArrayKeys)) 989 { 990 $addkey = array_shift($requestArrayKeys); 991 $this->addToQueue ( $requestArray, $addkey, $map ); 992 } 993 } 994 995 // Block for data in / output; error handling is done by curl_multi_exec 996 if ($active > 0) { 997 curl_multi_select($this->queue, 0.5); 998 } 999 1000 } while ($active); 1001 1002 curl_multi_close($this->queue); 1003 return $responses; 1004 } 1005 /** 1006 * @param requestArray 1007 * @param map 1008 * @param done 1009 */private function callback($requestArray, $map, $done, $otherCallbackArg) { 1010 //調用callback函數處理當前句柄的返回內容,callback函數參數有:(返回內容, 隊列id) 1011 $result = call_user_func($this->_callback, curl_multi_getcontent($done['handle']), $map[(string) $done['handle']], $requestArray[$map[(string) $done['handle']]], $otherCallbackArg ); 1012 return $result; 1013 } 1014 1015 /** 1016 * @param requestArray 1017 * @param map 1018 * @param keyvalue 1019 */private function addToQueue($requestArray, $keyvalue, &$map) { 1020 $ch = curl_init(); 1021 curl_setopt_array($ch, $this->_options); 1022 //檢查提交方式,並設置對應的設置,為空的話默認采用get方式 1023 if ("post" === $requestArray[$keyvalue]['method']) 1024 { 1025 curl_setopt($ch, CURLOPT_POST, true); 1026 curl_setopt($ch, CURLOPT_POSTFIELDS, $requestArray[$keyvalue]['postfields']); 1027 } 1028 else 1029 { 1030 curl_setopt($ch, CURLOPT_HTTPGET, true); 1031 } 1032 1033 //設置cookie保存文件路徑 1034 if (!empty($requestArray[$keyvalue]['cookiefilepath'])) 1035 { 1036 //如果這個文件存在,則采用采用此文件配置cookie 1037 if (file_exists($requestArray[$keyvalue]['cookiefilepath'])) 1038 { 1039 curl_setopt($ch, CURLOPT_COOKIEFILE, $requestArray[$keyvalue]['cookiefilepath']); 1040 } 1041 curl_setopt($ch, CURLOPT_COOKIEJAR, $requestArray[$keyvalue]['cookiefilepath']); 1042 } 1043 ////直接設定cookie。多個cookie用分號分隔,分號后帶一個空格(例如, "username=ligboy; password=123456; ")。 1044 if (!empty($requestArray[$keyvalue]['cookie'])) 1045 { 1046 curl_setopt($ch, CURLOPT_COOKIE, $requestArray[$keyvalue]['cookie']); 1047 } 1048 curl_setopt($ch, CURLOPT_URL, $requestArray[$keyvalue]['url']); 1049 curl_setopt($ch, CURLOPT_REFERER, $requestArray[$keyvalue]['referer']); 1050 curl_multi_add_handle($this->queue, $ch); 1051 $map[(string) $ch] = $keyvalue; 1052 } 1053 1054 /** 1055 * @return the $limitCount 1056 */ 1057 public function getLimitCount() { 1058 return $this->limitCount; 1059 } 1060 1061 /** 1062 * 設置並發性請求數量 1063 * @param number $limitCount 1064 */ 1065 public function setLimitCount($limitCount) { 1066 $this->limitCount = $limitCount; 1067 return $this; 1068 } 1069 1070 /** 1071 * 設置回調函數 1072 * @param field_type $_callback 1073 */ 1074 public function setCallback($_callback) { 1075 $this->_callback = $_callback; 1076 return $this; 1077 } 1078 /** 1079 * @return the $_otherCallbackArg 1080 */ 1081 public function getOtherCallbackArg() { 1082 return $this->_otherCallbackArg; 1083 } 1084 1085 /** 1086 * @param field_type $_otherCallbackArg 1087 */ 1088 public function setOtherCallbackArg($_otherCallbackArg) { 1089 $this->_otherCallbackArg = $_otherCallbackArg; 1090 } 1091 1092 1093 1094 }
調用實例

1 date_default_timezone_set("Asia/Shanghai"); 2 import("@.Common.Wechat"); 3 $options = array( 4 'token'=>'*****', 5 'account'=>'ligboy@gmail.com', 6 'password'=>'******' 7 ); 8 $this->wechatObj = new Wechat($options); 9 $this->wechatObj->setCookiefilepath("./app/Runtime/"); 10 $this->wechatObj->setWebtokenStoragefile("./app/Runtime/webtoken.txt"); 11 print_r($this->wechatObj->login()); 12 $callback = function ($result, $key, $request, $otherCallbackArg){ 13 $reruen_tmp = false; 14 foreach ($otherCallbackArg['data'] as $data_key => $data_value) 15 { 16 if(false !== strpos($result, substr(md5($data_value['openid']), 0, 16))) 17 { 18 $subscribeusersModel = D("Subscribeusers"); 19 $condition['openid'] = $data_value['openid']; 20 $data = $subscribeusersModel->where($condition)->save(array('fakeid'=>$request['postfields']['fromfakeid'])); 21 $otherCallbackArg['wechatObj']->putIntoGroup($request['postfields']['fromfakeid'], 101); 22 $reruen_tmp = $data_value['openid']; 23 break; 24 } 25 } 26 return $reruen_tmp; 27 }; 28 print_r($this->wechatObj->getfakeid($callback));