在PHP环境下进行微信公众平台的开发

2013年12月26日 分类: PHP (860 个脚步)

微信自从4.5以后就分为服务号和订阅号这两类。服务号偏向于对用户的沟通交流,可以一对一的跟用户沟通,所以所拥有的权限比较多;而订阅号则偏向于资讯的发布,使用开发模式时只能用最基本的文本回复和事件信息。

为了方便管理,这两种帐号都有两种运营模式。一种是“编辑模式”,运营者手工编辑、设置关键字,对粉丝发送来的关键字进行响应;另外一种是“开发模式”,运营者编写程序,通过使用公众平台提供的接口自动对粉丝发来的消息进行响应。两者不能同时存在,所以用了编辑模式,就不能用开发模式了。

编辑模式比较简单,直接去后台编辑内容就可以了,对于不懂写程序的人是个很好的编辑方式,但是比较繁琐,不能灵活变通;开发者模式就能够方便的调用网络资源,利用微信所提供的权限来推送新闻、资讯等内容到用户手上。

对于我们来说,开发者模式是最好的方式,能减少对于界面的维护经历,从而转移到内容上面去。

说完这些废话,就可以往下看了,不过前提是你必须得有一个微信公众的号和一个

1. 申请接口

在公众平台网站的高级功能 – 开发模式页,点击“成为开发者”按钮,填写URL和Token,其中URL是开发者用来接收微信服务器数据的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)

2. 建立数据接口文件

微信公众平台提供了一个php示例代码:http://mp.weixin.qq.com/mpres/htmledition/res/wx_sample.zip

你可以参考下这一个示例代码。

因为静态方法效率高,所以我就静态方法来实现。下面就来讲解各种函数

<?php
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");

define("TOKEN", "shiniv");

new WeixinCallbackApi();

class WeixinCallbackApi {
    private static $data            = array();
    private static $xml_head        = '<xml><ToUserName>%s</ToUserName><FromUserName>%s</FromUserName><CreateTime>%s</CreateTime><MsgType>%s</MsgType>';
    private static $xml_text_body   = '<Content>%s</Content>';
    private static $xml_news_head   = '<ArticleCount>%d</ArticleCount><Articles>';
    private static $xml_news_body   = '<item><Title>%s</Title><Description>%s</Description><PicUrl>%s</PicUrl><Url>%s</Url></item>';
    private static $xml_news_foot   = '</Articles>';
    private static $xml_music_body  = '<Music><Title>%s</Title><Description>%s</Description><MusicUrl>%s</MusicUrl><HQMusicUrl>%s</HQMusicUrl></Music>';
    private static $xml_foot        = '<FuncFlag>%d</FuncFlag></xml>';
    private static $type            = "text";

    public function __construct() {
        if(isset($_GET['echostr']) && self::valid()) {
            echo $_GET['echostr'];
            exit;
        }

        self::xml2array();

        if(in_array(($type=self::$data['MSGTYPE']), array('text', 'event', 'location')) ) {
            self::$type();
        } else {
            self::error();
        }
    }

    private static function xml2array() {
        $opened = array();
        $array = array();
        $xml_parser = xml_parser_create();
        xml_parse_into_struct($xml_parser, $GLOBALS["HTTP_RAW_POST_DATA"], $xmlarray);
        $arrsize = sizeof($xmlarray);
        for($j=0;$j<$arrsize;$j++){
            $val = $xmlarray[$j];
            switch($val["type"]){
                case "open":
                    $opened[$val["tag"]] = $array;
                    unset($array);
                    break;
                case "complete":
                    if(is_array($val["value"]) && count($val["value"])>1) {
                        $array[$val["tag"]][] = $val["value"];
                    } else {
                        $array[$val["tag"]] = trim($val["value"]);
                    }
                break;
                case "close":
                    $opened[$val["tag"]] = $array;
                    $array = $opened;
                break;
            }
        }
        self::$data = $array['XML'];
        unset($opened, $array, $xml_parser, $xmlarray, $arrsize, $j, $val);
    }

    private static function valid() {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];

        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);

        if($tmpStr == $signature) {
            return true;
        } else {
            return false;
        }
    }

    private static function error($message = '') {
        exit($message);
    }
}

验证签名数否正确

函数 valid() 判断当前传进来的内容是否为微信发送过来的,判断签名是否正确,正常后将 echostr 输出到文件完成验证或者判断用户输入内容进行输出。

解析xml

因为我的服务器不怎么需要使用到xml,只安装了一个libxml,为了这个小东西装一直simpleXML不值得,所以就改写了下,不用simpleXML去解析xml。
所以 xml2array 这个函数就是将提交上来的xml解析成数组的形式,后面的代码也会使用到数组。

解析完成后,就要判断当前提交的类型是否符合我们所拥有的类型,有就执行相应函数。没有则不会输出任何内容。

全局XML数据包结构

微信客户端只接收XML格式的内容,所以在发送内容到用户手上时需要格式化成相应的格式,才能正常显示。


