微信接口开发之高级篇系列【微信权限封装类WechatAuth】

简介: ThinkPHP框架目录结构:

ThinkPHP框架目录结构:

<?php
/**
 * Created by PhpStorm.
 * User: Tinywan
 * Date: 2016/9/11
 * Time: 9:55
 */

namespace Org\Util;

class WechatAuth
{

    /* 消息类型常量 */
    const MSG_TYPE_TEXT = 'text';
    const MSG_TYPE_IMAGE = 'image';
    const MSG_TYPE_VOICE = 'voice';
    const MSG_TYPE_VIDEO = 'video';
    const MSG_TYPE_SHORTVIDEO = 'shortvideo';
    const MSG_TYPE_LOCATION = 'location';
    const MSG_TYPE_LINK = 'link';
    const MSG_TYPE_MUSIC = 'music';
    const MSG_TYPE_NEWS = 'news';
    const MSG_TYPE_EVENT = 'event';

    /* 二维码类型常量 */
    const QR_SCENE = 'QR_SCENE';
    const QR_LIMIT_SCENE = 'QR_LIMIT_SCENE';

    /**
     * 微信开发者申请的appID
     * @var string
     */
    private $appId = '';

    /**
     * 微信开发者申请的appSecret
     * @var string
     */
    private $appSecret = '';

    /**
     * 获取到的access_token
     * @var string
     */
    private $accessToken = '';

    /**
     * 微信api根路径
     * @var string
     */
    private $apiURL = 'https://api.weixin.qq.com/cgi-bin';

    /**
     * 微信二维码根路径
     * @var string
     */
    private $qrcodeURL = 'https://mp.weixin.qq.com/cgi-bin';

    //授权地址
    private $requestCodeURL = 'https://open.weixin.qq.com/connect/oauth2/authorize';

    private $oauthApiURL = 'https://api.weixin.qq.com/sns';

    /**
     * 构造方法,调用微信高级接口时实例化SDK
     * @param string $appid 微信appid
     * @param string $secret 微信appsecret
     * @param string $token 获取到的access_token
     */
    public function __construct($appid, $secret, $token = null)
    {
        if ($appid && $secret) {
            $this->appId = $appid;
            $this->appSecret = $secret;
            if (!empty($token)) $this->accessToken = $token;
        } else {
            throw new \Exception('缺少参数 APP_ID 和 APP_SECRET!');
        }
    }

    // 是为了拼接成一URL地址,什么地址,拼接一个
    public function getRequestCodeURL($redirect_uri, $state = null, $scope = 'snsapi_userinfo')
    {

        $query = array(
            'appid' => $this->appId,
            'redirect_uri' => $redirect_uri,
            'response_type' => 'code',
            'scope' => $scope,
        );

        if (!is_null($state) && preg_match('/[a-zA-Z0-9]+/', $state)) $query['state'] = $state;

        //生成 URL-encode 之后的请求字符串 :foo=bar&baz=boom&cow=milk&php=hypertext+pro
        $query = http_build_query($query);
        return "{$this->requestCodeURL}?{$query}#wechat_redirect";
    }

    /**
     * 获取access_token,用于后续接口访问
     * @return array access_token信息,包含 token 和有效期
     */
    public function getAccessToken($type = 'client', $code = null)
    {
        $param = array(
            'appid' => $this->appId,
            'secret' => $this->appSecret
        );

        switch ($type) {
            case 'client':
                $param['grant_type'] = 'client_credential';
                $url = "{$this->apiURL}/token";
                break;

            case 'code':
                $param['code'] = $code;
                $param['grant_type'] = 'authorization_code';
                $url = "{$this->oauthApiURL}/oauth2/access_token";
                break;

            default:
                throw new \Exception('不支持的grant_type类型!');
                break;
        }

        $token = self::http($url, $param);
        $token = json_decode($token, true);

        if (is_array($token)) {
            if (isset($token['errcode'])) {
                throw new \Exception($token['errmsg']);
            } else {
                $this->accessToken = $token['access_token'];
                return $token;
            }
        } else {
            throw new \Exception('获取微信access_token失败!');
        }
    }

    /**
     * 获取授权用户信息
     * @param  string $openid 用户的OpenID
     * @param  string $lang 指定的语言
     * @return array          用户信息数据,具体参见微信文档
     */
    public function getUserInfo($openid, $lang = 'zh_CN')
    {
        $query = array(
            'access_token' => $this->accessToken,
            'openid' => $openid,
            'lang' => $lang,
        );

        $info = self::http("{$this->oauthApiURL}/userinfo", $query);
        return json_decode($info, true);
    }

