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

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

 

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"就是在公众号里配置的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;

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

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


 

 

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

相关博客

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

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

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

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

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

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

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

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

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

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

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

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

 

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值