微信公众号开发(四)自定义菜单

1、说明

微信的自定义菜单分为普通菜单和个性化菜单,个性化菜单可以根据地区、性别、语言等为不同的用户展示不同的菜单,定义个性化菜单之前必须定义普通菜单,删除普通菜单则个性化菜单也会删除,自定义菜单有以下需要注意的地方:

  1. 自定义菜单最多包括三个一级菜单,每个一级菜单最多包括5个二级菜单。
  2. 一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”显示。
  3. 创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号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种类型:

  1. click:点击推事件,点击后弹出“获取中...”,微信服务器会通过消息接口推送消息类型为event的结构给开发者,并带上按钮中开发者填写的key值,开发者可用此key值与用户进行交互。
  2. view:跳转URL,用户点击后直接打开微信浏览器跳转到该URL的地址。
  3. scancode_push:扫码推事件,点击按钮后,微信客户端将调用“扫一扫”功能,完成扫码后在微信浏览器显示扫码结果(如果是URL,则直接进入该URL),且会将扫码结果传送给开发者,此时可以下发消息给用户,但是用户收不到该消息。
  4. scancode_waitmsg:扫码推事件且弹出“获取中...”提示框,用户点击按钮后客户端将调用“扫一扫”功能,将扫码结果传给开发者,同时收起“扫一扫”功能,然后弹出“获取中...”提示框,随后可能收到开发者下发的消息。
  5. pic_sysphoto:弹出系统拍照发图用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。
  6. pic_photo_or_album:弹出拍照或者相册发图用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。
  7. pic_weixin:弹出相册发图器用户点击按钮后,微信客户端将调起相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。
  8. 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"就是在公众号里配置的TOKENrequire_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.xmlUtils::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;

返回结果是如下表示成功:

重新关注微信号后可以看见菜单被删除了

关于菜单推送事件字段的含义可以参见自定义菜单事件。

相关博客

微信公众号开发(一)服务器及接口的配置

微信公众号开发(二)基础接口

微信公众号开发(三)获取access_token

微信公众号开发(四)自定义菜单

微信公众号开发(五)个性化菜单

微信公众号开发(六)素材管理

微信公众号开发(七)发送客服消息

微信公众号开发(八)用户管理

微信公众号开发(九)群发消息接口

微信公众号开发(十)模板消息

微信公众号开发(十一)生成带参数二维码

微信公众号开发(十二)OAuth2.0网页授权