    /**
     * 上传零时媒体资源
     * @param  string $filename 媒体资源本地路径
     * @param  string $type 媒体资源类型,具体请参考微信开发手册
     */
    public function mediaUpload($filename, $type)
    {
        $filename = realpath($filename);
        if (!$filename) throw new \Exception('资源路径错误!');

        $data = array(
            'type' => $type,
            'media' => "@{$filename}"
        );

        return $this->api('media/upload', $data, 'POST', '', false);
    }

    /**
     * 上传永久媒体资源
     * @param string $filename 媒体资源本地路径
     * @param string $type 媒体资源类型,具体请参考微信开发手册
     * @param string $description 资源描述,仅资源类型为 video 时有效
     */
    public function materialAddMaterial($filename, $type, $description = '')
    {
        $filename = realpath($filename);
        if (!$filename) throw new \Exception('资源路径错误!');

        $data = array(
            'type' => $type,
            'media' => "@{$filename}",
        );

        if ($type == 'video') {
            if (is_array($description)) {
                //保护中文,微信api不支持中文转义的json结构
                array_walk_recursive($description, function (&$value) {
                    $value = urlencode($value);
                });
                $description = urldecode(json_encode($description));
            }
            $data['description'] = $description;
        }
        return $this->api('material/add_material', $data, 'POST', '', false);
    }

    /**
     * 获取媒体资源下载地址
     * 注意:视频资源不允许下载
     * @param  string $media_id 媒体资源id
     * @return string           媒体资源下载地址
     */
    public function mediaGet($media_id)
    {
        $param = array(
            'access_token' => $this->accessToken,
            'media_id' => $media_id
        );

        $url = "{$this->apiURL}/media/get?";
        return $url . http_build_query($param);
    }

    /**
     * 给指定用户推送信息
     * 注意:微信规则只允许给在48小时内给公众平台发送过消息的用户推送信息
     * @param  string $openid 用户的openid
     * @param  array $content 发送的数据,不同类型的数据结构可能不同
     * @param  string $type 推送消息类型
     */
    public function messageCustomSend($openid, $content, $type = self::MSG_TYPE_TEXT)
    {

        //基础数据
        $data = array(
            'touser' => $openid,
            'msgtype' => $type,
        );

        //根据类型附加额外数据
        $data[$type] = call_user_func(array(self, $type), $content);

        return $this->api('message/custom/send', $data);
    }

    /**
     * 发送文本消息
     * @param  string $openid 用户的openid
     * @param  string $text 发送的文字
     */
    public function sendText($openid, $text)
    {
        return $this->messageCustomSend($openid, $text, self::MSG_TYPE_TEXT);
    }

    /**
     * 发送图片消息
     * @param  string $openid 用户的openid
     * @param  string $media 图片ID
     */
    public function sendImage($openid, $media)
    {
        return $this->messageCustomSend($openid, $media, self::MSG_TYPE_IMAGE);
    }

    /**
     * 发送语音消息
     * @param  string $openid 用户的openid
     * @param  string $media 音频ID
     */
    public function sendVoice($openid, $media)
    {
        return $this->messageCustomSend($openid, $media, self::MSG_TYPE_VOICE);
    }

    /**
     * 发送视频消息
     * @param  string $openid 用户的openid
     * @param  string $media_id 视频ID
     * @param  string $title 视频标题
     * @param  string $discription 视频描述
     */
    public function sendVideo()
    {
        $video = func_get_args();
        $openid = array_shift($video);
        return $this->messageCustomSend($openid, $video, self::MSG_TYPE_VIDEO);
    }

    /**
     * 发送音乐消息
     * @param  string $openid 用户的openid
     * @param  string $title 音乐标题
     * @param  string $discription 音乐描述
     * @param  string $musicurl 音乐链接
     * @param  string $hqmusicurl 高品质音乐链接
     * @param  string $thumb_media_id 缩略图ID
     */
    public function sendMusic()
    {
        $music = func_get_args();
        $openid = array_shift($music);
        return $this->messageCustomSend($openid, $music, self::MSG_TYPE_MUSIC);
    }

