微信公众号开发模型WeChat

模型:WeChat (回复参考weiphp)

<?php
namespace Org;
/**
 * 微信开发工具类
 * Class WeChat
 * Author chenqionghe
 * @package Org
 */
class WeChat
{
    const LOG_NAME  = "PHP_LOG_%s.log.php"; //日志名
    const LOG_DIR   = "./Log/%s/";          //日志目录

    static private $fromUser = ‘‘;  //当前消息的发送者
    static private $toUser = ‘‘;    //当前消息的接收者
    static private $member = ‘‘;    //公众号会员记录

    /**
     * 设置要操作的微信公众号
     * @param $mid  公众号id
     */
    static public function setMember($mid)
    {
        self::$member = M(‘Member‘)->find($mid);
    }
    /**
     * 处理来自微信服务器的消息
     * @param $callback
     * @access public
     * @return void
     */
    static public function process($callback)
    {
        //如果回调函数没有设置,则退出
        if (!is_callable($callback))
        {
            return;
        }
        $postData = $GLOBALS[‘HTTP_RAW_POST_DATA‘];
        if (empty($postData)) return;  //如果没有POST数据,则退出
        $object = simplexml_load_string($postData, ‘SimpleXMLElement‘, LIBXML_NOCDATA);//解析POST数据(XML格式)
        $messgeType = trim($object->MsgType);    //取得消息类型
        self::$fromUser = $object->FromUserName; //记录消息发送方(不是发送者的微信号,而是一个加密后的OpenID)
        self::$toUser = $object->ToUserName;     //记录消息接收方(就是公共平台的OpenID)
        self::addLog($postData,1,$messgeType);       //记录日志

        //根据不同的消息类型,分别处理
        switch($messgeType)
        {
            case "text":   //文本消息
                //调用回调函数
                call_user_func($callback, "Text", array(‘Content‘=>$object->Content));
                break;
            case "image":  //图片消息
                call_user_func($callback, "Image",array(‘PicUrl‘=>$object->PicUrl, ‘MediaId‘=>$object->MediaId));
                break;
            case "voice":  //音频消息
                call_user_func($callback, "Voice",array(‘MediaId‘=>$object->MediaId, ‘Format‘=>$object->Format,‘Recognition‘=>$object->Recognition) );
                break;
            case "video":  //视频消息
                call_user_func($callback, "Video", array(‘MediaId‘=>$object->MediaId, ‘ThumbMediaId‘=>$object->ThumbMediaId));
                break;
            case "shortvideo":  //小视频消息
                call_user_func($callback, "Shortvideo", array(‘MediaId‘=>$object->MediaId, ‘ThumbMediaId‘=>$object->ThumbMediaId));
                break;
            case "location": //定位信息
                call_user_func($callback, "Location", array(‘Label‘=>$object->Label, ‘Location_X‘=>$object->Location_X, ‘Location_Y‘=>$object->Location_Y,‘Scale‘=>$object->Scale));
                break;
            case "link":  //链接信息
                call_user_func($callback, "Link", array(‘Url‘=>$object->Url, ‘Title‘=>$object->Title, ‘Description‘=>$object->Description));
                break;
            case "event":  //事件
                switch ($object->Event)
                {
case "subscribe":           //订阅事件
    call_user_func($callback, "Subscribe",array( ‘EventKey‘=>$object->EventKey, ‘Ticket‘=>$object->Ticket));
    break;
case "unsubscribe":         //取消订阅事件
    call_user_func($callback, "UnSubscribe", array(‘FromUserName‘=>$object->FromUserName));
    break;
case "CLICK":               //点击菜单拉取消息时的事件
    call_user_func($callback, "Click", array(‘EventKey‘=>$object->EventKey));
    break;
case "VIEW":                //点击菜单跳转链接时的事件
    call_user_func($callback, "View",array(‘EventKey‘=> $object->EventKey));
    break;
case "scancode_push":       //扫码推事件的事件推送
    call_user_func($callback, "ScanPush",array( ‘EventKey‘=>$object->EventKey,‘ScanCodeInfo‘=>$object->ScanCodeInfo,‘ScanType‘=>$object->ScanType,‘ScanResult‘=>$object->ScanResult));
    break;
case "scancode_waitmsg":    //扫码推事件且弹出“消息接收中”提示框的事件推送
    call_user_func($callback, "ScanWaitmsg",array( ‘EventKey‘=>$object->EventKey,‘ScanCodeInfo‘=>$object->ScanCodeInfo,‘ScanType‘=>$object->ScanType,‘ScanResult‘=>$object->ScanResult));
    break;
case "pic_sysphoto":        //弹出系统拍照发图的事件推送
    call_user_func($callback, "PicSysPhoto",array( ‘EventKey‘=>$object->EventKey, ‘SendPicsInfo‘=>$object->SendPicsInfo,‘Count‘=>$object->Count,‘PicList‘=>$object->PicList,‘PicMd5Sum‘=>$object->PicMd5Sum));
    break;
case "pic_photo_or_album":  //弹出拍照或者相册发图的事件推送
    call_user_func($callback, "PicPhotoOrAlbum",array( ‘EventKey‘=>$object->EventKey, ‘SendPicsInfo‘=>$object->SendPicsInfo,‘Count‘=>$object->Count,‘PicList‘=>$object->PicList,‘PicMd5Sum‘=>$object->PicMd5Sum));
    break;
case "pic_weixin":          //弹出微信相册发图器的事件推送
    call_user_func($callback, "PicWeixin",array( ‘EventKey‘=>$object->EventKey, ‘SendPicsInfo‘=>$object->SendPicsInfo,‘Count‘=>$object->Count,‘PicList‘=>$object->PicList,‘PicMd5Sum‘=>$object->PicMd5Sum));
    break;
case "location_select":     //弹出地理位置选择器的事件推送
    call_user_func($callback, "LocationSelect",array( ‘EventKey‘=>$object->EventKey, ‘SendLocationInfo‘=>$object->SendLocationInfo,‘Location_X‘=>$object->Location_X,‘Location_Y‘=>$object->Location_Y,‘Scale‘=>$object->Scale,‘Label‘=>$object->Label,‘Poiname‘=>$object->Poiname));
    break;
case ‘LOCATION‘:            //上报地址位置事件
    call_user_func($callback, "UpLocation",array( ‘Latitude‘=>$object->Latitude, ‘Longitude‘=>$object->Longitude, ‘Precision‘=>$object->Precision));
    break;
default :
    //Unknow Event
    break;
                }
                break;
            default:
                //未知消息类型
                break;
        }
    }

