微信公众号开发(四)自定义菜单
1、说明
微信的自定义菜单分为普通菜单和个性化菜单,个性化菜单可以根据地区、性别、语言等为不同的用户展示不同的菜单,定义个性化菜单之前必须定义普通菜单,删除普通菜单则个性化菜单也会删除,自定义菜单有以下需要注意的地方:
- 自定义菜单最多包括三个一级菜单,每个一级菜单最多包括5个二级菜单。
- 一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”显示。
- 创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。
本项目的目录结构如下:
create_menu.php:创建菜单
delete_menu.php:删除菜单
get_menu.php:查询菜单
index.php:接收和处理微信服务器的消息
output_log.php、output_query.php、Utils.php可以参见微信公众号开发(一),主要用于日志的打印,另外Utils.php增加了两函数,分别用于发送请求和获取access_token
Utils.php
<?php
class Utils
{
/**
* 捕获RUL查询字符串到query.xml
*/
public static function traceHttp()
{
$content = date('Y-m-d H:i:s')."\n\rremote_ip:".$_SERVER["REMOTE_ADDR"].
"\n\r".$_SERVER["QUERY_STRING"]."\n\r\n\r";
$max_size = 1000;
$log_filename = "./query.xml";
if (file_exists($log_filename) and (abs(filesize($log_filename))) > $max_size){
unlink($log_filename);
}else {
}
file_put_contents($log_filename, $content, FILE_APPEND);
}
/**
* 打印日志到log.xml
* @param $log_content:日志内容
* @param string $type:日志来源,默认‘用户’
*/
public static function logger($log_content, $type = '用户')
{
$max_size = 3000;
$log_filename = "./log.xml";
if (file_exists($log_filename) and (abs(filesize($log_filename)) >
$max_size)) {
unlink($log_filename);
}
file_put_contents($log_filename, "$type ".date('Y-m-d H:i:s')."\n\r".$log_content."\n\r",
FILE_APPEND);
}
/**
* 获取access_token
* @return mixed
*/
public static function get_access_token()
{
$appid = "wx07fff9c79a410b69"; //需替换成你的appID
$appsecret = "092c0c0c5bd62f66b76ad241612915fb"; //需替换成你的appsecret
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&".
"appid=$appid&secret=$appsecret";
$output = Utils::https_request($url);
$jsoninfo = json_decode($output, true);
return $jsoninfo["access_token"];
}
/**
* 发送请求
* @param $url:地址
* @param null $data:post的数据
* @return mixed:请求返回的结果
*/
public static function https_request($url, $data = null)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
if (!empty($data)){
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($curl);
curl_close($curl);
return $output;
}
}
2、自定义菜单的类型
自定义菜单有8种类型:
- click:点击推事件,点击后弹出“获取中...”,微信服务器会通过消息接口推送消息类型为event的结构给开发者,并带上按钮中开发者填写的key值,开发者可用此key值与用户进行交互。
- view:跳转URL,用户点击后直接打开微信浏览器跳转到该URL的地址。
- scancode_push:扫码推事件,点击按钮后,微信客户端将调用“扫一扫”功能,完成扫码后在微信浏览器显示扫码结果(如果是URL,则直接进入该URL),且会将扫码结果传送给开发者,此时可以下发消息给用户,但是用户收不到该消息。
- scancode_waitmsg:扫码推事件且弹出“获取中...”提示框,用户点击按钮后客户端将调用“扫一扫”功能,将扫码结果传给开发者,同时收起“扫一扫”功能,然后弹出“获取中...”提示框,随后可能收到开发者下发的消息。
- pic_sysphoto:弹出系统拍照发图用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。
- pic_photo_or_album:弹出拍照或者相册发图用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。
- pic_weixin:弹出相册发图器用户点击按钮后,微信客户端将调起相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。
- location_select:弹出地理位置选择器用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,随后可能会收到开发者下发的消息。
3、创建普通自定义菜单
创建自定义普通的菜单接口是: https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN,还需要以post方式将菜单的内容以该接口发送给微信服务器。具体的菜单项的含义请参见自定义菜单文档。
create_menu.php
<?php
require_once('./Utils.php');
//菜单字符串
$menujson = '{
"button": [
{
"name": "扫码",
"sub_button": [
{
"type": "scancode_waitmsg",
"name": "扫码带提示",
"key": "button_0_0"
},
{
"type": "scancode_push",
"name": "扫码推事件",
"key": "button_0_1"
}
]
},
{
"name": "发图",
"sub_button": [
{
"type": "pic_sysphoto",
"name": "系统拍照发图",
"key": "button_1_0"
},
{
"type": "pic_photo_or_album",
"name": "拍照或者相册发图",
"key": "button_1_1"
},
{
"type": "pic_weixin",
"name": "微信相册发图",
"key": "button_1_2"
}
]
},
{
"name": "其他",
"sub_button": [
{
"type": "location_select",
"name": "发送位置",
"key": "button_2_0"
},
{
"type": "click",
"name": "单击",
"key": "button_2_1"
},
{
"type": "view",
"name": "百度",
"url": "http://www.baidu.com"
}
]
}
]
}';
$url = $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token="
.Utils::get_access_token();
//创建菜单
$result = Utils::https_request($url, $menujson);
//返回{"errcode":0,"errmsg":"ok"}表示成功
echo $result;
返回结果为{"errcode":0,"errmsg":"ok"}表示成功。注意重新关注公众号才能马上看到效果。截图如下:
再写一个index.php文件来接收服务器推送的消息:
<?php
//设置时区
date_default_timezone_set("Asia/Shanghai");
//定义TOKEN常量,这里的"weixin"就是在公众号里配置的TOKEN
require_once("Utils.php");
//打印请求的URL查询字符串到query.xml
Utils::traceHttp();
$wechatObj = new wechatCallBackapiTest();
$wechatObj->responseMsg();
class wechatCallBackapiTest
{
public function responseMsg()
{
//获取post过来的数据,它一个XML格式的数据
$postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
//将数据打印到log.xml
Utils::logger($postStr);
if (!empty($postStr)) {
//将XML数据解析为一个对象
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$RX_TYPE = trim($postObj->MsgType);
//消息类型分离
switch($RX_TYPE)
{
case "event":
$result = $this->receiveEvent($postObj);
break;
case "image":
$result = $this->receiveImage($postObj);
break;
case "location":
$result = $this->receiveLocation($postObj);
break;
default:
$result = "";
break;
}
Utils::logger($result, '公众号');
echo $result;
}else {
echo "";
exit;
}
}
/*
* 接收事件消息
*/
private function receiveEvent($object)
{
switch ($object->Event)
{
case "subscribe": //关注公众号事件
$content = "欢迎关注微微一笑很倾城";
break;
case "unsubscribe":
$content = "";
break;
case "CLICK": //注意这里是大写的CLICK
$content = $object->EventKey;
break;
case "scancode_waitmsg":
sleep(2); //方便测试观看效果
$content = $object->EventKey." ".$object->ScanCodeInfo->ScanResult;
break;
case "scancode_push":
sleep(2); //方便测试观看效果
$content = $object->EventKey." ".$object->ScanCodeInfo->ScanResult;
break;
case "pic_weixin":
//回复无效
//$content = $object->Event." ".$object->EventKey;
exit;
break;
case "pic_photo_or_album":
//回复无效
//$content = $object->Event." ".$object->EventKey;
exit;
break;
case "pic_sysphoto":
//回复无效
exit;
break;
case "location_select":
//回复无效
//$content = "location";
exit;
break;
default:
$content = "";
break;
}
$result = $this->transmitText($object, $content);
return $result;
}
/**
* 接收图片消息,通过MediaId回复相同的图片给用户
*/
private function receiveImage($object)
{
$content = array("MediaId"=>$object->MediaId);
$result = $this->transmitImage($object, $content);
return $result;
}
/**
* 接收位置消息
*/
private function receiveLocation($object)
{
$content = "你发送的是位置,纬度为:".$object->Location_X.";经度为:".
$object->Location_Y.";缩放级别为:".$object->Scale.";位置为:".$object->Label;
$result = $this->transmitText($object, $content);
return $result;
}
/**
* 回复文本消息
*/
private function transmitText($object, $content)
{
$xmlTpl = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime><![CDATA[%s]]></CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>";
$result = sprintf($xmlTpl, $object->FromUserName, $object->ToUserName, time(), $content);
return $result;
}
/**
* 回复图片消息
*/
private function transmitImage($object, $imageArray)
{
$itemTpl = "<Image>
<MediaId><![CDATA[%s]]></MediaId>
</Image>";
$item_str = sprintf($itemTpl, $imageArray['MediaId']);
$textTpl = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
$item_str
</xml>";
$result = sprintf($textTpl, $object->FromUserName, $object->ToUserName,
time());
return $result;
}
}
点击“单击”菜单后,直接推送了一个CLICK事件过来。
点击“发送位置”,除开推送location_select事件过来之外,还推送一个loaction消息过来。
点击view“百度”菜单,什么也没推送过来,在微信浏览器打开了百度网页。
点击“系统拍照发图”菜单,直接弹出了相机功能,拍完照后直接发送一个图片消息过来,没有事件消息。
点击“”微信相册发图“菜单后,直接弹出相册,选择2张图片发送和,显示发送了一个pic_weixin的事件,然后分别有发送了2个图片消息,可分别对2个图片消息做回复,对事件消息回复无效。
单击”拍照或相册发图“,会弹出选择框供用户选择,除了推送的事件是pic_photo_or_album外,其他选择之后的消息和以上两个一样。
单击”扫码带提示“,则弹出扫码框扫描获得结果之后,会弹出”获取中...“,推送一个事件给开发者,稍候可能会获得开发者回复的消息,如图是我扫描一个百度URL的二维码的结果,直接把扫码结果回复给用户。
点击”扫码推事件“菜单,扫描到结果后,会推送一个事件给开发者,开发者回复消息用户不会接收到,如果是URL,则直接跳转到该URL,否则显示扫码结果。
4、查询自定义菜单
查询自定义菜单接口是https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN,代码实现如下
get_menu.php
<?php
@header('Content-type: text/plain;charset=UTF-8');
require_once('./Utils.php');
$url = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=".Utils::get_access_token();
//查询菜单
$result = Utils::https_request($url);
echo $result;
结果返回如下:
5、删除自定义菜单
删除自定义菜单的接口是:https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN
delete_menu.php
<?php
@header('Content-type: text/plain;charset=UTF-8');
require_once('./Utils.php');
$url = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=".Utils::get_access_token();
//删除菜单
$result = Utils::https_request($url);
echo $result;
返回结果是如下表示成功:
重新关注微信号后可以看见菜单被删除了
关于菜单推送事件字段的含义可以参见自定义菜单事件。
相关博客