PHP開發(fā)之用微信遠(yuǎn)程遙控服務(wù)器
摘要
微信公眾好的開發(fā)很火,小程序更火。于是也湊個(gè)熱鬧,嘗試了一把。
大致的功能還是有的,不過是不全,很多地方我沒有進(jìn)行處理。不過對(duì)于純文本方式的交流,已經(jīng)沒有問題啦。
環(huán)境搭建
下面大致的講講微信公眾號(hào)的原理吧??赡芪依斫獾挠行┎坏轿唬绻行┰S不當(dāng),歡迎批評(píng)指教。
客戶端發(fā)送給微信平臺(tái)請(qǐng)求,微信平臺(tái)將請(qǐng)求轉(zhuǎn)發(fā)給私服,交給程序處理之后,獲取到私服的處理結(jié)果,然后反饋給客戶端。
當(dāng)然,這其中起到核心作用的自然是“微信公眾平臺(tái)”啦。相當(dāng)于提供了一個(gè)舞臺(tái),一個(gè)能讓各位能人異士展現(xiàn)出各自的特色的平臺(tái)。其實(shí),不僅微信如此,阿里同樣是這樣,如此各大電商才能一展手腳不是。
開啟配置
這第一步,就是先申請(qǐng)一個(gè)微信開發(fā)者賬號(hào),個(gè)人的話選擇訂閱號(hào)就足夠了。網(wǎng)上相關(guān)的資料很多,也很詳細(xì),我就不多說了。咱們直奔主題好了。
首先登陸開發(fā)者賬號(hào)成功后,開啟服務(wù)器端的設(shè)置即可,如下圖
開啟完成,根據(jù)自己服務(wù)器的情況進(jìn)行一下設(shè)置即可。
- URL就是你的私服用于處理請(qǐng)求數(shù)據(jù)的地址
- TOKEN就是一個(gè)令牌,隨便設(shè)置。不過記住待會(huì)自己的代碼上會(huì)用到。
- 至于密鑰嘛,沒什么較大的作用,暫且可以先不用管。
按需設(shè)置
設(shè)置完,就可以啟用了。這就好比家里的電線全部裝修好了,現(xiàn)在要使用,按下開關(guān)一樣。如下圖
啟用服務(wù)器配置
服務(wù)器環(huán)境
關(guān)于服務(wù)器這塊,官網(wǎng)上講解的也是很詳細(xì)的啦。
我們還可以下載官方的demo來模擬。
官方樣本
代碼也很簡(jiǎn)單?;旧蠈W(xué)過了PHP基本語法的都能夠看得懂。
<?php /** * wechat php test */ //define your token define("TOKEN", "weixin"); $wechatObj = new wechatCallbackapiTest(); $wechatObj->valid(); class wechatCallbackapiTest { public function valid() { $echoStr = $_GET["echostr"]; //valid signature , option if($this->checkSignature()){ echo $echoStr; exit; } } public function responseMsg() { //get post data, May be due to the different environments $postStr = $GLOBALS["HTTP_RAW_POST_DATA"]; //extract post data if (!empty($postStr)){ /* libxml_disable_entity_loader is to prevent XML eXternal Entity Injection, the best way is to check the validity of xml by yourself */ libxml_disable_entity_loader(true); $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); $fromUsername = $postObj->FromUserName; $toUsername = $postObj->ToUserName; $keyword = trim($postObj->Content); $time = time(); $textTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[%s]]></MsgType> <Content><![CDATA[%s]]></Content> <FuncFlag>0</FuncFlag> </xml>"; if(!empty( $keyword )) { $msgType = "text"; $contentStr = "Welcome to wechat world!"; $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr); echo $resultStr; }else{ echo "Input something..."; } }else { echo ""; exit; } } private function checkSignature() { // you must define TOKEN by yourself if (!defined("TOKEN")) { throw new Exception('TOKEN is not defined!'); } $signature = $_GET["signature"]; $timestamp = $_GET["timestamp"]; $nonce = $_GET["nonce"]; $token = TOKEN; $tmpArr = array($token, $timestamp, $nonce); // use SORT_STRING rule sort($tmpArr, SORT_STRING); $tmpStr = implode( $tmpArr ); $tmpStr = sha1( $tmpStr ); if( $tmpStr == $signature ){ return true; }else{ return false; } } } ?>
核心思路,無非檢驗(yàn)一下簽名,處理一下請(qǐng)求,反饋一下結(jié)果罷了。
這里我不得不想說的就是,我覺得騰訊其實(shí)可以將那些個(gè)模板什么的去掉,直接暴露出黑盒模式,這樣的話安全性會(huì)更高一點(diǎn)。很多時(shí)候,權(quán)限放的越開,效果可能越差。
核心類
接下來就是我自己的處理邏輯了,參照官方文檔。微信公眾好上有6大接收接口,三大回復(fù)接口。依據(jù)MsgType即可判定。
接口詳情
驗(yàn)證
private function checkSignature() { // you must define TOKEN by yourself if (! defined ( "TOKEN" )) { throw new Exception ( 'TOKEN is not defined!' ); } $signature = $_GET ["signature"]; $timestamp = $_GET ["timestamp"]; $nonce = $_GET ["nonce"]; $token = TOKEN; $tmpArr = array ( $token, $timestamp, $nonce ); // use SORT_STRING rule sort ( $tmpArr, SORT_STRING ); $tmpStr = implode ( $tmpArr ); $tmpStr = sha1 ( $tmpStr ); if ($tmpStr == $signature) { return true; } else { return false; } }
驗(yàn)證方法核心就是依據(jù)咱們之前網(wǎng)頁上設(shè)置的TOKEN來工作的,所以代碼上會(huì)用得到。
回復(fù)
回復(fù)的代碼需要依據(jù)客戶端發(fā)送的數(shù)據(jù)的類型來區(qū)分對(duì)待,類型這塊微信平臺(tái)會(huì)將數(shù)據(jù)打包好封裝起來,我們住需要調(diào)用內(nèi)部的MsgType進(jìn)行處理即可。
拓展
拓展部分,是我自己異想天開往上加的。
添加機(jī)器人
調(diào)用一個(gè)機(jī)器人接口,來代替自己發(fā)送回復(fù),技能讓用戶得到一個(gè)良好的用戶體驗(yàn),還能愉悅大眾,何樂而不為?
我這邊測(cè)試了兩個(gè)接口,一個(gè)是curl模式,一個(gè)是file_get_contents模式,都挺好用的啦。
<?php /** * 圖靈 機(jī)器人接口 * * 使用curl來進(jìn)行瀏覽器模擬并抓取數(shù)據(jù) */ function turing($requestStr) { // 圖靈機(jī)器人接口 $url = "http://www.tuling123.com/openapi/api"; // 用于POST請(qǐng)求的數(shù)據(jù) $data = array( 'key'=>"哈哈,這個(gè)key還是得你自己去申請(qǐng)的啦", 'info'=>$requestStr, ); // 構(gòu)造curl下載器 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $responseStr = curl_exec($ch); curl_close($ch); return $responseStr; } /** * 調(diào)用另外的接口 * @param unknown $req * @return mixed */ function test($req){ $url = "http://api.qingyunke.com/api.php?key=free&appid=0&msg=".$req; $result = file_get_contents($url); $result = json_decode($result, true); return $result['content']; } $req = 'hello'; $res = test($req); echo $res;
命令模式
手機(jī)相對(duì)于電腦一個(gè)很大的優(yōu)點(diǎn)就是便攜,我們雖然不能隨時(shí)隨地?cái)y帶電腦,但是卻能使用手機(jī)來代替。很多時(shí)候?qū)Ψ?wù)器的管理需要的命令很簡(jiǎn)單,但是遠(yuǎn)程登錄的時(shí)候也不方便。這個(gè)時(shí)候就用微信來幫忙傳話也是不錯(cuò)的啦。
我平時(shí)喜歡使用Python寫一些腳本,什么獲取本地IP,聊天,查看內(nèi)存,網(wǎng)速啥的,可謂是應(yīng)有盡有。這下也終于能有用武之地了。利用微信的關(guān)鍵字匹配,就可以簡(jiǎn)單的讓微信公眾號(hào)當(dāng)一個(gè)小小傳話員啦。
這里給個(gè)思路,具體實(shí)現(xiàn)起來也比較簡(jiǎn)單,當(dāng)做是文本來處理即可。
完整代碼
下面貼出我服務(wù)器上的完整代碼,有些私密的地方我做了些更改,屆時(shí)按照自己的情況進(jìn)行修改即可。
<?php /** * wechat php test */ // define your token define ( "TOKEN", "您的TOKEN" ); $wechatObj = new wechatCallbackapiTest (); // $wechatObj->valid(); // 調(diào)用回復(fù)信息方法 $wechatObj->responseMsg (); // 微信消息處理核心類 class wechatCallbackapiTest { public function valid() { $echoStr = $_GET ["echostr"]; // valid signature , option if ($this->checkSignature ()) { echo $echoStr; exit (); } else { echo "驗(yàn)證失?。?; } } public function responseMsg() { // get post data, May be due to the different environments // 類似$_POST但是可以接受XML數(shù)據(jù),屬于增強(qiáng)型 $postStr = $GLOBALS ["HTTP_RAW_POST_DATA"]; // extract post data if (! empty ( $postStr )) { /* * libxml_disable_entity_loader is to prevent XML eXternal Entity Injection, * the best way is to check the validity of xml by yourself */ // 不解析外部數(shù)據(jù),防止xxml漏洞 libxml_disable_entity_loader ( true ); $postObj = simplexml_load_string ( $postStr, 'SimpleXMLElement', LIBXML_NOCDATA ); $fromUsername = $postObj->FromUserName; $toUsername = $postObj->ToUserName; $keyword = trim ( $postObj->Content ); $time = time (); /* * 微信客戶端發(fā)送信息的時(shí)候會(huì)附帶一些參數(shù),詳見官方文檔。所以要根據(jù)不同的類型,來分別做相關(guān)的處理。 * 于是MsgType 就充當(dāng)這樣的一個(gè)區(qū)分的標(biāo)記 */ $msgType = $postObj->MsgType; /* * 當(dāng)有用戶關(guān)注后者退訂的時(shí)候,會(huì)觸發(fā)相應(yīng)的事件。所以再來個(gè)event事件的監(jiān)聽更為友好。 * $event = $postObj->Event. * 具體的參數(shù)信息,官網(wǎng)上很詳細(xì)。 */ $event = $postObj->Event; switch ($msgType) { // 文本消息 處理部分 case "text" : if (! empty ( $keyword )) { // 在此處進(jìn)行對(duì)關(guān)鍵字的匹配就可以實(shí)現(xiàn):針對(duì)不同關(guān)鍵字組裝的相應(yīng)數(shù)據(jù) if($keyword=='音樂' || $keyword == "music") { $msgType = 'music'; $musictitle = "The Mountain"; $musicdescription = "夏日舒心清涼歌曲"; $musicurl = "http://101.200.58.242/wx/themaintain.mp3"; $hqmusicurl = "http://101.200.58.242/wx/themaintain.mp3"; musicMessageHandle($fromUsername, $toUsername, $time, $msgType, $musictitle, $musicdescription, $musicurl, $hqmusicurl); }elseif($keyword == '1'){ $msgType = 'text'; $contentStr = "人生得意須盡歡,莫使金樽空對(duì)月!"; textMessageHandle($fromUsername, $toUsername, $time, $msgType, $contentStr); }elseif($keyword == '命令模式'){ $msgType = 'text'; $contentStr = "進(jìn)入命令模式,開始對(duì)服務(wù)器進(jìn)行管理!\n接下來將依據(jù)您輸入的命令對(duì)服務(wù)器進(jìn)行管理!"; textMessageHandle($fromUsername, $toUsername, $time, $msgType, $contentStr); }else { // 直接調(diào)用 機(jī)器人接口,與用戶進(jìn)行交流 $msgType = "text"; $contentStr = turing($keyword)!=""?turing($keyword):"這里是微信 純文本測(cè)試數(shù)據(jù)!"; textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr ); } } else { echo "您得輸入點(diǎn)數(shù)據(jù),我才能回復(fù)不是!"; } break; // 接收?qǐng)D片信息 case "image" : if (! empty ( $keyword )) { // $msgType = "image"; $contentStr = "您發(fā)送的圖片看起來還真不錯(cuò)!"; textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr ); } else { echo "服務(wù)器沒能收到您發(fā)送的圖片!"; } break; // 接收語音信息 case "voice" : if (! empty ( $keyword )) { // $msgType = "voice"; $contentStr = "您發(fā)送的語音聽起來還真不錯(cuò)!"; textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr ); } else { echo "服務(wù)器沒能收到您發(fā)送的語音!"; } break; // 接收視頻信息 case "video" : if (! empty ( $keyword )) { // $msgType = "video"; $contentStr = "您發(fā)送的視頻看起來還真不錯(cuò)!"; textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr ); } else { echo "服務(wù)器沒能收到您發(fā)送的視頻!"; } break; // 接收視頻信息 case "shortvideo" : if (! empty ( $keyword )) { // $msgType = "shortvideo"; $contentStr = "您發(fā)送的小視頻看起來還真不錯(cuò)!"; textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr ); } else { echo "服務(wù)器沒能收到您發(fā)送的小視頻!"; } break; // 接收位置信息 case "location" : if (! empty ( $keyword )) { // $msgType = "location"; $contentStr = "您發(fā)送的位置已被接收!"; textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr ); } else { echo "服務(wù)器沒能收到您發(fā)送的位置!"; } break; // 接收視頻信息 case "link" : if (! empty ( $keyword )) { // $msgType = "link"; $contentStr = "您發(fā)送的鏈接看起來還真不錯(cuò)!"; textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr ); } else { echo "服務(wù)器沒能收到您發(fā)送的鏈接!"; } break; // 對(duì)事件進(jìn)行偵聽 case "event": switch ($event) { case "subscribe": // 發(fā)送一些消息! $msgType = 'text'; $contentStr = "終于等到你!"; textMessageHandle($fromUsername, $toUsername, $time, $msgType, $contentStr); break; } break; default : break; } } else { echo ""; exit (); } } private function checkSignature() { // you must define TOKEN by yourself if (! defined ( "TOKEN" )) { throw new Exception ( 'TOKEN is not defined!' ); } $signature = $_GET ["signature"]; $timestamp = $_GET ["timestamp"]; $nonce = $_GET ["nonce"]; $token = TOKEN; $tmpArr = array ( $token, $timestamp, $nonce ); // use SORT_STRING rule sort ( $tmpArr, SORT_STRING ); $tmpStr = implode ( $tmpArr ); $tmpStr = sha1 ( $tmpStr ); if ($tmpStr == $signature) { return true; } else { return false; } } } /** * 定義為心中想難關(guān)的六個(gè)接口的數(shù)據(jù)發(fā)送格式模板 */ function textMessageHandle($fromUsername, $toUsername, $time, $msgType, $contentStr) { $textTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[%s]]></MsgType> <Content><![CDATA[%s]]></Content> <FuncFlag>0</FuncFlag> </xml>"; $resultStr = sprintf ( $textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr ); echo $resultStr; } function imageMessageHandle($fromUsername, $toUsername, $time, $msgType, $contentStr) { $imageTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[%s]]></MsgType> <Content><![CDATA[%s]]></Content> <PicUrl><![CDATA[this is a url]]></PicUrl> <MediaId><![CDATA[media_id]]></MediaId> <MsgId>1234567890123456</MsgId> </xml>"; $resultStr = sprintf ( $textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr ); echo $resultStr; } function musicMessageHandle($fromUsername, $toUsername, $time, $msgType, $musictitle, $musicDescription, $musicurl, $hqmusicurl) { $musicTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[%s]]></MsgType> <Music> <Title><![CDATA[%s]]></Title> <Description><![CDATA[%s]]></Description> <MusicUrl><![CDATA[%s]]></MusicUrl> <HQMusicUrl><![CDATA[%s]]></HQMusicUrl> </Music> </xml>"; $resultStr = sprintf($musicTpl, $fromUsername, $toUsername, $time, $msgType, $musictitle, $musicDescription, $musicurl, $hqmusicurl); echo $resultStr; } /** * 圖靈 機(jī)器人接口 * * 使用curl來進(jìn)行瀏覽器模擬并抓取數(shù)據(jù) */ function turing($requestStr) { /* // 圖靈機(jī)器人接口 $url = "http://www.tuling123.com/openapi/api"; // 用于POST請(qǐng)求的數(shù)據(jù) $data = array( "key"=>"您在圖靈機(jī)器人官網(wǎng)上申請(qǐng)的key", "info"=>$requestStr ); // 構(gòu)造curl下載器 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $requestStr = curl_exec($ch); curl_close($ch); return responseStr; */ $url = "http://api.qingyunke.com/api.php?key=free&appid=0&msg=".$requestStr; $result = file_get_contents($url); $result = json_decode($result, true); return $result['content']; } ?>
總結(jié)
最后來回顧一下,本次試驗(yàn)用到了哪些知識(shí)點(diǎn)。
- PHP的面向?qū)ο蠓椒ň幊毯?jiǎn)單實(shí)現(xiàn)。
- 接口處理的兩種方式
- 微信公眾號(hào)后臺(tái)私服的接入,處理,反饋。
- 前后端的交互,以及聊天機(jī)器人的應(yīng)用。
其實(shí),這些代碼跟我一開始的設(shè)想還是差別挺大的,原本是想實(shí)現(xiàn)一個(gè)“遙控器”,晚上想睡覺之前,用微信發(fā)一條命令“打開電熱毯”,半個(gè)小時(shí)后,電視看完了,去睡覺的時(shí)候發(fā)現(xiàn)被窩很暖和,是的,只要加上點(diǎn)硬件,這很容易實(shí)現(xiàn)啦再者冰箱了,電視了統(tǒng)統(tǒng)可以完成,那樣估計(jì)就診的是“智能家居”了吧。
- 微信小程序上傳圖片到服務(wù)器實(shí)例代碼
- 本地搭建微信小程序服務(wù)器的實(shí)現(xiàn)方法
- 微信小程序圖片選擇、上傳到服務(wù)器、預(yù)覽(PHP)實(shí)現(xiàn)實(shí)例
- 微信小程序訪問node.js接口服務(wù)器搭建教程
- 微信小程序 消息推送php服務(wù)器驗(yàn)證實(shí)例詳解
- 微信公眾平臺(tái)開發(fā)-微信服務(wù)器IP接口實(shí)例(含源碼)
- 微信小程序 解決請(qǐng)求服務(wù)器手機(jī)預(yù)覽請(qǐng)求不到數(shù)據(jù)的方法
- 微信小程序 Windows2008 R2服務(wù)器配置TLS1.2方法
- java微信開發(fā)API第一步 服務(wù)器接入
- C#微信開發(fā)(服務(wù)器配置)
相關(guān)文章
最新制作ThinkPHP3.2.3完全開發(fā)手冊(cè)
本文給大家分享的是作者花了3個(gè)多小時(shí)制作完成的ThinkPHP3.2.3完全開發(fā)手冊(cè),非常的細(xì)致全面,有需要的小伙伴可以參考下2015-11-11Laravel 已登陸用戶再次查看登陸頁面的自動(dòng)跳轉(zhuǎn)設(shè)置方法
今天小編就為大家分享一篇Laravel 已登陸用戶再次查看登陸頁面的自動(dòng)跳轉(zhuǎn)設(shè)置方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09支付寶支付開發(fā)——當(dāng)面付條碼支付和掃碼支付實(shí)例
這篇文章主要介紹了支付寶支付開發(fā)——當(dāng)面付條碼支付和掃碼支付實(shí)例,具有一定的參考價(jià)值,有需要的可以了解一下。2016-11-11php微信公眾號(hào)js-sdk開發(fā)應(yīng)用
這篇文章主要為大家詳細(xì)介紹了php微信公眾號(hào)js-sdk開發(fā)應(yīng)用的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11laravel項(xiàng)目利用twemproxy部署redis集群的完整步驟
Twemproxy是一個(gè)代理服務(wù)器,可以通過它減少M(fèi)emcached或Redis服務(wù)器所打開的連接數(shù)。下面這篇文章主要給大家介紹了關(guān)于laravel項(xiàng)目利用twemproxy部署redis集群的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-05-05