    /**
     * 获取access_token
     * $mid 公众号id
     * @access public
     * @return mixed
     */
    static public function getAcctoken ($mid=‘‘)
    {
        if(empty($mid)) {
            $mid = defined(‘MID‘) ? MID : session(‘mid‘);   //如果定义了常量MID说明是微信请求的 ,否则是本地
        }
        $token = S(‘access_token‘.$mid);
        if ($token === false)
        {
            if(empty(self::$member)) {
                self::$member = M(‘Member‘)->find($mid);
            }
            $url = ‘https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=‘.self::$member[‘appid‘].‘&secret=‘.self::$member[‘secret‘];
            $result = self::http_get($url);
            $result = json_decode($result, true);
            $token = $result[‘access_token‘];
            S(‘access_token‘.$mid, $token, $result[‘expires_in‘] - 1000);
        }
        return $token;
    }

    /**
     * 验证签名是否正确
     * @access public
     * @param $token
     */
    static public function checkSignature($token)
    {
        $tmpArr = array($token,$_GET ["timestamp"],$_GET ["nonce"]);
        sort($tmpArr, SORT_STRING );
        $tmpStr = sha1(implode( $tmpArr));
        if ($tmpStr == $_GET ["signature"])
        {
            ob_clean();
            echo $_GET ["echostr"];
        }
        self::addLog($_GET,4,‘微信接入‘);//记录日志
        exit;
    }