    /**
     * 发送图文消息
     * @param  string $openid 用户的openid
     * @param  array $news 图文内容 [标题,描述,URL,缩略图]
     * @param  array $news1 图文内容 [标题,描述,URL,缩略图]
     * @param  array $news2 图文内容 [标题,描述,URL,缩略图]
     *                ...     ...
     * @param  array $news9 图文内容 [标题,描述,URL,缩略图]
     */
    public function sendNews()
    {
        $news = func_get_args();
        $openid = array_shift($news);
        return $this->messageCustomSend($openid, $news, self::MSG_TYPE_NEWS);
    }

    /**
     * 发送一条图文消息
     * @param  string $openid 用户的openid
     * @param  string $title 文章标题
     * @param  string $discription 文章简介
     * @param  string $url 文章连接
     * @param  string $picurl 文章缩略图
     */
    public function sendNewsOnce()
    {
        $news = func_get_args();
        $openid = array_shift($news);
        $news = array($news);
        return $this->messageCustomSend($openid, $news, self::MSG_TYPE_NEWS);
    }

    /**
     * 创建用户组
     * @param  string $name 组名称
     */
    public function groupsCreate($name)
    {
        $data = array('group' => array('name' => $name));
        return $this->api('groups/create', $data);
    }

    /**
     * 查询所有分组
     * @return array 分组列表
     */
    public function groupsGet()
    {
        return $this->api('groups/get', '', 'GET');
    }

    /**
     * 查询用户所在的分组
     * @param  string $openid 用户的OpenID
     * @return number         分组ID
     */
    public function groupsGetid($openid)
    {
        $data = array('openid' => $openid);
        return $this->api('groups/getid', $data);
    }

    /**
     * 修改分组
     * @param  number $id 分组ID
     * @param  string $name 分组名称
     * @return array        修改成功或失败信息
     */
    public function groupsUpdate($id, $name)
    {
        $data = array('id' => $id, 'name' => $name);
        return $this->api('groups/update', $data);
    }

    /**
     * 移动用户分组
     * @param  string $openid 用户的OpenID
     * @param  number $to_groupid 要移动到的分组ID
     * @return array              移动成功或失败信息
     */
    public function groupsMemberUpdate($openid, $to_groupid)
    {
        $data = array('openid' => $openid, 'to_groupid' => $to_groupid);

        return $this->api('groups/member/update', $data);
    }


    /**
     * 用户设备注名
     * @param  string $openid 用户的OpenID
     * @param  string $remark 设备注名
     * @return array          执行成功失败信息
     */
    public function userInfoUpdateremark($openid, $remark)
    {
        $data = array('openid' => $openid, 'remark' => $remark);


        return $this->api('user/info/updateremark', $data);
    }

    /**
     * 获取指定用户的详细信息
     * @param  string $openid 用户的openid
     * @param  string $lang 需要获取数据的语言
     */
    public function userInfo($openid, $lang = 'zh_CN')
    {
        $param = array('openid' => $openid, 'lang' => $lang);
        return $this->api('user/info', '', 'GET', $param);
    }

    /**
     * 获取关注者列表
     * @param  string $next_openid 下一个openid,在用户数大于10000时有效
     * @return array               用户列表
     */
    public function userGet($next_openid = '')
    {
        $param = array('next_openid' => $next_openid);
        return $this->api('user/get', '', 'GET', $param);
    }

    /**
     * 创建自定义菜单
     * @param  array $button 符合规则的菜单数组,规则参见微信手册
     */
    public function menuCreate($button)
    {
        $data = array('button' => $button);
        return $this->api('menu/create', $data);
    }

    /**
     * 获取所有的自定义菜单
     * @return array  自定义菜单数组
     */
    public function menuGet()
    {
        return $this->api('menu/get', '', 'GET');
    }

    /**
     * 删除自定义菜单
     */
    public function menuDelete()
    {
        return $this->api('menu/delete', '', 'GET');
    }

    /**
     * 创建二维码,可创建指定有效期的二维码和永久二维码
     * @param  integer $scene_id 二维码参数
     * @param  integer $expire_seconds 二维码有效期,0-永久有效
     */
    public function qrcodeCreate($scene_id, $expire_seconds = 0)
    {
        $data = array();

        if (is_numeric($expire_seconds) && $expire_seconds > 0) {
            $data['expire_seconds'] = $expire_seconds;
            $data['action_name'] = self::QR_SCENE;
        } else {
            $data['action_name'] = self::QR_LIMIT_SCENE;
        }

        $data['action_info']['scene']['scene_id'] = $scene_id;
        return $this->api('qrcode/create', $data);
    }

