首先建立一个入口IndexController.php文件
<?php namespace Home\Controller; use Think\Controller; use Com\Wechat; /** * 微信控制器 * 主要获取首页聚合数据 */ class IndexController extends Controller { //系统首页 public function index(){ $token = 'weixin'; //微信后台填写的TOKEN /* 加载微信SDK */ $wechat =new Wechat($token); /* 获取请求信息 */ $data = $wechat->request(); if($data && is_array($data)){ $content = '123'; //回复内容,回复不同类型消息,内容的格式有所不同 $type = 'text'; //回复消息的类型 /* 响应当前请求(自动回复) */ $wechat->response($content, $type); } } }
在Home模块下建立Com文件夹存放当前独立的类、
<?php /** * 微信封装类[请求或响应消息类型] * @author huangguojin * time 2015-7-14 */ namespace Com; class Wechat { /* 消息类型常量 */ const MSG_TYPE_TEXT = 'text'; const MSG_TYPE_IMAGE = 'image'; const MSG_TYPE_VOICE = 'voice'; const MSG_TYPE_VIDEO = 'video'; const MSG_TYPE_MUSIC = 'music'; const MSG_TYPE_NEWS = 'news'; const MSG_TYPE_LOCATION = 'location'; const MSG_TYPE_LINK = 'link'; const MSG_TYPE_EVENT = 'event'; /* 事件类型常量 */ const MSG_EVENT_SUBSCRIBE = 'subscribe'; const MSG_EVENT_SCAN = 'SCAN'; const MSG_EVENT_LOCATION = 'LOCATION'; const MSG_EVENT_CLICK = 'CLICK'; const MSG_EVENT_MASSSENDJOBFINISH = 'MASSSENDJOBFINISH'; /** * 微信推送过来的数据 * @var array */ private $data = array(); /** * 构造方法,用于实例化微信SDK * 自动回复消息时实例化该SDK * @param string $token 微信后台填写的TOKEN */ public function __construct($token){ echo "222";die; if($token){ self::auth($token) || exit; if(IS_GET){ exit($_GET['echostr']); } else { $xml = file_get_contents("php://input"); $xml = new \SimpleXMLElement($xml); $xml || exit; foreach ($xml as $key => $value) { $this->data[$key] = strval($value); } } } else { throw new \Exception('参数错误!'); } } /** * 获取微信推送的数据 * @return array 转换为数组后的数据 */ public function request(){ return $this->data; } /** * * 响应微信发送的信息(自动回复) * @param array $content 回复信息,文本信息为string类型 * @param string $type 消息类型 */ public function response($content, $type = self::MSG_TYPE_TEXT){ /* 基础数据 */ $data = array( 'ToUserName' => $this->data['FromUserName'], 'FromUserName' => $this->data['ToUserName'], 'CreateTime' => NOW_TIME, 'MsgType' => $type, ); /* 按类型添加额外数据 */ $content = call_user_func(array(self, $type), $content); if($type == self::MSG_TYPE_TEXT || $type == self::MSG_TYPE_NEWS){ $data = array_merge($data, $content); } else { $data[ucfirst($type)] = $content; } /* 转换数据为XML */ $xml = new \SimpleXMLElement('<xml></xml>'); self::data2xml($xml, $data); exit($xml->asXML()); } /** * 回复文本消息 * @param string $text 回复的文字 */ public function replyText($text){ return $this->response($text, self::MSG_TYPE_TEXT); } /** * 回复图片消息 * @param string $media_id 图片ID */ public function replyImage($media_id){ return $this->response($media_id, self::MSG_TYPE_IMAGE); } /** * 回复语音消息 * @param string $media_id 音频ID */ public function replyVoice($media_id){ return $this->response($media_id, self::MSG_TYPE_VOICE); } /** * 回复视频消息 * @param string $media_id 视频ID * @param string $title 视频标题 * @param string $discription 视频描述 */ public function replyVideo($media_id, $title, $discription){ return $this->response(func_get_args(), self::MSG_TYPE_VIDEO); } /** * 回复音乐消息 * @param string $title 音乐标题 * @param string $discription 音乐描述 * @param string $musicurl 音乐链接 * @param string $hqmusicurl 高品质音乐链接 * @param string $thumb_media_id 缩略图ID */ public function replyMusic($title, $discription, $musicurl, $hqmusicurl, $thumb_media_id){ return $this->response(func_get_args(), self::MSG_TYPE_MUSIC); } /** * 回复图文消息,一个参数代表一条信息 * @param array $news 图文内容 [标题,描述,URL,缩略图] * @param array $news1 图文内容 [标题,描述,URL,缩略图] * @param array $news2 图文内容 [标题,描述,URL,缩略图] * ... ... * @param array $news9 图文内容 [标题,描述,URL,缩略图] */ public function replyNews($news, $news1, $news2, $news3){ return $this->response(func_get_args(), self::MSG_TYPE_NEWS); } /** * 回复一条图文消息 * @param string $title 文章标题 * @param string $discription 文章简介 * @param string $url 文章连接 * @param string $picurl 文章缩略图 */ public function replyNewsOnce($title, $discription, $url, $picurl){ return $this->response(array(func_get_args()), self::MSG_TYPE_NEWS); } /** * 数据XML编码 * @param object $xml XML对象 * @param mixed $data 数据 * @param string $item 数字索引时的节点名称 * @return string */ protected static function data2xml($xml, $data, $item = 'item') { foreach ($data as $key => $value) { /* 指定默认的数字key */ is_numeric($key) && $key = $item; /* 添加子元素 */ if(is_array($value) || is_object($value)){ $child = $xml->addChild($key); self::data2xml($child, $value, $item); } else { if(is_numeric($value)){ $child = $xml->addChild($key, $value); } else { $child = $xml->addChild($key); $node = dom_import_simplexml($child); $cdata = $node->ownerDocument->createCDATASection($value); $node->appendChild($cdata); } } } } /** * 对数据进行签名认证,确保是微信发送的数据 * @param string $token 微信开放平台设置的TOKEN * @return boolean true-签名正确,false-签名错误 */ protected static function auth($token){ /* 获取数据 */ $data = array($_GET['timestamp'], $_GET['nonce'], $token); $sign = $_GET['signature']; /* 对数据进行字典排序 */ sort($data, SORT_STRING); /* 生成签名 */ $signature = sha1(implode($data)); return $signature === $sign; } /** * 构造文本信息 * @param string $content 要回复的文本 */ private static function text($content){ $data['Content'] = $content; return $data; } /** * 构造图片信息 * @param integer $media 图片ID */ private static function image($media){ $data['MediaId'] = $media; return $data; } /** * 构造音频信息 * @param integer $media 语音ID */ private static function voice($media){ $data['MediaId'] = $media; return $data; } /** * 构造视频信息 * @param array $video 要回复的视频 [视频ID,标题,说明] */ private static function video($video){ $data = array(); list( $data['MediaId'], $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['ThumbMediaId'], ) = $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['ArticleCount'] = count($articles); $data['Articles'] = $articles; return $data; } }
<?php /** * 微信封装类访问高级接口 * @author huangguojin * time 2015-7-14 */ namespace Com; 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_MUSIC = 'music'; const MSG_TYPE_NEWS = 'news'; const MSG_TYPE_LOCATION = 'location'; const MSG_TYPE_LINK = 'link'; 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 $mediaURL = 'http://file.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 * @author huangguojin */ 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('参数错误!'); } } 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; } $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 $token acess_token * @param string $lang 指定的语言 * @return array 用户信息数据,具体参见微信文档 */ public function getUserInfo($token, $lang = 'zh_CN'){ $query = array( 'access_token' => $token['access_token'], 'openid' => $token['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){ $param = array( 'access_token' => $this->accessToken, 'type' => $type ); $filename = realpath($filename); if(!$filename) throw new \Exception('资源路径错误!'); $file = array('media' => "@{$filename}"); $url = "{$this->mediaURL}/media/upload"; $data = self::http($url, $param, $file, 'POST'); return json_decode($data, true); } /** * 获取媒体资源下载地址 * 注意:视频资源不允许下载 * @param string $media_id 媒体资源id * @return string 媒体资源下载地址 */ public function mediaGet($media_id){ $param = array( 'access_token' => $this->accessToken, 'media_id' => $media_id ); $url = "{$this->mediaURL}/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 = ''){ $params = array('access_token' => $this->accessToken); if(!empty($param) && is_array($param)){ $params = array_merge($params, $param); } $url = "{$this->apiURL}/{$name}"; if(!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; } }
自动加载到系统中。在Application/Common/Conf/config.php 映射到对应的文件
/* 模块相关配置 */ 'AUTOLOAD_NAMESPACE' => array( 'Addons' => ONETHINK_ADDON_PATH, 'Com' => APP_PATH . 'Home/Com' ), //扩展模块列表
访问:localhost/XXXX/Index