    /*************************************************** 发送消息响应微信 begin ***************************************************/
    /**
     * 回复文本消息
     * @param $content 文本内容
     * @access public
     * @return void
     */
    static public function replyText($content)
    {
        $msg [‘Content‘] = $content;
        self::_replyData ( $msg, ‘text‘ );
    }

    /**
     * 回复图片消息
     * @param $media_id MediaId
     */
    static public function replyImage($media_id)
    {
        $msg [‘Image‘] [‘MediaId‘] = $media_id;
        self::_replyData ( $msg, ‘image‘ );
    }

    /**
     * 回复语音消息
     * @param $media_id MediaId
     * @access public
     * @return void
     */
    static public function replyVoice($media_id)
    {
        $msg [‘Voice‘] [‘MediaId‘] = $media_id;
        $msg [‘Voice‘] [‘MediaId‘] = $media_id;
        self::_replyData ( $msg, ‘voice‘ );
    }

    /**
     * 回复视频消息
     * @param $media_id MediaId
     * @param string $title 标题
     * @param string $description 简介
     * @access public
     * @return void
     */
    static public function replyVideo($media_id, $title = ‘‘, $description = ‘‘)
    {
        $msg [‘Video‘] [‘MediaId‘] = $media_id;
        $msg [‘Video‘] [‘Title‘] = $title;
        $msg [‘Video‘] [‘Description‘] = $description;
        self::_replyData ( $msg, ‘video‘ );
    }

    /**
     * 回复音乐消息
     * @param $media_id MediaId
     * @param string $title 标题
     * @param string $description 简介
     * @param $music_url 音乐地址
     * @param $HQ_music_url 高品质音乐地址
     * @access public
     * @return void
     */
    static function replyMusic($media_id, $title = ‘‘, $description = ‘‘, $music_url, $HQ_music_url)
    {
        $msg [‘Music‘] [‘ThumbMediaId‘] = $media_id;
        $msg [‘Music‘] [‘Title‘] = $title;
        $msg [‘Music‘] [‘Description‘] = $description;
        $msg [‘Music‘] [‘MusicURL‘] = $music_url;
        $msg [‘Music‘] [‘HQMusicUrl‘] = $HQ_music_url;
        self::_replyData ( $msg, ‘music‘ );
    }

    /**
     * 回复图文消息 格式如下:
        array(
            array($Title, $Description, $PicUrl , $Url),
            array($Title, $Description, $PicUrl , $Url),
        );
     * @param array $articles
     * @access public
     * @return void
     */
    static public function replyNews($articles)
    {
        foreach($articles as $k=>$v)
        {
            $arr[] = array(‘Title‘=>$v[0],‘Description‘=>$v[1],‘PicUrl‘=>$v[2],‘Url‘=>$v[3]);
        }
        $msg [‘ArticleCount‘] = count ( $articles );
        $msg [‘Articles‘] = $arr;

        self::_replyData ( $msg, ‘news‘ );
    }

    /**
     * 将消息转发给客服
     * @param string $kf_account 指定客服的账号,不传则由微信分配
     * @access public
     * @return void
     */
    static public function replyKefu($kf_account=‘‘)
    {
        if(!empty($kf_account))
        {
            $msg [‘TransInfo‘][] = $kf_account;
        }
        self::_replyData($msg,‘transfer_customer_service‘,‘KfAccount‘);
    }

    /**
     * 发送回复消息到微信平台
     * @param array $msg 消息数组
     * @param $msgType 消息类型
     * @param string $item 包含子元素的元素名
     * @access private
     * @return void
     */
    static private function _replyData($msg, $msgType, $item = ‘item‘)
    {
        $msg [‘ToUserName‘] = strval(self::$fromUser);
        $msg [‘FromUserName‘] = strval(self::$toUser);
        $msg [‘CreateTime‘] = NOW_TIME;
        $msg [‘MsgType‘] = $msgType;
        if($_REQUEST [‘doNotInit‘])
        {
            dump($msg);
            exit;
        }
        $xml = new SimpleXMLElement ( ‘<xml></xml>‘ );
        self::_data2xml ( $xml, $msg ,$item);
        $str = $xml->asXML ();
        self::addLog($str,2,‘发送消息‘);//记录日志
        echo $str;
    }