微信公众号开发(四)自定义菜单相关推荐

  1. 微信公众号开发:自定义菜单

    前言: 回顾之前的微信公众号配置和消息处理的内容,我们已经掌握了如何配置服务器与微信公众号建立连接,也掌握了通过消息管理的方式,对用户的信息进行处理,完成公众号消息回复功能,实现公众号与用户之间的完整 ...

  2. php 自定义菜单 openid,微信公众平台开发(99) 自定义菜单获取OpenID

    关键字 微信公众平台 自定义菜单 OpenID 作者:方倍工作室 原文:http://www.cnblogs.com/txw1958/p/weixin-menu-get-openid.html 在这篇 ...

  3. 微信公众号编辑底部自定义菜单解决方案

    微信公众号编辑底部自定义菜单解决方案 1.需求背景 最近开发公众号项目,关于公众号里面底部的菜单栏设置一般常用有两种方法. 1,是进入公众号后台,找到自定义菜单,点击后进入编辑页面,进行编辑即可. 2 ...

  4. 微信公众平台开发,自定义菜单,群发消息,网页授权(3)

    前两节说的都是微信开发的初级篇,这一节说高级篇.微信公众号关注以后,下面都是有菜单的,底部有三个一级菜单,每个一级菜单可以添加5个二级菜单.一级菜单最多四个汉字,二级菜单最多7个汉字,多出来的汉字用& ...

  5. 微信公众平台开发(104) 自定义菜单扫一扫、发图片、发地理位置

    关键字:微信公众平台 自定义菜单 扫一扫 发图片 发地理位置 作者:方倍工作室 原文:http://www.cnblogs.com/txw1958/p/weixin-menu-new-type.htm ...

  6. c# 微信公众号开发之自定义菜单栏

    在微信公众号开启了第三方服务器之后,很多在微信平台上的配置都需要开发者通过微信提供的API,POST请求,将JSON字符串按格式,告知微信服务器 在这里介绍微信公众号的自定义菜单栏开发 先恭迎我们的T ...

  7. 微信公众平台开发教程--自定义菜单

    微信公众平台开发教程(五)自定义菜单 请尊重作者版权,如需转载,请标明出处. 应大家强烈要求,将自定义菜单功能课程提前. 一.概述: 如果只有输入框,可能太简单,感觉像命令行.自定义菜单,给我们提供了 ...

  8. java微信公众号开发四步完成

    一.公众号注册 此处比较简单不做过多陈述(个人需要身份证号和300RMB,企业需要企业注册证书和企业法人证件),公众号每年一审,每次年审需要300RMB,提交信息自己官网一步步点击就可知道. 二.公众 ...

  9. 微信公众号之创建自定义菜单

    微信公众号菜单等功能如何开通 自定义菜单最大的优点是减少了用户的认知门槛,可以将公众账号里的重点信息入口直观的暴露给用户.当用户进入到公众账号时,可以一目了然的了解相关的服务,只需要点击,不需要再通过 ...

  10. php微信级联菜单,php微信公众号开发之二级菜单

    本文实例为大家分享了php微信公众号二级菜单的具体代码,供大家参考,具体内容如下 核心代码: $postObj = simplexml_load_string($postStr, 'SimpleXML ...

最新文章

  1. 为什么安装的是gpu版本训练时还是用的cpu?_免费GPU哪家强?谷歌Kaggle vs. Colab | 硬核评测...
  2. execute、executeQuery和executeUpdate之间的区别
  3. Linux Tomcat 简介
  4. c语言测验答案,C语言测验题答案.doc
  5. MySQL 条件查询 limit、in、between and、like等等
  6. Spring 核心特性
  7. cobbler工作流分析
  8. [转] vim自定义配置 和 在ubnetu中安装vim
  9. STM8L芯片启动时钟分频问题及发现(转)
  10. react 生命挂钩_角生命周期挂钩:ngOnChanges,ngOnInit等
  11. 左右法则 来解析指针问题
  12. aws rds监控慢sql_AWS RDS SQL Server中的本机差异备份概述
  13. [转载] python中断响应_用Python脚本监测.py脚本的进程状态,并实现中断重启。
  14. 1053 Path of Equal Weight (30 分)一般树的遍历 DFS+vector容器+sort排序
  15. 如何从PDF文件中提取几页为一个PDF文件?
  16. 低代码平台- Intellij IDEA 插件开发
  17. Docker安装CentOS容器并使用SSH工具远程连接
  18. 小米3流量显示无服务器,因为小米3元不限流量卡,我尝到了无网可用的滋味
  19. 多边形裁剪(Polygon Clipping) 2
  20. 如何自定义火狐背景_在Firefox中自定义菜单

热门文章

  1. 狐狸逮兔子——链式存储方式
  2. 智能家居出货量将达5.4亿台,全屋智能线下渠道是推广主力
  3. 配置Kafka的参数auto.offset.reset时earliest和latest的区别
  4. javascript:理解try...catch...finally
  5. 【matlab实现多种股票数据同列收盘价格分析走势图,以及涨跌幅变化曲线第二篇】
  6. 给新一代IT人的分享
  7. python小玩具(恶俗古风生成器)
  8. FogROS2 使用 ROS 2 的云和雾机器人的自适应和可扩展平台
  9. npm shrinkwrap的用途
  10. 用vb编制一个计算机程序,用vb编了个数独计算器