private static function transmit($content, $flag = 0) {
    $format = '';
    if(is_array($content) && self::$type == 'news'){
        $news_body = '';
        foreach($content as $v){
             $news_body .= sprintf(self::$xml_news_body, $v['title'], $v['description'], $v['picUrl'], $v['url']);
        }
        $format = sprintf(self::$xml_head.self::$xml_news_head.$news_body.self::$xml_news_foot.self::$xml_foot, self::$data['FROMUSERNAME'], self::$data['TOUSERNAME'], time(), self::$type, count($content), $flag);
    } elseif (is_array($content) && self::$type == 'music') {
        $format = sprintf(self::$xml_head.self::$xml_music_body.self::$xml_foot, self::$data['FROMUSERNAME'], self::$data['TOUSERNAME'], time(), self::$type, $content['title'], $content['description'], $content['musicurl'], $content['hqurl'], $flag);
    } else {
        $format = sprintf(self::$xml_head.self::$xml_text_body.self::$xml_foot, self::$data['FROMUSERNAME'], self::$data['TOUSERNAME'], time(), self::$type, $content, $flag);
    }
    echo $format;
    unset($format);
}

XML的结构我已经预先定于在类的头部了,所以现在调用就行了。
图文回复因为可以多条一起发送,需要循环将内容拼接起来。
这里我只写了图文回复、音乐回复和文字回复这三种相应数据包,因为回复图片等多媒体消息时需要预先上传多媒体文件到微信服务器,只支持认证服务号。普通的订阅号就别想了。

响应文字消息

用户主动跟微信交流后,我们就需要将预定好的内容推送给用户。首先我们要判断用户输入的内容,从而决定后面发送什么内容到用户手中。

这就要根据自己的情况来写了,我就给出一个参考。

我是将需要截取的内容放在if条件中,方便判断,其余的内容不变的话,直接使用switch来执行,提高效率。


private static function text() {
    $keyword = self::$data['CONTENT'];
    if(!empty($keyword)) {
        if ( mb_substr($keyword, -2, 2, "UTF-8") == '天气' && mb_substr($keyword, 0, -2, "UTF-8") != "") {
            $content = self::getWeather(mb_substr($keyword,0,-2,"UTF-8"));
        } elseif($keyword =="帮助" || $keyword =="help") {
            $content = "您好,你可以回复以下编号获得相关信息 \n".
                       "更多内容敬请期待。";
        } else {
            switch($keyword) {
                case '你好':
                    $content = "hello";
                break;
                case '新闻':
                    $content = self::getNews();
                break;
                default:
                    $content = "您好,你可以回复以下编号获得相关信息 \n".
                       "[1].获取最新资讯\n".
                       "\n回复 城市+天气 例如,输入“上海天气”即可获得上海的天气状况。\n".
                       "更多内容敬请期待。";
                break;
            }
        }
        self::transmit($content);
    } else {
        self::error("input something");
    }
}

然后调用self::transmit($content)进行格式化输出。如果你回复的内容不是文字型,你还需要将type修改到你想要的类型。

事件推送

由于订阅号的权限比较低,所以现在只有订阅事件可以使用。最新的消息是,花300进行认证后就能多一个菜单时间,但是谁会干这事呢。。
取消订阅也可以推送内容给用户,但是由于取消关注后,就会从用户列表中消失,所以这个事件没用的。



private static function event() {
    switch (self::$data['EVENT']) {
        case "subscribe":
            $content = "感谢您关注【XXXX】\n微信号:shiniv \n这里是一些介绍信息。";
            break;
        case 'unsubscribe':
            $content = "感谢您关注【XXXX】"."\n";
            break;
        default :
            $content = "Unknow Event: ".self::$data['EVENT'];
            break;
    }
    echo self::transmit($content);
}

推送图文内容

图文回复可以是单图文,也可以是多图文。单图文会显示当前的预览,并且会显示一张大图在上面。

多图文的话,第一条会显示一张大图,然后后面的变成一张小图放在右边。

这些数据你可以从服务器动态获取,就不用每次都要修改这个文件了。


private static function getNews() {
    self::$type = 'news';
    $data[] = array('title'=>'标题','url' = 'http://blog.shiniv.com', 'description' => '这是一个描述', 'picUrl'=> 'http://blog.shiniv.com/logo.png');
    $data[] = array('title'=>'标题','url' = 'http://blog.shiniv.com', 'description' => '这是一个描述', 'picUrl'=> 'http://blog.shiniv.com/logo.png');
    return $data;
}

现在这个接口基本上能够应付大多数人的需求,后面的就需要自己的想象力去修改了。

研究这个接口几天了,官方给的文档我真的很想吐槽,但是想想算了,没必要为这个苦了我自己。
最后再吐槽一句,MLGB的微信居然还在用XML格式,就不能换个JSON的么,处理XML麻烦死了。

]]>

在PHP环境下进行微信公众平台的开发 【声明】本文 在PHP环境下进行微信公众平台的开发 为柠之漠然原创文章,转载请注明出自 枫之落叶
并保留本文有效链接:https://blog.shiniv.com/2013/12/weixin-api-develop-in-php/ , 转载请保留本声明!

标签: , , , ,
目前还没有任何评论.
你必须要启用 Javascript