文件路径
/Users/jiao/Desktop/全栈教程/9微信开发/Day-1
一、1.4、微信公众平台的两种运行模式
1.4.1、编辑模式
使用微信公众号提供的统一的微信公众管理平台,进行操作。所有功能都已经写好,直接使用即可。只需要编辑相关内容信息。适合不会编程的人使用。
如果我们使用了编辑模式,它就不能很好的与我们现有的公司的系统结合,所以如果企业中使用了此方式就和我们开发没有太多关系了。
1.4.2、开发者模式
微信公众平台提供了服务器接入的方式,具有开发能力的运营者,可以进行定制开发。
开发者模式:特别适合企业,所以我们说的微信公众号开发就是说开发者模式的学习与使用。
开发者模式:需要一台或多台外网能访问到的服务器
注意:微信公众平台管理中,编辑模式和开发者模式是互斥的,也就是说只能使用其中一种。
开发者模式有2种请求方式:
被动:
主动(curl发起的请求):
二、编辑模式
微信给我们提供好的一个后台管理平台,通过此管理平台,可以进行公众号的管理操作。
2.1、编辑模式与开发模式切换
停用开发者模式,启用编辑模式
2.2、被关注时自动回复
2.3、关键词回复
补充:
半匹配关键词,要想让它回复,必须在你发送的文本中要有全的关键词才可以。
全匹配 必须和关键词一致才可以。
三、开发公众号准备
3.1、外网服务器(方案一)
因为在开发模式下面,需要一或N台外网服务器,给微信公众平台服务器能访问到,就有如下几种选择方案
阿里云 ECS
腾讯云
百度云
新浪Sae
公司机房实体机
而这些方案都是需要一定的费用。
此方案,公众号上线必须要在此方案中选择一个。但是在开发或学习队段我们有更好的选择。
3.2、内网穿透简介(方案二)【开发推荐】.
natapp软件,它给我们提供一个外网的域名,通过此软件,把我们内网的IP和端口,映射到natapp的服务器上面,这样公众号服务器访问到natapp的服务器上,然后natapp在把请求转发到内网中,从而就实现了内网的穿透。
软件网址:https://natapp.cn/
注:软件平台,需要注册,注册是免费不花钱的。可以免费使用,需要实名认证。成年。
3.6、natapp软件配置
软件下载: https://natapp.cn/#download
有一个配置文件 【config.ini】
[default]
# 对应一条隧道的authtoken值,在后台列表中复制过来
authtoken=aabbcc
# 日志等级,默认即可
loglevel=DEBUG
在本机进行一次替换已购买好的隧道中的authtoken值。
3.7、运行natapp
可以用外网的域名来访问本机的web服务了
总结:
注册此平台账号,购买隧道得到了authtoken值
配置软件的相关的配置 config.ini文件
配置多端口的虚拟主机
运行natapp 一定要注意目录地址
四、开发模式
具有开发能力的人员,使用此方式来进行公众号的接入开发。
4.1、开启开发模式
4.2、api文档位置
接入指南
https://mp.weixin.qq.com/wiki
通过这里,就可以学习到接入配置设置
4.3、接入原理图
微信开发第1步,也是初次上线时第1次
4.4、代码接入
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319
到公众平台后台去,到基本配置修改配置
4.5、测试开发微信公众平台的使用
因为个人注册未认证或公司已使用的平台不方便调试,还有一些个人注册的订阅号权限过小,有些功能无法开发测试,所以公众平台提供一个,测试平台,方便开发者开发。
进入测试平台
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421137522
进入了测试平台后,就需要再次接入一次。
接入成功后,就可以进行相关的业务代码的编写了。
总结:
需要一个外网能访问的域名和服务器
创建一个web运行环境
创建一个文件
在公众平台开启开发者模式并填写好相对应的URL地址和token值
根据官方文档进行校验,校验通过后返回相对应的字符串
接入成功后就可以进行相关的业务代码的编写
五、常用接收消息
# 获取原生请求数据
$postStr = file_get_contents('php://input');
//php5.5之前可以使用另一种获取原生请求数据
$GLOBALS["HTTP_RAW_POST_DATA"];
# xml转化成对象
simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
//LIBXML_NOCDATA 把cdata括起来的字符串解析为字符串,如果xml节点中有富文本的话,必须使用 LIBXML_NOCDATA
sprintf//格式化输出
%s 字符串
%d 数字
小结:
cdata:在节点中有富文本时才使用它。
获取原生请求数据,我们用file_get_contents(php://input);
<?php
#var_dump(json_decode(file_get_contents('php://input'),true));
# 获取原生的请求数据
$xml = file_get_contents('php://input');
# 把xml转换为object对象来处理
$obj = simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOCDATA); # SimpleXMLElement LIBXML_NOCDATA
var_dump($obj);
// 获取name
echo $obj->stus->name;
echo "\n";
$name = '张三';
$age = 20;
# 数据展开一定要是索引数组
$arr[] = $name;
$arr[] = $age;
#$str = "小伙子姓名:{$name} 芳龄:${age}";
$str = "小伙子姓名:%s 芳龄:%d";
# 格式化替换输出
$str = sprintf($str,$name,$age);
# 数据展开
$str = sprintf($str,...$arr);
echo $str;
5.2、微信的6大接收接口
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140453
当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
6大接收接口
文本(text)、图片(image)、语音(voice)、视频(video)、地理位置(location)、链接(link)
当然如果用了natapp我们还可以通过4040来查看接受的数据
六、常用被动回复消息
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543
6.1、介绍
当用户发送消息给公众号时或某些特定的用户操作引发的事件推送时,会产生一个POST请求,开发者可以在返回特定XML结构,来对该消息进行响应。
现支持回复类型有:
文本、图片、图文、语音、视频、音乐
严格来说:发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复(响应)。
6.2、回复文本消息
改进版本
6.3、回复图片
6.4、回复音乐
八、课程代码
<?php
/**
* 公众号被动接受处理类
*/
$wx = new Wx();
class Wx {
// 微信后台设置的token值 php7.1之后可以加权限 private
const TOKEN = 'weixin';
// 构造方法
public function __construct(){
// 判断是否是第1次接入 echostr
if (!empty($_GET['echostr'])) {
echo $this->checkSign();
}else{
// 接受处理数据
$this->acceptMsg();
}
}
/**
* 接收公众号发过来的数据
* @return [type] [description]
*/
private function acceptMsg(){
// 获取原生请求数据
$xml = file_get_contents('php://input');
# 把xml转换为object对象来处理
$obj = simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOCDATA);
// 写接受日志
$this->writeLog($xml);
// 处理回复消息
// 1、判断消息类型
// 2、根据不同的类型,回复处理不同信息
// 判断类型
$MsgType = $obj->MsgType;
/*switch ($MsgType) {
case 'text':
$str = '<xml>
<ToUserName><![CDATA[%s]]>
</ToUserName>
<FromUserName><![CDATA[%s]]>
</FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]>
</MsgType>
<Content><![CDATA[%s]]>
</Content>
</xml>';
// 格式化替换输出
$str = sprintf($str,$obj->FromUserName,$obj->ToUserName,time(),'公众号:'.$obj->Content);
// 写日志
$this->writeLog($str,2);
echo $str;
break;
}*/
$fun = $MsgType.'Fun';
// 调用方法
//echo $ret = $this->$fun($obj);
echo $ret = call_user_func([$this,$fun],$obj);
// 写发送日志
$this->writeLog($ret,2);
}
// 处理回复文本
private function textFun($obj){
$content = $obj->Content;
// 回复文本
if('音乐' == $content){
return $this->musicFun($obj);
}
$content = '公众号:'.$content;
return $this->createText($obj,$content);
}
// 回复图片消息
private function imageFun($obj){
$mediaid = $obj->MediaId;
return $this->createImage($obj,$mediaid);
}
// 回复音乐
private function musicFun($obj){
// 图片媒体ID
$mediaid = '1QgKrdNTGOexznSBvGTiN7DTN3rPm1is0UhZ1Axfq7dBtMIf2zFL-MQH6Wb95DXc';
// 音乐播放地址
$url = 'https://wx.1314000.cn/mp3/ykz.mp3';
return $this->createMusic($obj,$url,$mediaid);
}
// 生成文本消息XML
private function createText($obj,string $content){
$xml = '<xml>
<ToUserName><![CDATA[%s]]>
</ToUserName>
<FromUserName><![CDATA[%s]]>
</FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]>
</MsgType>
<Content><![CDATA[%s]]>
</Content>
</xml>';
// 格式化替换输出
$str = sprintf($xml,$obj->FromUserName,$obj->ToUserName,time(),$content);
return $str;
}
// 生成图片消息xml
private function createImage($obj,string $mediaid){
$xml = '<xml>
<ToUserName><![CDATA[%s]]>
</ToUserName>
<FromUserName><![CDATA[%s]]>
</FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[image]]>
</MsgType>
<Image>
<MediaId><![CDATA[%s]]>
</MediaId>
</Image>
</xml>';
// 格式化替换输出
$str = sprintf($xml,$obj->FromUserName,$obj->ToUserName,time(),$mediaid);
return $str;
}
// 生成音乐XML消息
private function createMusic($obj,string $url,string $mediaid){
$xml = '<xml>
<ToUserName><![CDATA[%s]]>
</ToUserName>
<FromUserName><![CDATA[%s]]>
</FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[music]]>
</MsgType>
<Music>
<Title><![CDATA[夜空中最亮的星]]>
</Title>
<Description><![CDATA[一首非常好的歌]]>
</Description>
<MusicUrl><![CDATA[%s]]>
</MusicUrl>
<HQMusicUrl><![CDATA[%s]]>
</HQMusicUrl>
<ThumbMediaId><![CDATA[%s]]>
</ThumbMediaId>
</Music>
</xml>';
// 格式化替换输出
$str = sprintf($xml,$obj->FromUserName,$obj->ToUserName,time(),$url,$url,$mediaid);
return $str;
}
/**
* 写日志
* @param string $xml 写入的xml
* @param int|integer $flag 标识 1:请求 2:发送
* @return [type] [description]
*/
private function writeLog(string $xml,int $flag=1){
$flagstr = $flag == 1 ? '接受' : '发送';
$prevstr = '【'.$flagstr.'】'.date('Y-m-d')."-----------------------------\n";
$log = $prevstr.$xml."\n---------------------------------------------\n";
// 写日志 追加的形式去写入
file_put_contents('wx.xml',$log,FILE_APPEND);
return true;
}
/**
* 初次接入校验
* @return [type] [description]
*/
private function checkSign(){
// 得到微信公众号发过来的数据
$input = $_GET;
// 把echostr放在临时变量中
$echostr = $input['echostr'];
$signature = $input['signature'];
// 在数组中删除掉
unset($input['echostr'],$input['signature']);
// 在数据中添加一个字段token
$input['token'] = self::TOKEN;
// 进行字典排序
$tmpStr = implode( $input );
// 进行加密操作
$tmpStr = sha1( $tmpStr );
// 进行比对
if ($tmpStr === $signature) {
return $echostr;
}
return '';
}
}