微信公衆號開發(四)自定義菜單

微信公衆號開發(四)自定義菜單


一、說明

微信的自定義菜單分爲普通菜單和個性化菜單,個性化菜單能夠根據地區、性別、語言等爲不一樣的用戶展現不一樣的菜單,定義個性化菜單以前必須定義普通菜單,刪除普通菜單則個性化菜單也會刪除,自定義菜單有如下須要注意的地方:
  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;
    }
}


二、自定義菜單的類型

自定義菜單有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:彈出地理位置選擇器用戶點擊按鈕後,微信客戶端將調起地理位置選擇工具,完成選擇操做後,將選擇的地理位置發送給開發者的服務器,同時收起位置選擇工具,隨後可能會收到開發者下發的消息。

三、建立普通自定義菜單

建立自定義普通的菜單接口是:  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,不然顯示掃碼結果。



四、查詢自定義菜單

查詢自定義菜單接口是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;
結果返回以下:

五、刪除自定義菜單

刪除自定義菜單的接口是: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;
返回結果是以下表示成功:
從新關注微信號後能夠看見菜單被刪除了




關於菜單推送事件字段的含義能夠參見 自定義菜單事件
相關文章
相關標籤/搜索