您现在的位置是:网站首页 > 心得笔记

PHP实现微信公众平台开发—提升篇

盛悦2019-02-18492人围观
简介提升篇主要讲解了 基于laravel这款框架来重构了验证接口,讲解了事件推送,也就是关注和取消关注,再一个就是讲解了接受信息与发送信息,接收主要是接收了用户的文本,发送主要讲解了图文信息、单文本信息

1、事件推送

事件推送是微信主动推送消息到我们在微信公众平台填写的url地址上


2、推送类型


1、关注或者取消关注微信公众账号,单击关注账号时,微信公众平台会推送消息

2、单击微信账号界面下方自定义菜单时,微信公众平台也会推送消息到我们填写的url地址上


接收到事件推送之后我们要做的是如何响应事件


3、如何响应事件

1、使用laravel接入api验证,验证消息的确来自微信服务器
public function index () {
  //获取参数echostr timestamp token signature nonce
  $echostr = $_GET['echostr'];
  $timestamp = $_GET['timestamp'];
  $nonce = $_GET['nonce'];
  $token = $_GET['token'];
  $signature = $_GET['signature'];
  
  //形成数组,然后按字典序排序
  $array = array($nonce, $timestamp, $token);
  sort($array);
  
  //拼接成字符串,sha1加密,然后与signature校验
  $tmpStr = sha1( implode($array) );
  if ( $tmpStr == $signature ) {
    echo $echostr;
    exit;
  }
}


例用以下实例来讲解如何实现微信的自动回复:

1、定义路由

    Route::any('/test/api', 'Front\TestController@api');
    
    /*注意:laravel框架中路由的定义有一坑 —— 正确定义发请求的方式是POST还是GET,要不然是无法正确响应请求的,这个地方坑过很多使用laravel开发微信的朋友们,而这些在thinkphp中是不用定义的。在这里有个坑就是微信验证默认token的请求是GET方式,但是真正接入微信服务器用以开发时采用的却是POST方式,所以我们在微信后台配置验证接口时,要使用GET方式,在验证成功配置完成后再改回POST方式。那在laravel框架中,我们直接使用any包含这两种方式!*/
    
2、手动关闭中间件对url的验证

      打开app\Http\Middleware\VerifyCsrfToken.php,将第14行

          protected $except = [
          //
          ];

      双斜杠替换为

      '/test/api',
      
      /*注意:laravel引进了一个中间件的概念,所有通过URL发过来的请求都需要经过中间件一系列验证才能分发给相应的控制器。最重要的是我们在所有post请求中必须包含一个crsfToken的字段用以防止跨域攻击。但是如果我们是用在微信开发接口中,显然微信服务器是不会发送包含这个字段的,那么就需要我们手动的关闭!*/
      
      
 3、在app\Http\目录下面新建TestCntroller.php,编写代码为:
 
         <?php
namespace App\Http\Controllers\Front;
use App\Http\Controllers\Controller;


class TestController extends Controller {