    /**
     * 根据ticket获取二维码URL
     * @param  string $ticket 通过 qrcodeCreate接口获取到的ticket
     * @return string         二维码URL
     */
    public function showqrcode($ticket)
    {
        return "{$this->qrcodeURL}/showqrcode?ticket={$ticket}";
    }

    /**
     * 长链接转短链接
     * @param  string $long_url 长链接
     * @return string           短链接
     */
    public function shorturl($long_url)
    {
        $data = array(
            'action' => 'long2short',
            'long_url' => $long_url
        );

        return $this->api('shorturl', $data);
    }

    /**
     * 调用微信api获取响应数据
     * @param  string $name API名称
     * @param  string $data POST请求数据
     * @param  string $method 请求方式
     * @param  string $param GET请求参数
     * @return array          api返回结果
     */
    protected function api($name, $data = '', $method = 'POST', $param = '', $json = true)
    {
        $params = array('access_token' => $this->accessToken);

        if (!empty($param) && is_array($param)) {
            $params = array_merge($params, $param);
        }

        $url = "{$this->apiURL}/{$name}";
        if ($json && !empty($data)) {
            //保护中文,微信api不支持中文转义的json结构
            array_walk_recursive($data, function (&$value) {
                $value = urlencode($value);
            });
            $data = urldecode(json_encode($data));
        }

        $data = self::http($url, $params, $data, $method);

        return json_decode($data, true);
    }

    /**
     * 发送HTTP请求方法,目前只支持CURL发送请求
     * @param  string $url 请求URL
     * @param  array $param GET参数数组
     * @param  array $data POST的数据,GET请求时该参数无效
     * @param  string $method 请求方法GET/POST
     * @return array          响应数据
     */
    protected static function http($url, $param, $data = '', $method = 'GET')
    {
        $opts = array(
            CURLOPT_TIMEOUT => 30,
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
        );

        /* 根据请求类型设置特定参数 */
        $opts[CURLOPT_URL] = $url . '?' . http_build_query($param);

        if (strtoupper($method) == 'POST') {
            $opts[CURLOPT_POST] = 1;
            $opts[CURLOPT_POSTFIELDS] = $data;

            if (is_string($data)) { //发送JSON数据
                $opts[CURLOPT_HTTPHEADER] = array(
                    'Content-Type: application/json; charset=utf-8',
                    'Content-Length: ' . strlen($data),
                );
            }
        }

        /* 初始化并执行curl请求 */
        $ch = curl_init();
        curl_setopt_array($ch, $opts);
        $data = curl_exec($ch);
        $error = curl_error($ch);
        curl_close($ch);

        //发生错误,抛出异常
        if ($error) throw new \Exception('请求发生错误:' . $error);

        return $data;
    }

    /**
     * 构造文本信息
     * @param  string $content 要回复的文本
     */
    private static function text($content)
    {
        $data['content'] = $content;
        return $data;
    }

    /**
     * 构造图片信息
     * @param  integer $media 图片ID
     */
    private static function image($media)
    {
        $data['media_id'] = $media;
        return $data;
    }

    /**
     * 构造音频信息
     * @param  integer $media 语音ID
     */
    private static function voice($media)
    {
        $data['media_id'] = $media;
        return $data;
    }

    /**
     * 构造视频信息
     * @param  array $video 要回复的视频 [视频ID,标题,说明]
     */
    private static function video($video)
    {
        $data = array();
        list(
            $data['media_id'],
            $data['title'],
            $data['description'],
            ) = $video;

        return $data;
    }

    /**
     * 构造音乐信息
     * @param  array $music 要回复的音乐[标题,说明,链接,高品质链接,缩略图ID]
     */
    private static function music($music)
    {
        $data = array();
        list(
            $data['title'],
            $data['description'],
            $data['musicurl'],
            $data['hqmusicurl'],
            $data['thumb_media_id'],
            ) = $music;

        return $data;
    }

    /**
     * 构造图文信息
     * @param  array $news 要回复的图文内容
     * [
     *      0 => 第一条图文信息[标题,说明,图片链接,全文连接],
     *      1 => 第二条图文信息[标题,说明,图片链接,全文连接],
     *      2 => 第三条图文信息[标题,说明,图片链接,全文连接],
     * ]
     */
    private static function news($news)
    {
        $articles = array();
        foreach ($news as $key => $value) {
            list(
                $articles[$key]['title'],
                $articles[$key]['description'],
                $articles[$key]['url'],
                $articles[$key]['picurl']
                ) = $value;

            if ($key >= 9) break; //最多只允许10条图文信息
        }

        $data['articles'] = $articles;
        return $data;
    }

}

 

