PHP开发之用微信远程遥控服务器

 摘要

微信公众好的开发很火,小程序更火。于是也凑个热闹,尝试了一把。

大致的功能还是有的,不过是不全,很多地方我没有进行处理。不过对于纯文本方式的交流,已经没有问题啦。

命令

音乐

环境搭建

下面大致的讲讲微信公众号的原理吧。可能我理解的有些不到位,如果有些许不当,欢迎批评指教。

客户端发送给微信平台请求,微信平台将请求转发给私服,交给程序处理之后,获取到私服的处理结果,然后反馈给客户端。

当然,这其中起到核心作用的自然是“微信公众平台”啦。相当于提供了一个舞台,一个能让各位能人异士展现出各自的特色的平台。其实,不仅微信如此,阿里同样是这样,如此各大电商才能一展手脚不是。

开启配置

这第一步,就是先申请一个微信开发者账号,个人的话选择订阅号就足够了。网上相关的资料很多,也很详细,我就不多说了。咱们直奔主题好了。

首先登陆开发者账号成功后,开启服务器端的设置即可,如下图

开启配置

开启完成,根据自己服务器的情况进行一下设置即可。

  • URL就是你的私服用于处理请求数据的地址
  • TOKEN就是一个令牌,随便设置。不过记住待会自己的代码上会用到。
  • 至于密钥嘛,没什么较大的作用,暂且可以先不用管。

按需设置

按需设置

设置完,就可以启用了。这就好比家里的电线全部装修好了,现在要使用,按下开关一样。如下图

启用服务器配置

启用服务器配置

服务器环境

关于服务器这块,官网上讲解的也是很详细的啦。

https://mp.weixin.qq.com/wiki

我们还可以下载官方的demo来模拟。

官方样本

官方样本

代码也很简单。基本上学过了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;

}

}

}

?>

核心思路,无非检验一下签名,处理一下请求,反馈一下结果罢了。

这里我不得不想说的就是,我觉得腾讯其实可以将那些个模板什么的去掉,直接暴露出黑盒模式,这样的话安全性会更高一点。很多时候,权限放的越开,效果可能越差。

核心类

接下来就是我自己的处理逻辑了,参照官方文档。微信公众好上有6大接收接口,三大回复接口。依据MsgType即可判定。

接口详情

验证

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;

}

}

验证方法核心就是依据咱们之前网页上设置的TOKEN来工作的,所以代码上会用得到。

回复

回复的代码需要依据客户端发送的数据的类型来区分对待,类型这块微信平台会将数据打包好封装起来,我们住需要调用内部的MsgType进行处理即可。

拓展

拓展部分,是我自己异想天开往上加的。

添加机器人

调用一个机器人接口,来代替自己发送回复,技能让用户得到一个良好的用户体验,还能愉悦大众,何乐而不为?

我这边测试了两个接口,一个是curl模式,一个是file_get_contents模式,都挺好用的啦。

<?php

/**

* 图灵 机器人接口

*

* 使用curl来进行浏览器模拟并抓取数据

*/

function turing($requestStr) {

// 图灵机器人接口

$url = "http://www.tuling123.com/openapi/api";

// 用于POST请求的数据

$data = array(

'key'=>"哈哈,这个key还是得你自己去申请的啦",

'info'=>$requestStr,

);

// 构造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;

}

/**

* 调用另外的接口

* @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;

命令模式

手机相对于电脑一个很大的优点就是便携,我们虽然不能随时随地携带电脑,但是却能使用手机来代替。很多时候对服务器的管理需要的命令很简单,但是远程登录的时候也不方便。这个时候就用微信来帮忙传话也是不错的啦。

我平时喜欢使用Python写一些脚本,什么获取本地IP,聊天,查看内存,网速啥的,可谓是应有尽有。这下也终于能有用武之地了。利用微信的关键字匹配,就可以简单的让微信公众号当一个小小传话员啦。

这里给个思路,具体实现起来也比较简单,当做是文本来处理即可。

完整代码

下面贴出我服务器上的完整代码,有些私密的地方我做了些更改,届时按照自己的情况进行修改即可。

<?php

/**

* wechat php test

*/

// define your token

define ( "TOKEN", "您的TOKEN" );

$wechatObj = new wechatCallbackapiTest ();

// $wechatObj->valid();

// 调用回复信息方法

$wechatObj->responseMsg ();

// 微信消息处理核心类