    public function api()
    {
        $echoStr = isset($_GET["echostr"]) ? $_GET["echostr"] : '';
        if (!empty($echoStr) && $this->checkSignature()) {//1、验证 —— 验证服务器地址的有效性
            echo $echoStr;
            exit;
        } else {//2、处理消息 —— 微信用户向微信公众账号发送消息时的响应
        
            //2-1、获取微信推送过来的Post数据(xml格式)
            //get post data, May be due to the different environments
            $postStr = file_get_contents("php://input", 'r');//php:input
            
            //写入日志  在同级目录下建立php_log.txt
            //chmod 777php_log.txt(赋权) chown wwwphp_log.txt(修改主)
            error_log(var_export($postStr,1),3,'php_log.txt');

            //extract post data
            if (!empty($postStr)){
                /* libxml_disable_entity_loader is to prevent XML eXternal Entity Injection,
                   the best way is to check the validity of xml by yourself */
                   
                //2-1、处理消息类型,并设置回复类型和内容(xml转为对象)
                
                libxml_disable_entity_loader(true);
                $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
                
                $fromUsername = $postObj->FromUserName;//注意:这里的大小写一定要根据xml格式处理
                $toUsername = $postObj->ToUserName;
                $keyword = trim($postObj->Content);
                $time = time();
                
                //订阅事件
                if($postObj->Event=="subscribe") {//判断该数据包是否是订阅的事件推送 —— 关注
                    $template = "<xml>
                           <ToUserName><![CDATA[%s]]></ToUserName>
                           <FromUserName><![CDATA[%s]]></FromUserName>
                           <CreateTime>%s</CreateTime>
                           <MsgType><![CDATA[%s]]></MsgType>
                           <Content><![CDATA[%s]]></Content>
                           </xml>";

                    $msgType = "text";
                    $content = "欢迎关注楠辕贝辙,微信Y348033046";
                    $resultStr = sprintf($template, $fromUsername,$toUsername, $time, $msgType, $content);//多个参数,param1按照这个参数解析变量,将后面的参数一一赋值给模板中的%s
                    echo $resultStr;
                }

                //图文事件 微信用户输入tuwen时,微信公众号相应imooc的图文消息
                if( strtolower($postObj->MsgType) == 'text' && trim($keyword)=='tuwen' ){
                    $arr = array(
                        array(
                            'title'=>'imooc',
                            'description'=>"imooc is very cool",
                            'picUrl'=>'https://www.chuangkit.com/ruanjian/assets/i/002.jpg',
                            'url'=>'http://www.imooc.com',
                        ),
                        array(
                            'title'=>'hao123',
                            'description'=>"hao123 is very cool",
                            'picUrl'=>'https://www.baidu.com/img/bdlogo.png',
                            'url'=>'http://www.hao123.com',
                        ),
                        array(
                            'title'=>'qq',
                            'description'=>"qq is very cool",
                            'picUrl'=>'http://www.imooc.com/static/img/common/logo.png',
                            'url'=>'http://www.qq.com',
                        ),
                    );

                    //注意此处有一大坑:模板中代码一定不能有空格 从官方文档粘贴下来的有空格 自行删除
                    
                    $template = "<xml>
                                    <ToUserName><![CDATA[%s]]></ToUserName>
                                    <FromUserName><![CDATA[%s]]></FromUserName>
                                    <CreateTime>%s</CreateTime>
                                    <MsgType><![CDATA[%s]]></MsgType>
                                    <ArticleCount>".count($arr)."</ArticleCount>
                                    <Articles>";
                                    foreach ($arr as $key => $value) {
                                    $template .= "<item>
                                                    <Title><![CDATA[".$value['title']."]]></Title>
                                                    <Description><![CDATA[".$value['description']."]]></Description>
                                                    <PicUrl><![CDATA[".$value['picUrl']."]]></PicUrl>
                                                    <Url><![CDATA[".$value['url']."]]></Url>
                                                </item>";
                                    }
                    $template .= "</Articles>
                                  </xml>";
                    $msgType = "news";
                    echo sprintf($template, $fromUsername,$toUsername, $time, $msgType);
                }



                //自动回复
                if(!empty( $keyword ) && trim($keyword) != 'tuwen') {
                    $template = "<xml>
                               <ToUserName><![CDATA[%s]]></ToUserName>
                               <FromUserName><![CDATA[%s]]></FromUserName>
                               <CreateTime>%s</CreateTime>
                               <MsgType><![CDATA[%s]]></MsgType>
                               <Content><![CDATA[%s]]></Content>
                               </xml>";
                    $msgType = "text";
                    $contentStr = "小朋友你好!";
                    $resultStr = sprintf($template, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                    echo $resultStr;
                } else {
                    echo "Input something...";
                }

            } else {
                echo "";
                exit;
            }
        }

    }



    //检查签名
    private function checkSignature()
    {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];
        $token = "weixin";
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);
        if($tmpStr == $signature){
            return true;

        }else{
            return false;
        }
    }



}


     /*
                     * 被动回复消息与客服消息接口消息类型调整
                        针对近期被动回复消息与客服消息接口能力使用不合理的情况,微信公众平台将对上述接口的多图文消息类型进行调整。10月12日起,被动回复消息与客服消息接口的图文消息类型中图文数目只能为一条,请知悉。
                        从2018年10月12日起,微信公众平台图文消息被限制为1条。
                        受影响的有 客服接口发送的图文消息 以及 被动回复用户的图文消息。
                        也就是说,通过代码处理的图文消息,就只有单图文,没有多图文了。
                        如果图文数超过1,则将会返回错误码45008。
                        替换方案:
                        1. 如果要带图片,则用单图文。
                        2. 如果是重要的消息,可用模板消息。
                        3. 如果内容比较多,用文本回复
                        4. 如果有多个链接,则用文本。
                     * */