目录
相关文章
|
14天前
|
小程序 前端开发 API
微信小程序全栈开发中的异常处理与日志记录
【4月更文挑战第12天】本文探讨了微信小程序全栈开发中的异常处理和日志记录,强调其对确保应用稳定性和用户体验的重要性。异常处理涵盖前端(网络、页面跳转、用户输入、逻辑异常)和后端(数据库、API、业务逻辑)方面;日志记录则关注关键操作和异常情况的追踪。实践中,前端可利用try-catch处理异常,后端借助日志框架记录异常,同时采用集中式日志管理工具提升分析效率。开发者应注意安全性、性能和团队协作,以优化异常处理与日志记录流程。
|
14天前
|
小程序 安全 数据安全/隐私保护
微信小程序全栈开发中的身份认证与授权机制
【4月更文挑战第12天】本文探讨了微信小程序全栈开发中的身份认证与授权机制。身份认证包括手机号验证、微信登录和第三方登录,而授权机制涉及角色权限控制、ACL和OAuth 2.0。实践中,开发者可利用微信登录获取用户信息,集成第三方登录,以及实施角色和ACL进行权限控制。注意点包括安全性、用户体验和合规性,以保障小程序的安全运行和良好体验。通过这些方法,开发者能有效掌握小程序全栈开发技术。
|
14天前
|
JavaScript 前端开发 小程序
微信小程序全栈开发之性能优化策略
【4月更文挑战第12天】本文探讨了微信小程序全栈开发的性能优化策略,包括前端的资源和渲染优化,如图片压缩、虚拟DOM、代码分割;后端的数据库和API优化,如索引创建、缓存使用、RESTful API设计;以及服务器的负载均衡和CDN加速。通过这些方法,开发者可提升小程序性能,优化用户体验,增强商业价值。
|
14天前
|
小程序 前端开发 JavaScript
微信小程序全栈开发中的PWA技术应用
【4月更文挑战第12天】本文探讨了微信小程序全栈开发中PWA技术的应用,PWA结合Web的开放性和原生应用的性能,提供离线访问、后台运行、桌面图标和原生体验。开发者可利用Service Worker实现离线访问,Worker处理后台运行,Web App Manifest添加桌面图标,CSS和JavaScript提升原生体验。实践中需注意兼容性、性能优化和用户体验。PWA技术能提升小程序的性能和用户体验,助力开发者打造优质小程序。
|
2天前
|
数据采集 存储 人工智能
【Python+微信】【企业微信开发入坑指北】4. 企业微信接入GPT,只需一个URL,自动获取文章总结
【Python+微信】【企业微信开发入坑指北】4. 企业微信接入GPT,只需一个URL,自动获取文章总结
13 0
|
2天前
|
人工智能 机器人 API
【Python+微信】【企业微信开发入坑指北】3. 如何利用企业微信API给微信群推送消息
【Python+微信】【企业微信开发入坑指北】3. 如何利用企业微信API给微信群推送消息
6 0
|
2天前
|
缓存 人工智能 API
【Python+微信】【企业微信开发入坑指北】2. 如何利用企业微信API主动给用户发应用消息
【Python+微信】【企业微信开发入坑指北】2. 如何利用企业微信API主动给用户发应用消息
7 0
|
14天前
|
SQL 安全 小程序
探索微信小程序全栈开发的安全性问题
【4月更文挑战第12天】本文探讨了微信小程序全栈开发中的安全性问题,包括数据安全、接口安全、隐私保护和代码安全。为解决这些问题,建议采取数据加密、使用HTTPS协议、身份认证与授权、输入验证、安全审计及漏洞扫描以及安全培训等措施。通过这些方法,开发者可提升小程序安全性,保护用户隐私和数据。
|
1月前
|
小程序 JavaScript
在使用微信小程序开发中用vant2框架中的Uploader 文件上传wx.uploadFile无反应和使用多图上传
网上有的说是bind:after-read="afterRead"的命名问题不支持-,但是我这儿执行了console.log("file",file);证明函数运行了。后来发现是multiple="true"原因开启了多图上传,如果是多图上传的话file就是数组了
36 2
|
1月前
|
小程序 前端开发 定位技术
【微信小程序】-- 常用视图容器类组件介绍 -- view、scroll-view和swiper(六)
【微信小程序】-- 常用视图容器类组件介绍 -- view、scroll-view和swiper(六)

热门文章

最新文章