class wechatCallbackapiTest {

public function valid() {

$echoStr = $_GET ["echostr"];

// valid signature , option

if ($this->checkSignature ()) {

echo $echoStr;

exit ();

} else {

echo "验证失败!";

}

}

public function responseMsg() {

// get post data, May be due to the different environments

// 类似$_POST但是可以接受XML数据,属于增强型

$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

*/

// 不解析外部数据,防止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 ();

/*

* 微信客户端发送信息的时候会附带一些参数,详见官方文档。所以要根据不同的类型,来分别做相关的处理。

* 于是MsgType 就充当这样的一个区分的标记

*/

$msgType = $postObj->MsgType;

/*

* 当有用户关注后者退订的时候,会触发相应的事件。所以再来个event事件的监听更为友好。

* $event = $postObj->Event.

* 具体的参数信息,官网上很详细。

*/

$event = $postObj->Event;

switch ($msgType) {

// 文本消息 处理部分

case "text" :

if (! empty ( $keyword )) {

// 在此处进行对关键字的匹配就可以实现:针对不同关键字组装的相应数据

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 = "人生得意须尽欢,莫使金樽空对月!";

textMessageHandle($fromUsername, $toUsername, $time, $msgType, $contentStr);

}elseif($keyword == '命令模式'){

$msgType = 'text';

$contentStr = "进入命令模式,开始对服务器进行管理!\n接下来将依据您输入的命令对服务器进行管理!";

textMessageHandle($fromUsername, $toUsername, $time, $msgType, $contentStr);

}else {

// 直接调用 机器人接口,与用户进行交流

$msgType = "text";

$contentStr = turing($keyword)!=""?turing($keyword):"这里是微信 纯文本测试数据!";

textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr );

}

} else {

echo "您得输入点数据,我才能回复不是!";

}

break;

// 接收图片信息

case "image" :

if (! empty ( $keyword )) {

// $msgType = "image";

$contentStr = "您发送的图片看起来还真不错!";

textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr );

} else {

echo "服务器没能收到您发送的图片!";

}

break;

// 接收语音信息

case "voice" :

if (! empty ( $keyword )) {

// $msgType = "voice";

$contentStr = "您发送的语音听起来还真不错!";

textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr );

} else {

echo "服务器没能收到您发送的语音!";

}

break;

// 接收视频信息

case "video" :

if (! empty ( $keyword )) {

// $msgType = "video";

$contentStr = "您发送的视频看起来还真不错!";

textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr );

} else {

echo "服务器没能收到您发送的视频!";

}

break;

// 接收视频信息

case "shortvideo" :

if (! empty ( $keyword )) {

// $msgType = "shortvideo";

$contentStr = "您发送的小视频看起来还真不错!";

textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr );

} else {

echo "服务器没能收到您发送的小视频!";

}

break;

// 接收位置信息

case "location" :

if (! empty ( $keyword )) {

// $msgType = "location";

$contentStr = "您发送的位置已被接收!";

textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr );

} else {

echo "服务器没能收到您发送的位置!";

}

break;

// 接收视频信息

case "link" :

if (! empty ( $keyword )) {

// $msgType = "link";

$contentStr = "您发送的链接看起来还真不错!";

textMessageHandle ( $fromUsername, $toUsername, $time, $msgType, $contentStr );

} else {

echo "服务器没能收到您发送的链接!";

}

break;

// 对事件进行侦听

case "event":

switch ($event) {

case "subscribe":

// 发送一些消息!

$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;

}

}

}

/**

* 定义为心中想难关的六个接口的数据发送格式模板

*/

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;

}

/**

* 图灵 机器人接口

*

* 使用curl来进行浏览器模拟并抓取数据

*/

function turing($requestStr) {

/* // 图灵机器人接口

$url = "http://www.tuling123.com/openapi/api";

// 用于POST请求的数据

$data = array(

"key"=>"您在图灵机器人官网上申请的key",

"info"=>$requestStr

);

// 构造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'];

}

?>

总结

最后来回顾一下,本次试验用到了哪些知识点。

  • PHP的面向对象方法编程简单实现。
  • 接口处理的两种方式
  • 微信公众号后台私服的接入,处理,反馈。
  • 前后端的交互,以及聊天机器人的应用。

其实,这些代码跟我一开始的设想还是差别挺大的,原本是想实现一个“遥控器”,晚上想睡觉之前,用微信发一条命令“打开电热毯”,半个小时后,电视看完了,去睡觉的时候发现被窝很暖和,是的,只要加上点硬件,这很容易实现啦再者冰箱了,电视了统统可以完成,那样估计就诊的是“智能家居”了吧。

以上是 PHP开发之用微信远程遥控服务器 的全部内容, 来源链接: utcz.com/p/221548.html

回到顶部