    /**
     * 组装xml数据
     * @param $xml xml对象
     * @param $data 要格式化的数组
     * @param string $item 包含子元素的元素名
     * @access public
     * @return void
     */
    static public function _data2xml($xml, $data, $item = ‘item‘)
    {
        foreach ( $data as $key => $value )
        {
            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 );
$node->appendChild ( $node->ownerDocument->createCDATASection ( $value ) );
                }
            }
        }
    }
    /*************************************************** 发送消息响应微信 end ***************************************************/



    /*************************************************** 上传下载文件 begin ***************************************************/
    /**
     * 上传临时素材
     * @param $file 要发送的文件
     * @param string $type 文件类型
     * @access public
     * @return mixed
     */
    static public function uploadFile($file, $type = ‘image‘)
    {
        // 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
        $post_data = array(‘meida‘=>"@".$file,‘type‘=>$type);
        $url = "http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=".self::getAcctoken() ."&type=".$type;
        return self::http_post($url,$post_data);
    }

    /* 上传永久素材*/
    static public function uploadYJFile($file, $type = ‘image‘)
    {
        // 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
        $post_data = array(‘meida‘=>"@".$file,‘type‘=>$type);
        $url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=".self::getAcctoken();
        return self::http_post($url,$post_data);
    }

    /**
     * 下载多媒体文件
     * @param $media_id MediaId
     * @access public
     * @return void
     */
    static public function downloadFile($media_id)
    {
        $url = ‘‘;
        $filename = date(‘Y-m-d_H_i_s‘,time()).‘.jpg‘;//设置保存的文件名
        $ch = curl_init ();
        curl_setopt ( $ch, CURLOPT_URL, $url );
        curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
        curl_setopt ( $ch, CURLOPT_TIMEOUT, 60 );
        $temp = curl_exec($ch);
        if(@file_get_contents($filename,$temp) && !curl_errno($ch)) {
            echo $filename;
        }
        else  {
            echo false;
        } // TODO
    }
    /*************************************************** 上传下载文件 end ***************************************************/




    /*************************************************** 执行http请求方法 begin ***************************************************/
    /**
     * 发送GET 请求
     * @param string $url 地址
     * @access public
     * @return mixed
     */
    static public function http_get($url)
    {
        $oCurl = curl_init ();
        if (stripos ( $url, "https://" ) !== FALSE)
        {
            curl_setopt ( $oCurl, CURLOPT_SSL_VERIFYPEER, FALSE );
            curl_setopt ( $oCurl, CURLOPT_SSL_VERIFYHOST, FALSE );
        }
        curl_setopt ( $oCurl, CURLOPT_URL, $url );
        curl_setopt ( $oCurl, CURLOPT_RETURNTRANSFER, 1 );
        $sContent = curl_exec ( $oCurl );
        $aStatus = curl_getinfo ( $oCurl );
        curl_close ( $oCurl );
        return intval ( $aStatus ["http_code"] ) == 200 ? $sContent : false;
    }

    /**
     * 发送POST 请求
     * @param string $url 地址
     * @param array $param 参数
     * @access public
     * @return string content
     */
    static public function http_post($url, $param)
    {
        $oCurl = curl_init ();
        if (stripos ( $url, "https://" ) !== FALSE)
        {
            curl_setopt ( $oCurl, CURLOPT_SSL_VERIFYPEER, FALSE );
            curl_setopt ( $oCurl, CURLOPT_SSL_VERIFYHOST, false );
        }
        curl_setopt ( $oCurl, CURLOPT_URL, $url );
        curl_setopt ( $oCurl, CURLOPT_RETURNTRANSFER, 1 );
        curl_setopt ( $oCurl, CURLOPT_POST, true );
        curl_setopt ( $oCurl, CURLOPT_POSTFIELDS, $param );
        $sContent = curl_exec ( $oCurl );
        $aStatus = curl_getinfo ( $oCurl );
        curl_close ( $oCurl );
        return intval ( $aStatus ["http_code"] ) == 200 ? $sContent : false;
    }
    /*************************************************** 执行http请求方法 end ***************************************************/


    /*************************************************** 记录日志 begin ***************************************************/
    /**
     * @param $data 要记录的内容
     * @param int $type 日志类型(1:Receive,2:Send,3:Sql,4:Signature)
     * @param string $comment 备注信息
     * @return null
     */
    static public function addLog($data,$type=1,$comment=‘‘)
    {
        //日志存放文件夹名
        $dirName = array(‘1‘ => ‘Receive‘,‘2‘ => ‘Send‘,‘3‘ => ‘Sql‘,‘4‘=>‘Signature‘);

        //构造日志文件名
        $logDir = sprintf(self::LOG_DIR, $dirName[$type]); //格式化文件夹
        $logName = sprintf(self::LOG_NAME,date(‘ymd‘));            //格式化文件名
        if(!is_dir($logDir)) mkdir($logDir,0777,true);             //如果文件夹不存在,创建
        $logFile = $logDir.$logName;            //日志最终文件名

        //构造日志文件内容
        $separator = str_repeat(‘*‘,42);
        $content[] = "/$separator <- Begin:".date(‘Y-m-d H:i:s‘)." --> $separator/";
        $content[] = trim(var_export($data,true),‘‘‘);
        if(!empty($comment)) $content[] = ‘备注: ‘.$comment;
        $content[] = "/$separator <------------  End ------------> $separator/";
        $logContent =   PHP_EOL.implode(PHP_EOL,$content);

        //写入日志文件
        $fp = fopen($logFile,"a+");
        flock($fp, LOCK_EX) ;
        fwrite($fp,$logContent);
        flock($fp, LOCK_UN);
        fclose($fp);
    }
    /*************************************************** 记录日志 end ***************************************************/



    /*************************************************** oAuth网页授权获取用户信息 begin ***************************************************/

    /**
     * 网页授权获取用户信息
     * @param $type 类型为openid或者userinfo
     * @return mixed
     */
    public static function oAuthGet($type)
    {
        if (!isset($_GET[‘code‘]))
        {
            //获取code码,以获取openid
            $scopeArr = array(‘openid‘=>‘snsapi_base‘,‘userinfo‘=>‘snsapi_userinfo‘);
            $scope = $scopeArr[$type];
            $redirect_url = urlencode(‘http://‘.$_SERVER[‘HTTP_HOST‘].$_SERVER[‘PHP_SELF‘].$_SERVER[‘QUERY_STRING‘]);
            $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=".self::$member[‘appid‘]."&redirect_uri=$redirect_url&response_type=code&scope=$scope&state=oAuthInfo#wechat_redirect";
            header ( ‘Location: ‘ . $url );
        }
        else
        {
            /******************* 1.回调页面得到code *******************/
            $code = $_GET[‘code‘];
            /******************* 2.用code去获取access_token和openid *******************/
            $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=".self::$member[‘appid‘]."&secret=".self::$member[‘secret‘]."&code=$code&grant_type=authorization_code";
            $result = self::http_get($url);
            $result = json_decode($result,true);
            $openid =  $result[‘openid‘];
            if($result[‘scope‘] == ‘snsapi_base‘)   //如果类型是snsapi_base,只返回openid
                return $openid;
            else
            {
                $access_token = $result[‘access_token‘];    //access_token
                /******************* 3.用获取到的openid和access_token获取获取用户详细信息 *******************/
                $url = "https://api.weixin.qq.com/sns/userinfo?access_token=$access_token&openid=$openid&lang=zh_CN";
                $result = self::http_get($url);
                return json_decode($result,true);
            }
        }

    }
    /*************************************************** oAuth网页授权获取用户信息 end ***************************************************/



    /*************************************************** 获取jssdk配置 begin ***************************************************/

    /**
     * 获取 JSSDK 配置
     * @param bool $debug 是否开启调试模式
     * @access public
     * @return array
     */
    public static function getJssdkConf($debug = false) {
        $data = array(
            ‘noncestr‘ => self::createNonceStr(),
            ‘jsapi_ticket‘ => self::getJsApiTicket(),
            ‘timestamp‘ => time(),
            ‘url‘ => SERVER.__SELF__,
        );
        $str = ‘jsapi_ticket=‘.$data[‘jsapi_ticket‘].‘&noncestr=‘.$data[‘noncestr‘].‘&timestamp=‘.$data[‘timestamp‘].‘&url=‘.$data[‘url‘];
        return array(
            ‘debug‘	    => $debug,
            ‘appId‘		=> self::$member[‘appid‘],
            ‘timestamp‘	=> $data[‘timestamp‘],
            ‘nonceStr‘	=> $data[‘noncestr‘],
            ‘signature‘	=> sha1($str),
        );
    }

    private static function getJsApiTicket() {
        $mid = self::$member[‘id‘];
        $ticket = S(‘jsapi_ticket‘.$mid);
        if ($ticket == false) {
            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=".self::getAcctoken($mid);
            $result =WeChat::http_get($url);
            $result = json_decode($result, true);
            $ticket = $result[‘ticket‘];
            S(‘jsapi_ticket‘.$mid, $ticket, $result[‘expires_in‘] - 1000);
        }
        return $ticket;
    }

    private static function createNonceStr($length = 16) {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
    /*************************************************** 获取jssdk配置 end ***************************************************/

}

回复微信控制器:WeChatController

<?php
namespace ManageController;
use ThinkController;
use OrgWeChat;
class WeChatController extends Controller
{
    /* 接收微信的消息 */
    public function index()
    {
        $id = intval(I(‘get.id‘));
        $info = M(‘Member‘)->where("status=‘1‘")->find($id);
        if (!is_array($info)) exit(‘请求无效!‘);
        if (!empty ($_GET [‘echostr‘]) && !empty ($_GET ["signature"]) && !empty ($_GET ["nonce"])) {
            WeChat::checkSignature($info[‘token‘]);  //微信接入验证
        }
        define(‘MID‘, $id);     //定义公众号常量MID
        WeChat::process(array($this, ‘reply‘));    //处理消息
    }

    /* 回复微信消息  */
    public function reply($messageType, $data)
    {
        $controller = ‘Process‘ . ‘Controller‘ . $messageType . ‘Controller‘;
        $class = new ReflectionClass($controller);
        $method = $class->getMethod(‘run‘);
        $instance = $class->newInstance();
        $method->invokeArgs($instance, array($data));
    }
}

WeChat控制器,负责将请求分发给Process模块下对应控制器处理, 如Click类型由ClickController处理, Text类型由TextController处理,下面给出一个TextController控制器

<?php
namespace ProcessController;
use OrgWeChat;

class TextController extends ProcessController
{
    /* 用户点击菜单 */
    public function run($data)
    {
            WeChat::replyText("您输入的内容是:" .$data[‘Content‘]);
        }
    }

}

 oAuth网页授权获取用户信息示例TestController

<?php
namespace TestController;
use ThinkController;
use OrgWeChat;
class TestController extends Controller
{
    /* 获取用户openid */
    public function get_openid()
    {
        WeChat::setMember(2);
        $openid =  WeChat::oAuthGet(‘openid‘);
    }

    /* 获取用户信息 */
    public function get_userInfo()
    {
        WeChat::setMember(2);
        $userinfo = WeChat::oAuthGet(‘userinfo‘);
        var_dump($userinfo);
    }
}

 

微信公众号开发模型WeChat

原文:http://www.cnblogs.com/chenqionghe/p/4633586.html

以上是微信公众号开发模型WeChat的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>