微信公众号接口类(PHP版本)
【项目需求】
通过微信提供的接口,实现微信公众号与后端的应用程序数据交互、消息响应等功能。
【项目疑难点】
- 理解接口工作方式,统一接口API,响应速度、安全性等
【代码举例】
WeixinApi.class.php 微信公众号接口基类
- <?php
- /**
- * 微信API 公用方法
- *
- * PHP version 5
- *
- * @category Lib
- * @package COM
- * @subpackage GZNC
- * @author zhongyiwen
- * @version SVN: $Id: WeixinApi.class.php 10 2013-10-08 01:34:05Z zhongyw $
- */
- /**
- * 错误代码
- */
- define('WXAPI_ERR_CONFIG', 1001); // 配置错误
- define('WXAPI_ERR_HTTP', 1002); // 请求失败
- define('WXAPI_ERR_LOGIN', 1003); // 登录失败
- define('WXAPI_ERR_FETCH_DATA', 1004); // 获取数据失败
- define('WXAPI_ERR_MISS_RESPONSE', 1005); // 缺少响应
- define('WXAPI_ERR_BAD_SIGNATURE', 1006); // 签名校验失败
- define('WXAPI_ERR_BAD_DECRYPT', 1007); // 消息解密失败
- define('WXAPI_ERR_BAD_ENCRYPT', 1008); // 消息加密失败
- define('WXAPI_ERR_ACCESS_TOKEN', 1009); // access token凭证错误
- /**
- * 日志级别
- */
- define('WXAPI_LOG_EMERG', 'EMERG'); // 严重错误: 导致系统崩溃无法使用
- define('WXAPI_LOG_ALERT', 'ALERT'); // 警戒性错误: 必须被立即修改的错误
- define('WXAPI_LOG_CRIT', 'CRIT'); // 临界值错误: 超过临界值的错误,例如一天24小时,而输入的是25小时这样
- define('WXAPI_LOG_ERR', 'ERR'); // 一般错误: 一般性错误
- define('WXAPI_LOG_WARN', 'WARN'); // 警告性错误: 需要发出警告的错误
- define('WXAPI_LOG_NOTICE', 'NOTIC'); // 通知: 程序可以运行但是还不够完美的错误
- define('WXAPI_LOG_INFO', 'INFO'); // 信息: 程序输出信息
- define('WXAPI_LOG_DEBUG', 'DEBUG'); // 调试: 调试信息
- define('WXAPI_LOG_EXCEPTION', 'EXCEPTION'); // 异常信息
- /**
- * 微信接口默认常量值
- */
- define('WXAPI_ACCESS_TOKEN_EXPIRE', 7100); // access token有效时间,设置比微信默认有效时间7200秒小,避免出现过期错误
- define('WXAPI_ACCESS_TOKEN_LIMIT', 2000); // access token每日限制次数
- define('WXAPI_JSAPI_TICKET_EXPIRE', 7100); // jsapi ticket有效时间,单位秒
- define('WXAPI_JSAPI_TICKET_LIMIT', 2000); // jsapi ticket每日限制次数
- define('WXAPI_QRCODE_MIN_SCENE', 1); // 二维码场景值最小值
- define('WXAPI_QRCODE_MAX_SCENE', 2147483647); // 二维码场景值最大值, 32位非0整型
- define('WXAPI_QRCODE_MAX_LIMIT_SCENE', 100000); // 永久二维码场景值最大值
- define('WXAPI_QRCODE_EXPIRE', 1800); // 临时二维码有效时间,单位秒
- define('WXAPI_GROUP_MIN_CUSTOM_ID', 100); // 用户自定义用户组起始id值
- /**
- * 微信暗语
- */
- define('WXAPI_ARGOT_WHO_AM_I', 'show me your name'); // 显示当前4susername
- define('WXAPI_ARGOT_DESTORY_SESSION', 'let me out'); // 清除当前用户session
- class WeixinApi{
- /**
- * 实例化对象
- *
- * @var array
- */
- protected static $_instance = array();
- /**
- * 是否启用缓存
- * @var bool
- */
- protected $_cache = false;
- /**
- * 是否启用调试
- * @var bool
- */
- protected $_debug = false;
- /**
- * 配置对象实例
- * @var object
- */
- public $Config;
- /**
- * 错误信息
- * @var string
- */
- protected $_error = NULL;
- public function __construct($Config=NULL){
- $this->Config = is_object($Config)?$Config:self::instance('WeixinApi_Config');
- $this->_cache = $this->Config->Cache;
- }
- /**
- * 取得对象实例 支持调用类的静态方法
- * @param string $class 对象类名
- * @param string $method 类的静态方法名
- * @return object
- */
- public static function instance($class,$args=array()) {
- $identify = $class.md5(serialize($args));
- if(!isset(WeixinApi::$_instance[$identify])) {
- if(!class_exists($class)){
- require $class . ".class.php";
- }
- if(class_exists($class)){
- $arg_str = '';
- if($args && is_array($args)){
- foreach ($args as $i=>$arg){
- /*
- if(is_object($arg) || is_array($arg)){
- return WeixinApi::throw_exception(
- "Cann't init class $class instanse with object argument"
- , WXAPI_ERR_CONFIG
- , array('class' => $class, 'args' => $args)
- , __FILE__, __LINE);
- }else{
- $arg_str = "'" . implode("', '", array_map('addslashes', $args)) . "'";
- }*/
- if(is_object($arg) || is_array($arg)){
- $arg_param_name = 'arg_param' . $i;
- $$arg_param_name = $arg;
- $arg_str .= ", \${$arg_param_name}";
- }else{
- $arg_str .= ", '" . addcslashes($arg, "'") . "'";
- }
- }
- if($arg_str){
- $arg_str = substr($arg_str, 2);
- }
- }elseif($args && is_object($args)){
- /*
- return WeixinApi::throw_exception(
- "Cann't init class $class instanse with object argument"
- , WXAPI_ERR_CONFIG
- , array('class' => $class, 'args' => $args)
- , __FILE__, __LINE);
- */
- $arg_param_name = 'arg_param';
- $$arg_param_name = $args;
- $arg_str = "\${$arg_param_name}";
- }elseif($args){
- $arg_str = "'" . addcslashes($args, "'") . "'";
- }
- $code = "return new " . $class . "(" . $arg_str . ");";
- $o = eval($code);
- if(!$o){
- return WeixinApi::throw_exception(
- "Cann't init class instanse: $class"
- , WXAPI_ERR_CONFIG
- , array('class' => $class, 'args' => $args)
- , __FILE__, __LINE);
- }
- WeixinApi::$_instance[$identify] = $o;
- }
- else{
- return WeixinApi::throw_exception(
- "Cann't found class: $class file."
- , WXAPI_ERR_CONFIG
- , array('class' => $class, 'args' => $args)
- , __FILE__, __LINE__);
- }
- }
- return self::$_instance[$identify];
- }
- public static function throw_exception($message, $code=NULL, $data=NULL, $file=NULL, $line=NULL){
- if(!class_exists('WeixinApi_Exception')){
- require 'WeixinApi_Exception.class.php';
- }
- // 只有配置错误才再次抛出异常
- //if($code==WXAPI_ERR_CONFIG){
- throw new WeixinApi_Exception($message, $code, $data, $file, $line);
- //}else{
- // return false;
- //}
- }
- protected function _throw_exception($message, $code=NULL, $data=NULL, $file=NULL, $line=NULL){
- try{
- WeixinApi::throw_exception($message, $code, $data, $file, $line);
- }catch(Exception $e){
- //$this->_error = $e->getMessage();
- $this->_setError($e->getMessage());
- $this->_log($e->__toString(), WXAPI_LOG_ERR);
- // 只有配置错误才再次抛出异常
- if($code==WXAPI_ERR_CONFIG){
- throw $e;
- }else{
- return false;
- }
- }
- }
- public function getError(){
- return is_array($this->_error)?implode(',', $this->_error):$this->_error;
- }
- /**
- * 设置错误信息
- * @param string $error
- */
- protected function _setError($error){
- $this->_error[] = $error;
- }
- public function __get($n){
- if(isset($this->$n)){
- return $this->$n;
- }else if(in_array($n, array('Http', 'Cache', 'Log'))){
- if('Http'==$n && !$this->Config->$n){
- return $this->_throw_exception("$n is not setted in your config"
- , WXAPI_ERR_CONFIG
- , array('class'=>$n)
- , __FILE__, __LINE__
- );
- }elseif(!$this->Config->$n){
- // Do Nothing
- // Disabled Cache or Log
- return false;
- }
- if(is_object($this->Config->$n)){
- return $this->Config->$n;
- }elseif(is_array($this->Config->$n)){
- list($callback, $params) = $this->Config->$n;
- if(!is_array($params)){
- $params = array($params);
- }
- return call_user_func_array($callback, $params);
- }else{
- return $this->$n = WeixinApi::instance($this->Config->$n);
- }
- }else{
- return false;
- }
- }
- protected function _check_http_url($url){
- if(strcasecmp('http', substr($url, 0, 4))){
- $url = $this->Config->ApiGateway . $url;
- }
- return $url;
- }
- protected function _check_http_ssl($url){
- if(!strcasecmp('https://', substr($url, 0, 8))){
- $this->Http->setSsl();
- // 指定ssl v3
- // 2014.09.05 zhongyw 微信API不能指定用ssl v3版本
- //$this->Http->setOpt(CURLOPT_SSLVERSION, 3);
- // 指定TLS
- // 2014.10.31 zhongyw
- // 微信公众平台将关闭掉SSLv2、SSLv3版本支持,不再支持部分使用SSLv2、 SSLv3或更低版本的客户端调用。请仍在使用这些版本的开发者于11月30日前尽快修复升级。
- defined('CURL_SSLVERSION_TLSv1') || define('CURL_SSLVERSION_TLSv1', 1); // 兼容PHP<=5.3
- $this->Http->setOpt(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
- }
- return $url;
- }
- protected function _check_http_data($data){
- return $data;
- }
- /**
- * 发送GET请求
- *
- * @param string $url 链接
- * @param string|array $data 参数
- * @param bool $check 是否检查链接和参数
- * @return string
- */
- public function get($url, $data = null, $check=true) {
- if ($check) {
- $url = $this->_check_http_url ( $url );
- $url = $this->_check_http_ssl ( $url );
- $data = $this->_check_http_data ( $data );
- }
- if(!($return = $this->Http->get($url, $data)) && ($error=$this->Http->getError())){
- return $this->_throw_exception(
- $error
- , WXAPI_ERR_HTTP
- , array('url' => $url, 'data' => $data, 'method' => 'get', 'response' => $return)
- , __FILE__, __LINE__);
- }
- return $return;
- }
- /**
- * 发送POST请求
- *
- * @param string $url 链接
- * @param array $data 参数
- * @param bool $check 是否检查链接和参数
- * @return string
- */
- public function post($url, $data, $check=true) {
- if ($check) {
- $url = $this->_check_http_url ( $url );
- $url = $this->_check_http_ssl ( $url );
- $data = $this->_check_http_data ( $data );
- }
- // 使用plainPost
- if(!($return = $this->Http->plainPost($url, $data)) && ($error=$this->Http->getError())){
- return $this->_throw_exception(
- $error
- , WXAPI_ERR_HTTP
- , array('url' => $url, 'data' => $data, 'method' => 'post', 'response' => $return)
- , __FILE__, __LINE__);
- }
- return $return;
- }
- public function setHttpOption($opt, $val=NULL){
- if(!$opt){
- return false;
- }
- $options = array();
- if(!is_array($opt)){
- $options = array($opt=>$val);
- }else{
- $options = $opt;
- }
- foreach($options as $opt=>$val){
- $this->Http->setOpt(constant($opt), $val);
- }
- }
- /**
- * 运行回调函数
- *
- * 回调函数支持以下几种格式:
- * 1、直接函数:funcname,或带参数:array(funcname, params)
- * 2、静态方法:array(array('WeixinApi', 'methodname'), params)
- * 3、对象方法:array(Object, 'methodname') 或 array(array(Object, 'methodname'), params)
- * 4、二次回调,如:
- * array(array(
- array(array('WeixinApi', 'instance'), 'S4WeixinResponse')
- , 'run')
- , '')
- 可以先调用Runder::instance()初始化S4Web实例后,再调用S4Web->apiglog_save()方法执行回调
- *
- * @param mixed $callback 回调函数
- * @param array $extra_params 回调参数
- * @return mixed
- */
- protected function _run_callback($callback, $extra_params=array(), &$callbackObject=NULL) {
- $extra_params = is_array ( $extra_params ) ? $extra_params : ($extra_params ? array (
- $extra_params
- ) : array ());
- $params = $extra_params;
- if(is_object($callback)){
- return $this->_throw_exception(
- "Object callback must set method"
- , SCRIPT_ERR_CONFIG
- , array('callback'=>$callback)
- , __FILE__, __LINE__
- );
- }
- else if (is_array ( $callback )) {
- $func = $callback [0];
- if (! empty ( $callback [1] )) {
- if (is_array ( $callback [1] )) {
- $params = array_merge ( $extra_params, $callback [1] );
- } else {
- $params [] = $callback [1];
- }
- }
- if (is_object ( $func )) {
- $callbackObject = $func;
- // 注意:此处不需要传$params作为参数
- return call_user_method_array ( $callback [1], $callback [0], $extra_params );
- } elseif (is_object ( $callback [0] [0] )) {
- $callbackObject = $callback [0] [0];
- return call_user_method_array ( $callback [0] [1], $callback [0] [0], $params);
- }
- } else {
- $func = $callback;
- }
- if(is_array($func) && is_array($func[0])){
- $call = call_user_func_array($func[0][0], is_array($func[0][1])?$func[0][1]:array($func[0][1]));
- if($call===false){
- return false;
- }
- $func = array($call, $func[1]);
- }
- if(is_array($func) && is_object($func[0])){
- $callbackObject = $func[0];
- }
- return call_user_func_array ( $func, $params);
- }
- /**
- * 是否缓存
- * @param bool|int $cache true = 启用缓存,false = 不缓存,-1 = 重新生成缓存,3600 = 设置缓存时间为3600秒
- * @return WeixinClient
- */
- public function cache($cache=true){
- $this->_cache = $cache;
- return $this;
- }
- public function debug($debug=true){
- $this->_debug = $debug;
- return $this;
- }
- /**
- * 写入或者获取缓存
- *
- * @param string $cache_id 缓存id
- * @param string $cache_data 缓存数据
- * @param int $cache_expire 缓存时间
- * @return mixed|boolean
- */
- protected function _cache($cache_id, $cache_data=NULL, $cache_expire=NULL){
- if($this->Config->Cache){
- // 保存缓存索引
- if($cache_id && (!is_null($cache_data) && $cache_data!==false && $cache_expire!==false)
- && $this->Config->CacheSaveIndex
- && strcasecmp($cache_id, $this->Config->CacheSaveIndex)
- ){
- $index_cache_id = $this->Config->CacheSaveIndex;
- $index_cache_expire = 315360000; // 永久保存: 3600*24*365*10
- // 取已有的缓存
- if(!($index_cache_data=$this->_cache($index_cache_id))){
- $index_cache_data = array();
- }
- // 删除已过期索引
- $now_time = time();
- foreach($index_cache_data as $k=>$d){
- if($d && $d['expire'] && $d['created'] && ($d['created']+$d['expire'])<$now_time){
- unset($index_cache_data[$k]);
- }
- }
- $index_cache_data[$cache_id] = array(
- 'created' => $now_time,
- 'expire' => $cache_expire,
- );
- //S4Web::debug_log("\$index_cache_id=$index_cache_id");
- //S4Web::debug_log("\$index_cache_data=" . print_r($index_cache_data, true));
- $succ = $this->_cache($index_cache_id, $index_cache_data, $index_cache_expire);
- $this->_log("Save cache id: " . $cache_id . ' ' . ($succ?'Succ':'Failed') . '!', WXAPI_LOG_DEBUG);
- }
- return $this->_run_callback($this->Config->Cache, array($cache_id, $cache_data, $cache_expire));
- }else{
- return false;
- }
- }
- protected function _cache_id($url, $data = NULL, $cache = NULL) {
- if ($cache && $cache!==true && !is_numeric($cache)){
- if(is_string ( $cache )) {
- $cache_id = $cache;
- } elseif (is_array ( $cache ) && isset($cache['cache_id'])) {
- $cache_id = $cache ['cache_id'];
- } elseif (is_object ( $cache ) && isset($cache['cache_id'])) {
- $cache_id = $cache->cache_id;
- }
- // 添加缓存前缀
- /*
- 注:由ThinkPHP处理缓存添加前缀:C('DATA_CACHE_PREFIX')
- if($cache_id && $this->Config->CacheBin){
- $cache_id = $this->Config->CacheBin . $cache_id;
- }*/
- }
- if (!$cache_id) {
- $param = '';
- if ($data && is_array ( $data )) {
- $param .= http_build_query ( $data );
- } else {
- $param .= $data;
- }
- $cache_id = md5 ( $this->Config->AppId . $url . $param );
- //return $cache_id;
- }
- return $cache_id;
- }
- protected function _cache_expire($url, $data=NULL, $cache=NULL){
- if(!$cache){
- return 0;
- }elseif(is_numeric($cache) && $cache>0){
- $cache_expire = $cache;
- }elseif (is_array($cache) && isset($cache['cache_expire'])){
- $cache_expire = $cache['cache_expire'];
- }elseif (is_object($cache) && isset($cache->cache_expire)){
- $cache_expire = $cache->cache_expire;
- }
- return $cache_expire?$cache_expire:$this->Config->CacheExpire;
- }
- /**
- * 判断是否强制刷新缓存
- * @param unknown $url
- * @param string $data
- * @param string $cache
- * @return bool
- */
- protected function _cache_refresh($url, $data=NULL, $cache=NULL){
- $cache_refresh = false;
- if ($cache && $cache!==true && !is_numeric($cache)){
- if (is_array ( $cache ) && isset($cache['cache_refresh'])) {
- $cache_refresh = $cache ['cache_refresh'];
- } elseif (is_object ( $cache ) && isset($cache['cache_refresh'])) {
- $cache_refresh = $cache->cache_refresh;
- }
- }
- return $cache_refresh;
- }
- /**
- * 写入日志
- * @param string $message
- * @param string $level
- * @return boolean
- */
- protected function _log($message, $level=WXAPI_LOG_INFO){
- if($this->Config->Log){
- static $aLogLevelMaps = array(
- WXAPI_LOG_EMERG => 0,
- WXAPI_LOG_ALERT => 1,
- WXAPI_LOG_CRIT => 2,
- WXAPI_LOG_ERR => 3,
- WXAPI_LOG_WARN => 4,
- WXAPI_LOG_NOTICE => 5,
- WXAPI_LOG_INFO => 6,
- WXAPI_LOG_DEBUG => 7,
- );
- if($this->Config->LogLevel && $aLogLevelMaps[$level]>$aLogLevelMaps[$this->Config->LogLevel]){
- return false;
- }
- return $this->_run_callback($this->Config->Log, array($message, $level));
- }else{
- return false;
- }
- }
- /**
- * 写入支付日志
- * @param string $message
- * @param string $level
- * @return boolean
- */
- protected function _logpay($message, $level=WXAPI_LOG_INFO){
- if($this->Config->PayLog){
- static $aLogLevelMaps = array(
- WXAPI_LOG_EMERG => 0,
- WXAPI_LOG_ALERT => 1,
- WXAPI_LOG_CRIT => 2,
- WXAPI_LOG_ERR => 3,
- WXAPI_LOG_WARN => 4,
- WXAPI_LOG_NOTICE => 5,
- WXAPI_LOG_INFO => 6,
- WXAPI_LOG_DEBUG => 7,
- );
- if($this->Config->PayLogLevel && $aLogLevelMaps[$level]>$aLogLevelMaps[$this->Config->PayLogLevel]){
- return false;
- }
- return $this->_run_callback($this->Config->PayLog, array($message, $level));
- }else{
- return false;
- }
- }
- /**
- * 判断是否微信媒体文件id
- * @param string $mediaid
- * @return boolean
- */
- protected function _isMediaId($mediaid){
- // aSeyL8Ym_0mu3u1qeHixvCe54XU-b8teahDXHdYl1tOB_1mgyUxJgj0A8CJZRNzl
- //return is_file($mediaid)?false:true;
- if(preg_match('/\.[a-z0-9]{1,4}$/i', $mediaid)){
- return false;
- }else{
- return true;
- }
- }
- /**
- * 清空微信API所有缓存数据
- *
- * @return bool
- */
- public function clearCache(){
- $this->_log("START Clear Cache...", WXAPI_LOG_INFO);
- if(!$this->Config->Cache || !$this->Config->CacheSaveIndex){
- $this->_log("Skipped, Cache or Save Cache index is disabled!", WXAPI_LOG_INFO);
- return false;
- }
- // 取缓存的索引
- $index_cache_id = $this->Config->CacheSaveIndex;
- if(!($index_cache_data=$this->_cache($index_cache_id))){
- $this->_log("Skipped, Cache Index is Empty!", WXAPI_LOG_INFO);
- return false;
- }
- $clear_succ = true;
- foreach($index_cache_data as $cache_id=>$d){
- $succ = $this->_cache($cache_id, false, false);
- $this->_log("Delete cache id: " . $cache_id . " " . ($succ?'Succ':'Failed') . '!', WXAPI_LOG_DEBUG);
- $clear_succ = $succ && $clear_succ;
- }
- // 删除索引自身
- $succ = $this->_cache($index_cache_id, false, false);
- $clear_succ = $succ && $clear_succ;
- $this->_log("Delete Index Cache Id: " . $index_cache_id . " " . ($succ?'Succ':'Failed') . '!', WXAPI_LOG_INFO);
- $this->_log("END Clear Cache, " . ($clear_succ?'Succ':'Failed') . '!', WXAPI_LOG_INFO);
- return $clear_succ;
- }
- }
WeixinReceive.class.php 微信接口接收类
- <?php
- /**
- * 微信API 接收接口
- *
- * PHP version 5
- *
- * @category Lib
- * @package COM
- * @subpackage GZNC
- * @author zhongyiwen
- * @version SVN: $Id: WeixinReceive.class.php 10 2013-10-08 01:34:05Z zhongyw $
- */
- class WeixinReceive extends WeixinApi{
- protected $_rawget = NULL;
- protected $_rawpost = NULL;
- protected $_postData = NULL;
- protected $_getData = NULL;
- protected $_postObj = NULL; // 兼容旧程序
- protected $_getObj = NULL; // 兼容旧程序
- protected $_responseMsg;
- protected $_responseObj;
- /**
- * 消息体加密模式
- * @var string
- */
- protected $_msgEncodingMode = NULL;
- /**
- * 消息加密私钥
- * @var string
- */
- protected $_msgEncodingKey = NULL;
- /**
- * 原始加密消息
- * @var string
- */
- protected $_msgEncrypt = NULL;
- /**
- * 解密后的消息原文
- * @var string
- */
- protected $_msgDecrypt = NULL;
- /**
- * 解密后的消息数组
- * @var array
- */
- protected $_msgData = NULL;
- /**
- * 检查消息签名
- * @param object $getObj
- * @return boolean 成功返回true,失败返回false
- */
- protected function _checkSignature($getData)
- {
- $signature = $getData['signature'];
- $timestamp = $getData['timestamp'];
- $nonce = $getData['nonce'];
- $token = $this->Config->AppToken;
- $tmpArr = array($token, $timestamp, $nonce);
- sort($tmpArr, SORT_STRING);
- $tmpStr = implode( $tmpArr );
- $tmpStr = sha1( $tmpStr );
- if( $tmpStr == $signature ){
- return true;
- }else{
- return false;
- }
- }
- /**
- * 判断消息加密模式
- * @param object $getObj
- * @param object $postObj
- * @return string|false
- */
- protected function _checkEncodingMode($getData, $postData){
- if(!is_null($this->_msgEncodingMode)){
- return $this->_msgEncodingMode;
- }
- if(empty($getData['encrypt_type']) || !strcasecmp($getData['encrypt_type'], 'raw')){
- $this->_msgEncodingMode = WXAPI_APP_ENCODING_CLEAR;
- }elseif(strlen($getData['msg_signature']) && !strcasecmp($getData['encrypt_type'], 'aes')){
- if(!empty($postData['MsgType']) && !empty($postData['FromUserName'])){
- $this->_msgEncodingMode = WXAPI_APP_ENCODING_COMPAT;
- }else{
- $this->_msgEncodingMode = WXAPI_APP_ENCODING_SECURE;
- }
- }else{
- $this->_msgEncodingMode = false;
- }
- return $this->_msgEncodingMode;
- }
- protected function _postData(){
- if(!is_null($this->_postData)){
- return $this->_postData;
- }
- $this->_rawpost = file_get_contents("php://input");
- if(!empty($this->_rawpost)){
- $postObj = simplexml_load_string(trim($this->_rawpost), 'SimpleXMLElement', LIBXML_NOCDATA);
- $this->_postData = WeixinApi_Kit::get_object_vars_final($postObj);
- // 2015.3.3 zhongyw 必须从postData转为object
- // simplexml_load_string()返回的为SimpleXMLElement Object,而不是stdClass Object,
- // 用is_string($postObj->FromUserName)判断时会返回false
- $this->_postObj = (object) $this->_postData; // 兼容旧程序
- }else{
- $this->_postData = false;
- $this->_postObj = false;
- }
- return $this->_postData;
- }
- protected function _getData(){
- if(!is_null($this->_getData)){
- return $this->_getData;
- }
- $this->_rawget = $_GET;
- if ($this->_rawget) {
- $getData = array (
- 'signature' => $_GET ["signature"],
- 'timestamp' => $_GET ["timestamp"],
- 'nonce' => $_GET ["nonce"]
- );
- if (isset ( $_GET ['echostr'] )) { $getData ['echostr'] = $_GET ['echostr']; }
- if (isset ( $_GET ['encrypt_type'] )) { $getData ['encrypt_type'] = $_GET ['encrypt_type']; }
- if (isset ( $_GET ['msg_signature'] )) { $getData ['msg_signature'] = $_GET ['msg_signature']; }
- $this->_getData = $getData;
- // 兼容旧程序
- $this->_getObj = ( object ) $getData;
- }else{
- $this->_getData = false;
- $this->_getObj = false;
- }
- return $this->_getData;
- }
- /**
- * 运行接收
- * @param mixed $responseObj 响应对象,可以传回调函数
- */
- public function run($responseObj=NULL){
- $request_url = WeixinApi_Kit::get_request_url();
- $client_ip = WeixinApi_Kit::get_client_ip();
- $this->_log("--------------------------------------------------------");
- $this->_log("Received new request from {$client_ip}", WXAPI_LOG_INFO);
- $this->_log("Request URL: {$request_url}", WXAPI_LOG_INFO);
- $this->_log("Get: " . print_r($_GET, true), WXAPI_LOG_DEBUG);
- $this->_log("Post: " . print_r($_POST, true), WXAPI_LOG_DEBUG);
- $getData = $this->_getData();
- // 验证签名
- if(!$getData || !$this->_checkSignature($getData)){
- // invalid request
- // log it? or do other things
- $this->_log("Bad Request, Check Signature Failed!", WXAPI_LOG_ERR);
- return false;
- }
- $postData = $this->_postData();
- // 消息体是否为空?
- if(false==$postData){
- $this->_log("Msg Body is Empty!", WXAPI_LOG_ERR);
- return false;
- }
- $this->_log ( "rawPost: " . $this->_rawpost, WXAPI_LOG_DEBUG );
- $this->_log ( "postData: " . print_r ( $postData, true ), WXAPI_LOG_DEBUG );
- // 判断消息加密模式
- $encodingMode = $this->_checkEncodingMode($getData, $postData);
- if(false==$encodingMode){
- $this->_log("Check Msg Encoding Mode Failed!", WXAPI_LOG_ERR);
- return false;
- }
- $this->_log("MSG Encoding Mode is: " . $encodingMode, WXAPI_LOG_DEBUG);
- // 解密消息
- switch($encodingMode){
- case WXAPI_APP_ENCODING_SECURE:
- if(false===$this->_decodeMessage()){
- $this->_log("Bad Request, Decode Message Failed!", WXAPI_LOG_ERR);
- return false;
- }else{
- $this->_log("Decode Message Succ!", WXAPI_LOG_INFO);
- }
- break;
- case WXAPI_APP_ENCODING_COMPAT:
- if(false===$this->_decodeMessage()){
- $this->_log("Decode Message Failed!", WXAPI_LOG_ERR);
- }else{
- $this->_log("Decode Message Succ!", WXAPI_LOG_INFO);
- }
- break;
- default:
- // DO NOTHING
- break;
- }
- if (empty ( $responseObj )) {
- $responseObj = $this->Config->Response;
- }
- // get response
- $response = $this->_responseMsg = $this->_response ( $responseObj );
- if ($response === false) {
- $this->_log ( "No Reponse Sent!", WXAPI_LOG_INFO );
- // save message
- $this->_saveMessage ();
- return false;
- }
- // echo response
- echo $response;
- flush ();
- // log
- $this->_log ( "Succ! Send Response: " . $response, WXAPI_LOG_INFO );
- // save message
- $this->_saveMessage ();
- // save response
- $this->_saveResponse ( $this->_responseObj );
- return true;
- }
- protected function _response($responseObj){
- if(is_object($responseObj)){
- $callback = array($responseObj, 'run');
- }else{
- $callback = $responseObj;
- }
- return $this->_run_callback($callback, array($this), $this->_responseObj);
- }
- /**
- * 保存消息
- * @return mixed|boolean
- */
- protected function _saveMessage(){
- if($this->Config->SaveMessage){
- return $this->_run_callback($this->Config->SaveMessage, array($this));
- }else{
- return false;
- }
- }
- /**
- * 保存回复
- * @param mixed $responseObj
- * @return mixed|boolean
- */
- protected function _saveResponse($responseObj){
- if($this->Config->SaveResponse){
- return $this->_run_callback($this->Config->SaveResponse, array($this, $responseObj));
- }else{
- return false;
- }
- }
- public function __get($c){
- if(substr($c, 0, 1)!='_'){
- if(in_array($c, array('Http'))){
- return parent::__get($c);
- }else{
- $n = '_' . $c;
- return $this->$n;
- }
- }
- }
- public function __isset($c){
- if(substr($c, 0, 1)!='_'){
- if(in_array($c, array('Http'))){
- return parent::__isset($c);
- }else{
- $n = '_' . $c;
- return isset($this->$n);
- }
- }
- }
- /**
- * 获取openid
- * @return string
- */
- public function parse_openid(){
- if(isset($this->_postObj->FromUserName) && !empty($this->_postObj->FromUserName)){
- return $this->_postObj->FromUserName;
- }else{
- return null;
- }
- }
- /**
- * 对密文消息进行解密
- * @param string $msg_encrypt 需要解密的密文
- * @param string $encodingkey 加密私钥
- * @return string|false 解密得到的明文,失败返回flase
- */
- protected function _decryptMsg($msg_encrypt, $encodingkey=NULL)
- {
- $AESKey = base64_decode(($encodingkey?$encodingkey:$this->Config->AppEncodingAESKey) . "=");
- //使用BASE64对需要解密的字符串进行解码
- $ciphertext_dec = base64_decode($msg_encrypt);
- $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
- if(false===$module){
- return $this->_throw_exception(
- "Cann't open an encryption descriptor"
- , WXAPI_ERR_BAD_ENCRYPT
- , $msg_encrypt
- , __FILE__, __LINE__
- );
- }
- $iv = substr($AESKey, 0, 16);
- $init = mcrypt_generic_init($module, $AESKey, $iv);
- if(false===$init){
- return $this->_throw_exception(
- "Cann't initialize buffers for encryption"
- , WXAPI_ERR_BAD_ENCRYPT
- , array('msg_encrypt' => $msg_encrypt, 'mcrypt_generic_init return' => $init)
- , __FILE__, __LINE__
- );
- }elseif(-3==$init){
- return $this->_throw_exception(
- "the key length was incorrect"
- , WXAPI_ERR_BAD_ENCRYPT
- , array('msg_encrypt' => $msg_encrypt, 'mcrypt_generic_init return' => $init)
- , __FILE__, __LINE__
- );
- }elseif(-4==$init){
- return $this->_throw_exception(
- "there was a memory allocation problem"
- , WXAPI_ERR_BAD_ENCRYPT
- , array('msg_encrypt' => $msg_encrypt, 'mcrypt_generic_init return' => $init)
- , __FILE__, __LINE__
- );
- }elseif($init<0){
- return $this->_throw_exception(
- "an unknown error occurred when initialize buffers for encryption"
- , WXAPI_ERR_BAD_ENCRYPT
- , array('msg_encrypt' => $msg_encrypt, 'mcrypt_generic_init return' => $init)
- , __FILE__, __LINE__
- );
- }
- //解密
- $decrypted = mdecrypt_generic($module, $ciphertext_dec);
- mcrypt_generic_deinit($module);
- mcrypt_module_close($module);
- if(!$decrypted){
- return "";
- }
- // 去除补位字符
- $result = WeixinApi_Kit::pkcs7_decode( $decrypted, 32 );
- // 去除16位随机字符串,网络字节序和AppId
- if (strlen ( $result ) < 16){
- return "";
- }
- $content = substr ( $result, 16, strlen ( $result ) );
- $len_list = unpack ( "N", substr ( $content, 0, 4 ) );
- $xml_len = $len_list [1];
- $xml_content = substr ( $content, 4, $xml_len );
- return $xml_content;
- }
- /**
- * 返回微信发过来的加密消息
- * @return string
- */
- protected function _getMsgEncrypt(){
- if($this->_msgEncrypt){
- return $this->_msgEncrypt;
- }
- if(!empty($this->_getData['echostr'])){
- $this->_msgEncrypt = $this->_getData['echostr'];
- }else{
- $this->_msgEncrypt = $this->_postData['Encrypt'];
- }
- return $this->_msgEncrypt;
- }
- protected function _msgData() {
- if (! is_null ( $this->_msgData )) {
- return $this->_msgData;
- }
- $this->_msgData = false;
- $msg_encrypt = $this->_getMsgEncrypt ();
- if ($msg_encrypt) {
- if(!empty($this->_getData['echostr'])){
- $this->_msgData = array();
- }else{
- $xml_content = false;
- $encodingkey = $this->Config->AppEncodingAESKey;
- if($encodingkey){
- $xml_content = $this->_decryptMsg ( $msg_encrypt, $encodingkey );
- if($xml_content){
- $this->_msgEncodingKey = $encodingkey;
- $this->_log("AES Key: Decode Succ! ", WXAPI_LOG_DEBUG);
- }else{
- $this->_log("AES Key: Decode Failed!", WXAPI_LOG_DEBUG);
- }
- }else{
- $this->_log("Encoding AES Key is empty", WXAPI_LOG_DEBUG);
- }
- // 尝试旧密钥
- if(!$xml_content && ($encodingkey = $this->Config->AppEncodingOLDKey)){
- $xml_content = $this->_decryptMsg ( $msg_encrypt, $encodingkey );
- $this->_log("Try to apply OLD Key", WXAPI_LOG_DEBUG);
- if($xml_content){
- $this->_msgEncodingKey = $encodingkey;
- $this->_log("OLD Key: Decode Succ! ", WXAPI_LOG_DEBUG);
- }else{
- $this->_log("OLD Key: Decode Failed!", WXAPI_LOG_DEBUG);
- }
- }
- if($xml_content){
- $this->_msgDecrypt = $xml_content;
- import ( 'COM.GZNC.WeixinApi.WeixinApi_Kit' );
- $postObj = simplexml_load_string ( $xml_content, 'SimpleXMLElement', LIBXML_NOCDATA );
- $this->_msgData = WeixinApi_Kit::get_object_vars_final ( $postObj );
- $this->_log('Decoded MSG XML: ' . $this->_msgDecrypt, WXAPI_LOG_DEBUG);
- $this->_log('Decoded MSG DATA: ' . print_r($this->_msgData, true), WXAPI_LOG_DEBUG);
- }
- }
- }
- return $this->_msgData;
- }
- protected function _decodeMessage(){
- if(false===$this->_msgData()){
- return false;
- }
- // 兼容旧程序
- $this->_postData = array_merge($this->_postData, $this->_msgData);
- $this->_postObj = (object) $this->_postData;
- return true;
- }
- }
WeixinResponse.class.php 微信接口响应类
- <?php
- /**
- * 微信API 响应接口
- *
- * PHP version 5
- *
- * @category Lib
- * @package COM
- * @subpackage GZNC
- * @author zhongyiwen
- * @version SVN: $Id: WeixinResponse.class.php 10 2013-10-08 01:34:05Z zhongyw $
- */
- class WeixinResponse extends WeixinApi{
- const AS_ECHO = 'ECHO'; // ECHO消息
- const AS_EMPTY = 'EMPTY'; // 空消息
- const AS_COMMAND = 'COMMAND'; // 指令消息
- const AS_SUBSCRIBE = 'SUBSCRIBE'; // 订阅消息
- const AS_UNSUBSCRIBE = 'UNSUBSCRIBE'; // 取消订阅消息
- const AS_SCAN = 'SCAN'; // 扫描消息
- const AS_CLICK = 'CLICK'; // 点击菜单拉取消息事件
- const AS_VIEW = 'VIEW'; // 点击菜单跳转链接事件
- const AS_SCANCODE_PUSH = 'SCANCODE_PUSH'; // 扫码推事件
- const AS_SCANCODE_WAITMSG = 'AS_SCANCODE_WAITMSG'; // 扫码推事件且弹出“消息接收中”提示框
- const AS_PIC_SYSPHOTO = 'pic_sysphoto'; // 弹出系统拍照发图
- const AS_PIC_PHOTO_OR_ALBUM = 'PIC_PHOTO_OR_ALBUM'; // 弹出拍照或者相册发图
- const AS_PIC_WEIXIN = 'pic_weixin'; // 弹出微信相册发图器
- const AS_LOCATION_SELECT = 'location_select'; // 弹出地理位置选择器
- const AS_LOCATION = 'LOCATION'; // 地理位置消息
- const AS_MESSAGE = 'MESSAGE'; // 普通消息
- const AS_MASSSENDJOBFINISH = 'MASSSENDJOBFINISH'; // 群发消息
- const AS_TEMPLATESENDJOBFINISH = 'TEMPLATESENDJOBFINISH'; // 模板消息
- const AS_UNKNOWN = 'UNKNOWN'; // 未知消息
- protected $_responseType; // 响应类型,对应上面的常量
- protected $_responseKey; // 响应值,如菜单点击,值为菜单Key
- protected $_responseContent; // 响应的原始数据
- protected $_responseMessage; // 响应输出消息数据(数组)
- protected $_responseMedia; // 响应输出的媒体文件
- /**
- * 运行
- * @param object $receiveObj 接收对象
- */
- public function run($receiveObj){
- try{
- return $this->_dispatchResponse($receiveObj);
- }catch (Exception $e){
- return false;
- }
- }
- /**
- * 分发响应
- * 判断响应类型,并返回相应的响应内容
- * @param object $receiveObj 接收对象
- */
- protected function _dispatchResponse($receiveObj){
- // 可以直接判定消息类别,不需要依赖外部配置数据
- if($this->_isEcho($receiveObj)){
- $this->_responseType = WeixinResponse::AS_ECHO;
- $this->_responseKey = '';
- $this->_responseContent = $this->_responseEcho($receiveObj);
- $msgFormat = 'raw';
- }elseif($this->_isEmpty($receiveObj)){
- $this->_responseType = WeixinResponse::AS_EMPTY;
- $this->_responseKey = '';
- $this->_responseContent = $this->_responseEmpty($receiveObj);
- $msgFormat = 'raw';
- }elseif(($key=$this->_isView($receiveObj))){
- $this->_responseType = WeixinResponse::AS_VIEW;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseView($receiveObj);
- $msgFormat = 'xml';
- }
- // 事件,需要加载外部配置数据
- // 注意:要先判断订阅事件,避免缓存误判
- elseif(($key=$this->_isSubscribe($receiveObj))){
- $this->_responseType = WeixinResponse::AS_SUBSCRIBE;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseSubscribe($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isUnsubscribe($receiveObj))){
- $this->_responseType = WeixinResponse::AS_UNSUBSCRIBE;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseUnsubscribe($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isClick($receiveObj))){
- $this->_responseType = WeixinResponse::AS_CLICK;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseClick($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isScanCodePush($receiveObj))){
- $this->_responseType = WeixinResponse::AS_SCANCODE_PUSH;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseScancodePush($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isScanCodeWaitMsg($receiveObj))){
- $this->_responseType = WeixinResponse::AS_SCANCODE_WAITMSG;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseScanCodeWaitMsg($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isPicSysPhoto($receiveObj))){
- $this->_responseType = WeixinResponse::AS_PIC_SYSPHOTO;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responsePicSysPhoto($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isPicPhotoOrAlbum($receiveObj))){
- $this->_responseType = WeixinResponse::AS_PIC_PHOTO_OR_ALBUM;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responsePicPhotoOrAlbum($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isPicWeixin($receiveObj))){
- $this->_responseType = WeixinResponse::AS_PIC_WEIXIN;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responsePicWeixin($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isLocationSelect($receiveObj))){
- $this->_responseType = WeixinResponse::AS_LOCATION_SELECT;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseLocationSelect($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isScan($receiveObj))){
- $this->_responseType = WeixinResponse::AS_SCAN;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseScan($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isLocation($receiveObj))){
- $this->_responseType = WeixinResponse::AS_LOCATION;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseLocation($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isMassSendJobFinish($receiveObj))){
- $this->_responseType = WeixinResponse::AS_MASSSENDJOBFINISH;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseMassSendJobFinish($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isTemplateSendJobFinish($receiveObj))){
- $this->_responseType = WeixinResponse::AS_TEMPLATESENDJOBFINISH;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseTemplateSendJobFinish($receiveObj);
- $msgFormat = 'xml';
- }
- // 文本消息
- elseif(($key=$this->_isCommand($receiveObj))){
- $this->_responseType = WeixinResponse::AS_COMMAND;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseCommand($receiveObj);
- $msgFormat = 'xml';
- }elseif(($key=$this->_isMessage($receiveObj))){
- $this->_responseType = WeixinResponse::AS_MESSAGE;
- $this->_responseKey = $key===true?'':$key;
- $this->_responseContent = $this->_responseMessage($receiveObj);
- $msgFormat = 'xml';
- }
- // 未知,可能是新类型
- else{
- $this->_responseType = WeixinResponse::AS_UNKNOWN;
- $this->_responseKey = '';
- $this->_responseContent = $this->_responseUnknown($receiveObj);
- $msgFormat = 'raw';
- }
- if($this->_responseContent===false){
- // 出错:未配置对事件或消息的响应
- return false;
- }
- $this->_log("ResponseType: " . $this->_responseType, WXAPI_LOG_DEBUG);
- $this->_log("ResponseKey: " . $this->_responseKey, WXAPI_LOG_DEBUG);
- $this->_log("ResponseContent: " . print_r($this->_responseContent, true), WXAPI_LOG_DEBUG);
- $this->_responseMessage = $this->_createResponseMessage(
- $receiveObj,
- $this->_responseContent,
- $msgFormat
- );
- $this->_log("Generated Response Message: " . print_r($this->_responseMessage, true), WXAPI_LOG_DEBUG);
- return $this->_responseMessage['MsgContent'];
- }
- /**
- * 是否空消息
- * @param object $receiveObj 接收对象
- * @return boolean
- */
- protected function _isEmpty($receiveObj){
- // 注意:empty($receiveObj->postObj)即使非空也返回true
- // When using empty() on inaccessible object properties, the __isset() overloading method will be called, if declared.
- $postObj = $receiveObj->postObj;
- return empty($postObj)?true:false;
- }
- /**
- * 是否验证消息
- * @param object $receiveObj 接收对象
- * @return boolean
- */
- protected function _isEcho($receiveObj){
- return isset($receiveObj->getObj->echostr) && $receiveObj->getObj->echostr;
- }
- /**
- * 是否指令消息
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isCommand($receiveObj){
- $aMsgTypes = array(
- 'text'
- );
- if(!in_array($receiveObj->postObj->MsgType, $aMsgTypes, false)){
- return false;
- }
- $command = trim($receiveObj->postObj->Content);
- if(($c=$this->Config->Command) && !empty($c[$command])){
- return $command;
- }else{
- return false;
- }
- }
- /**
- * 是否事件消息
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isEvent($receiveObj){
- return !strcasecmp($receiveObj->postObj->MsgType, 'event');
- }
- /**
- * 是否订阅事件
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isSubscribe($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'subscribe')){
- return isset($receiveObj->postObj->EventKey) && ($key=(string)$receiveObj->postObj->EventKey)?
- $key:true;
- }else{
- return false;
- }
- }
- /**
- * 是否取消订阅事件
- * @param object $receiveObj 接收对象
- * @return boolean
- */
- protected function _isUnsubscribe($receiveObj){
- return $this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'unsubscribe');
- }
- /**
- * 是否扫描事件
- * @param object $receiveObj 接收对象
- * @return array|false
- */
- protected function _isScan($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'scan')){
- return array(
- 'EventKey' => $receiveObj->postObj->EventKey,
- 'Ticket' => $receiveObj->postObj->Ticket,
- );
- }else{
- return false;
- }
- }
- /**
- * 是否地理位置消息
- * @param object $receiveObj 接收对象
- * @return array|false
- */
- protected function _isLocation($receiveObj){
- if(!strcasecmp($receiveObj->postObj->MsgType, 'location')){
- return array(
- 'Latitude' => $receiveObj->postObj->Location_Y,
- 'Longitude' => $receiveObj->postObj->Location_X,
- 'Precision' => $receiveObj->postObj->Scale,
- 'Label' => $receiveObj->postObj->Label,
- );
- }elseif($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'LOCATION')){
- return array(
- 'Latitude' => $receiveObj->postObj->Latitude,
- 'Longitude' => $receiveObj->postObj->Longitude,
- 'Precision' => $receiveObj->postObj->Precision,
- 'Label' => '',
- );
- }else{
- return false;
- }
- }
- /**
- * 是否点击菜单拉取消息事件
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isClick($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'CLICK')){
- return $receiveObj->postObj->EventKey;
- }else{
- return false;
- }
- }
- /**
- * 是否点击菜单跳转链接事件
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isView($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'VIEW')){
- return $receiveObj->postObj->EventKey;
- }else{
- return false;
- }
- }
- /**
- * 是否:扫码推事件
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isScanCodePush($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'scancode_push')){
- return $receiveObj->postObj->EventKey;
- }else{
- return false;
- }
- }
- /**
- * 是否:扫码推事件且弹出“消息接收中”提示框
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isScanCodeWaitMsg($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'scancode_waitmsg')){
- return $receiveObj->postObj->EventKey;
- }else{
- return false;
- }
- }
- /**
- * 是否:弹出系统拍照发图
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isPicSysPhoto($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'pic_sysphoto')){
- return $receiveObj->postObj->EventKey;
- }else{
- return false;
- }
- }
- /**
- * 是否:弹出拍照或者相册发图
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isPicPhotoOrAlbum($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'pic_photo_or_album')){
- return $receiveObj->postObj->EventKey;
- }else{
- return false;
- }
- }
- /**
- * 是否:弹出微信相册发图器
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isPicWeixin($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'pic_weixin')){
- return $receiveObj->postObj->EventKey;
- }else{
- return false;
- }
- }
- /**
- * 是否:弹出地理位置选择器
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isLocationSelect($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'location_select')){
- return $receiveObj->postObj->EventKey;
- }else{
- return false;
- }
- }
- /**
- * 是否普通消息
- * @param object $receiveObj 接收对象
- * @return string|false
- */
- protected function _isMessage($receiveObj){
- $aMsgTypes = array(
- 'text', 'image', 'voice', 'video', 'link','shortvideo'
- );
- return in_array($receiveObj->postObj->MsgType, $aMsgTypes, false)?$receiveObj->postObj->MsgType:false;
- }
- /**
- * 是否群发消息通知事件
- * @param object $receiveObj 接收对象
- * @return string|false 返回消息id
- */
- protected function _isMassSendJobFinish($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'MASSSENDJOBFINISH')){
- return $receiveObj->postObj->MsgID;
- /*
- return array(
- 'MsgID' => $receiveObj->postObj->MsgID,
- 'Status' => $receiveObj->postObj->Status,
- 'TotalCount' => $receiveObj->postObj->TotalCount,
- 'FilterCount' => $receiveObj->postObj->FilterCount,
- 'SentCount' => $receiveObj->postObj->SentCount,
- 'ErrorCount' => $receiveObj->postObj->ErrorCount,
- );*/
- }else{
- return false;
- }
- }
- /**
- * 是否模板消息通知事件
- * @param object $receiveObj 接收对象
- * @return string|false 返回消息id
- */
- protected function _isTemplateSendJobFinish($receiveObj){
- if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'TEMPLATESENDJOBFINISH')){
- return $receiveObj->postObj->MsgID;
- }else{
- return false;
- }
- }
- /**
- * 创建响应消息
- * @param object $receiveObj 接收对象
- * @param mixed $responseContent 原始响应内容
- * @param string $msgFormat 消息格式
- * @return array|false
- */
- protected function _createResponseMessage($receiveObj, $responseContent, $msgFormat='xml'){
- if(is_array($responseContent) && !empty($responseContent['Callback'])){
- $data = $this->_run_callback($responseContent['Callback'], array($receiveObj, $this));
- if($data===false){
- $this->_log("Run Callback : " . print_r($responseContent['Callback'], true) . " Failed", WXAPI_LOG_ERR);
- return false;
- }
- if(is_array($data)){
- $t = $data;
- $responseContent = array(
- 'MsgType' => $t['MsgType'],
- 'Content' => $t['Content'],
- );
- }else{
- $responseContent['Content'] = $data;
- if($responseContent['MsgType']=='callback'){
- $responseContent['MsgType'] = 'text';
- }
- }
- }
- if(is_string($responseContent)){
- $responseContent = array(
- 'MsgType' => 'text',
- 'Content' => $responseContent,
- );
- }elseif(!$responseContent['MsgType']){
- $responseContent['MsgType'] = 'text';
- }
- if(!$responseContent['Content'] && !strlen($responseContent['Content'])
- && strcasecmp('transfer_customer_service', $responseContent['MsgType']) // 转发客服消息,允许Content为空
- ){
- return false;
- }
- // 预处理消息
- if($msgFormat=='xml'){
- $responseContent = $this->_preprocessResponseMedia($responseContent);
- }
- $msgContentOutput = self::generateMessage($receiveObj->postData['FromUserName'], $receiveObj->postData['ToUserName'], $responseContent);
- // 根据加密类型生成相应响应消息
- switch($receiveObj->msgEncodingMode){
- // 兼容模式
- case WXAPI_APP_ENCODING_COMPAT:
- // 未正确解密,使用明文返回
- if(empty($receiveObj->msgEncodingKey)){
- $msgEncoding = WXAPI_APP_ENCODING_CLEAR;
- $this->_log("Encoding Response Msg in Clear Mode", WXAPI_LOG_DEBUG);
- break;
- }
- // 安全模式
- case WXAPI_APP_ENCODING_SECURE:
- $msgContentOriginal = $msgContentOutput;
- $msgContentOutput = self::_encrypt_response($msgContentOutput, $receiveObj->msgEncodingKey);
- $msgEncoding = WXAPI_APP_ENCODING_SECURE;
- $this->_log("Encoding Response Msg in Secure Mode", WXAPI_LOG_DEBUG);
- break;
- // 明文模式
- case WXAPI_APP_ENCODING_CLEAR:
- default:
- $msgEncoding = WXAPI_APP_ENCODING_CLEAR;
- $this->_log("Encoding Response Msg In Clear Mode", WXAPI_LOG_DEBUG);
- break;
- }
- return array(
- 'MsgType' => $responseContent['MsgType'],
- 'MsgFormat' => $msgFormat,
- 'MsgContent' => $msgFormat=='xml'?$msgContentOutput: $responseContent['Content'],
- 'MsgOriginal' => $msgContentOriginal?$msgContentOriginal:NULL,
- 'MsgEncoding' => $msgEncoding,
- 'RawContent' => $responseContent['Content']
- );
- }
- /**
- * 错误响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseError($receiveObj){
- return "Error";
- }
- /**
- * 未知响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseUnknown($receiveObj){
- //return "Unknown";
- }
- /**
- * 验证响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseEcho($receiveObj){
- return $receiveObj->getObj->echostr;
- }
- /**
- * 空消息响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseEmpty($receiveObj){
- return "Empty";
- }
- /**
- * 指令响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseCommand($receiveObj){
- $command = trim($receiveObj->postObj->Content);
- $settings = $this->Config->getConfig('Command');
- if(!isset($settings[$command])){
- return $this->_throw_exception("Command {$command} not configured", WXAPI_ERR_MISS_RESPONSE, '', __FILE__, __LINE__);
- }
- $content = $settings[$command];
- return $content;
- }
- /**
- * 事件响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseEvent($receiveObj){
- $event = strtolower($receiveObj->postObj->Event);
- $settings = $this->Config->getConfig('Event');
- if(!isset($settings[$event])){
- return $this->_throw_exception("Miss resoponse for Event {$event}", WXAPI_ERR_MISS_RESPONSE, '', __FILE__, __LINE__);
- }
- if(isset($settings[$event]['MsgType'])){
- $content = $settings[$event];
- }elseif(isset($receiveObj->postObj->EventKey)){
- $eventkey = (string) $receiveObj->postObj->EventKey;
- if(!isset($settings[$event][$eventkey])){
- return $this->_throw_exception("Miss response for Event {$event}, Key {$eventkey}", WXAPI_ERR_MISS_RESPONSE, '', __FILE__, __LINE__);
- }
- $content = $settings[$event][$eventkey];
- }
- return $content;
- }
- /**
- * 订阅事件响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseSubscribe($receiveObj){
- return $this->_responseEvent($receiveObj);
- }
- /**
- * 取消订阅事件响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseUnsubscribe($receiveObj){
- return $this->_responseEvent($receiveObj);
- }
- /**
- * 扫描事件响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseScan($receiveObj){
- return $this->_responseEvent($receiveObj);
- }
- /**
- * 地理位置消息响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseLocation($receiveObj){
- if(!strcasecmp($receiveObj->postObj->MsgType, 'event')){
- return $this->_responseEvent($receiveObj);
- }else if(!strcasecmp($receiveObj->postObj->MsgType, 'location')){
- // 普通位置消息
- // @todo 处理接收到的普通位置消息
- }
- }
- /**
- * 点击菜单拉取消息事件响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseClick($receiveObj){
- return $this->_responseEvent($receiveObj);
- }
- /**
- * 点击菜单跳转链接事件响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseView($receiveObj){
- //return $this->_responseEvent($receiveObj);
- }
- /**
- * 响应:扫码推事件
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseScanCodePush($receiveObj){
- return $this->_responseEvent($receiveObj);
- }
- /**
- * 响应:扫码推事件且弹出“消息接收中”提示框
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseScanCodeWaitMsg($receiveObj){
- return $this->_responseEvent($receiveObj);
- }
- /**
- * 响应:弹出系统拍照发图
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responsePicSysPhoto($receiveObj){
- return $this->_responseEvent($receiveObj);
- }
- /**
- * 响应:弹出拍照或者相册发图
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responsePicPhotoOrAlbum($receiveObj){
- return $this->_responseEvent($receiveObj);
- }
- /**
- * 响应:弹出微信相册发图器
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responsePicWeixin($receiveObj){
- return $this->_responseEvent($receiveObj);
- }
- /**
- * 响应:弹出地理位置选择器
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseLocationSelect($receiveObj){
- return $this->_responseEvent($receiveObj);
- }
- /**
- * 普通消息响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseMessage($receiveObj){
- // write your code, such as save message and remind customer service
- // 通关密语
- if(($msg=$this->_responseArgot($receiveObj))!==false){
- return $msg;
- }
- // 转发客服消息到微信多客服系统
- elseif($this->Config->TransferCustomerService){
- return array(
- 'MsgType' => 'transfer_customer_service',
- );
- }
- }
- /**
- * 响应暗语
- *
- * @param object $receiveObj
- * @return false|string 返回false表示非暗语处理,可以由其它逻辑处理
- */
- protected function _responseArgot($receiveObj){
- if(!isset($receiveObj->postObj->Content) || !($msg=$receiveObj->postObj->Content)){
- return false;
- }
- $msg = trim($msg);
- if(defined('WXAPI_ARGOT_WHO_AM_I') && WXAPI_ARGOT_WHO_AM_I && !strcasecmp(WXAPI_ARGOT_WHO_AM_I, $msg)){
- return "OH LORD,\nMY 4susername is " . $this->Config->AppName . ",\nAppId: " . $this->Config->AppId . ",\n Server: " . $_SERVER['HTTP_HOST'] ." .";
- }
- elseif(defined('WXAPI_ARGOT_DESTORY_SESSION') && WXAPI_ARGOT_DESTORY_SESSION && !strcasecmp(WXAPI_ARGOT_DESTORY_SESSION, $msg)){
- $openid = $receiveObj->parse_openid();
- if($openid && class_exists('WeixinUserModel') && method_exists('WeixinUserModel', 'destroy_session')){
- $oWeixinUserModel = new WeixinUserModel();
- $succ = $oWeixinUserModel->destroy_session($openid);
- return $succ?"Your session has been destoryed Successfully!":"Failed destroy your session!";
- }else{
- return false;
- }
- }
- else{
- return false;
- }
- }
- /**
- * 群发消息通知响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseMassSendJobFinish($receiveObj){
- // write your code, such as save message and remind customer service
- }
- /**
- * 模板消息通知响应
- * @param object $receiveObj 接收对象
- * @return string
- */
- protected function _responseTemplateSendJobFinish($receiveObj){
- // write your code, such as save message and remind customer service
- }
- /**
- * 预处理多媒体文件
- * 可以根据消息类型,调用微信接口,将消息中的图片、音频等多媒体文件上传到微信服务器,
- * 得到MediaId,并替换掉原来的多媒体文件
- * @param mixed $Content
- * @return mixed
- */
- protected function _preprocessResponseMedia($Content){
- if(!is_array($Content) || empty($Content['MsgType'])){
- return $Content;
- }
- $msgtype = strtolower($Content['MsgType']);
- switch ($msgtype){
- case 'image':
- $mediaField = 'MediaId';
- if(is_string($Content['Content']) && !$this->_isMediaId($Content['Content'])){
- $mediaFile = $Content['Content'];
- $oClient = WeixinApi::instance($this->Config->Client, $this->Config);
- $mediaId = $oClient->upload_media($mediaFile, 'image');
- $Content['Content'] = $mediaId;
- }else{
- $mediaId = $Content['Content'];
- $mediaFile = '';
- }
- $this->_responseMedia[$mediaField] = array($mediaId, 'image', $mediaFile);
- break;
- case 'voice':
- $mediaField = 'MediaId';
- if(is_string($Content['Content']) && !$this->_isMediaId($Content['Content'])){
- $mediaFile = $Content['Content'];
- $oClient = WeixinApi::instance($this->Config->Client, $this->Config);
- $mediaId = $oClient->upload_media($mediaFile, 'voice');
- $Content['Content'] = $mediaId;
- }else{
- $mediaId = $Content['Content'];
- $mediaFile = '';
- }
- $this->_responseMedia[$mediaField] = array($mediaId, 'voice', $mediaFile);
- break;
- case 'video':
- $mediaField = 'MediaId';
- if(!$this->_isMediaId($Content['Content']['MediaId'])){
- $mediaFile = $Content['Content']['MediaId'];
- $mediaField = 'MediaId';
- $oClient = WeixinApi::instance($this->Config->Client, $this->Config);
- $mediaId = $oClient->upload_media($mediaFile, 'video');
- $Content['Content']['MediaId'] = $mediaId;
- }else{
- $mediaId = $Content['Content']['MediaId'];
- $mediaFile = '';
- }
- $this->_responseMedia[$mediaField] = array($mediaId, 'video', $mediaFile);
- $thumbMediaField = 'ThumbMediaId';
- if(!$this->_isMediaId($Content['Content']['ThumbMediaId'])){
- $thumbMediaFile = $Content['Content']['ThumbMediaId'];
- $oClient = WeixinApi::instance($this->Config->Client, $this->Config);
- $thumbMediaId = $oClient->upload_media($thumbMediaFile, 'thumb');
- $Content['Content']['ThumbMediaId'] = $thumbMediaId;
- }else{
- $thumbMediaId = $Content['Content']['ThumbMediaId'];
- $thumbMediaFile = '';
- }
- $this->_responseMedia[$thumbMediaField] = array($thumbMediaId, 'thumb', $thumbMediaFile);
- break;
- case 'music':
- $thumbMediaField = 'ThumbMediaId';
- if(is_array($Content['Content'])){
- if(!$this->_isMediaId($Content['Content']['ThumbMediaId'])){
- $thumbMediaFile = $Content['Content']['ThumbMediaId'];
- $oClient = WeixinApi::instance($this->Config->Client, $this->Config);
- $thumbMediaId = $oClient->upload_media($thumbMediaFile, 'thumb');
- $Content['Content']['ThumbMediaId'] = $thumbMediaId;
- }else{
- $thumbMediaId = $Content['Content']['ThumbMediaId'];
- $thumbMediaFile = '';
- }
- }else{
- if(!$this->_isMediaId($Content['Content'])){
- $thumbMediaFile = $Content['Content'];
- $oClient = WeixinApi::instance($this->Config->Client, $this->Config);
- $thumbMediaId = $oClient->upload_media($thumbMediaFile, 'thumb');
- $Content['Content'] = $thumbMediaId;
- }else{
- $thumbMediaId = $Content['Content'];
- $thumbMediaFile = '';
- }
- }
- $this->_responseMedia[$thumbMediaField] = array($thumbMediaId, 'thumb', $thumbMediaFile);
- break;
- default:
- // other type, do nothing
- }
- return $Content;
- }
- public function createMessage($FromUserName, $ToUserName, $Content){
- return self::generateMessage($FromUserName, $ToUserName, $Content);
- }
- /**
- * 根据消息类型,创建消息
- * @param string $FromUserName 发送者
- * @param string $ToUserName 接收者
- * @param string|array $Content 发送内容,默认为文本消息,传数组可设定消息类型,格式为:aray('MsgType' => 'image', 'Content' => '消息内容')
- * @return string 返回XML格式消息
- */
- public static function generateMessage($FromUserName, $ToUserName, $Content){
- $aMsgTypes = array(
- 'text', 'image', 'voice', 'video', 'music', 'news', 'transfer_customer_service'
- );
- if(is_array($Content)){
- $type = $Content['MsgType'];
- $data = $Content['Content'];
- }else{
- $type = 'text';
- $data = $Content;
- }
- if(!in_array($type, $aMsgTypes, false)){
- return WeixinApi::throw_exception("Unknown MsgType: $type", WXAPI_ERR_CONFIG, $Content, __FILE__, __LINE__);
- }
- if(!strcasecmp($type, 'transfer_customer_service')){
- $method = 'generateTransferCustomerServiceMessage';
- }else{
- $method = 'generate' . ucfirst(strtolower($type)) . 'Message';
- }
- return self::$method($FromUserName, $ToUserName, $data);
- }
- public function createTextMessage($FromUserName, $ToUserName, $content){
- return self::generateTextMessage($FromUserName, $ToUserName, $content);
- }
- /**
- * 创建文本消息
- * @param object $object
- * @param string $content 文本内容,支持换行
- * @return string
- */
- public static function generateTextMessage($FromUserName, $ToUserName, $content)
- {
- $msgTpl = "<xml>
- <ToUserName><![CDATA[%s]]></ToUserName>
- <FromUserName><![CDATA[%s]]></FromUserName>
- <CreateTime>%s</CreateTime>
- <MsgType><![CDATA[text]]></MsgType>
- <Content><![CDATA[%s]]></Content>
- </xml>";
- return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $content);
- }
- public function createImageMessage($FromUserName, $ToUserName, $mediaId){
- return self::generateImageMessage($FromUserName, $ToUserName, $mediaId);
- }
- /**
- * 创建图片消息
- * @param object $object
- * @param string $mediaId 通过上传多媒体文件,得到的id
- * @return string
- */
- public static function generateImageMessage($FromUserName, $ToUserName, $mediaId)
- {
- $msgTpl = "<xml>
- <ToUserName><![CDATA[%s]]></ToUserName>
- <FromUserName><![CDATA[%s]]></FromUserName>
- <CreateTime>%s</CreateTime>
- <MsgType><![CDATA[image]]></MsgType>
- <Image>
- %s
- </Image>
- </xml>";
- $mediaTpl = "<MediaId><![CDATA[%s]]></MediaId>";
- if(!is_array($mediaId)){
- $mediaIds = array($mediaId);
- }else{
- $mediaIds = $mediaId;
- }
- $media = '';
- foreach($mediaIds as $mediaId){
- $media .= sprintf($mediaTpl, $mediaId);
- }
- return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $media);
- }
- public function createVoiceMessage($FromUserName, $ToUserName, $mediaId){
- return self::generateVoiceMessage($FromUserName, $ToUserName, $mediaId);
- }
- /**
- * 创建语音消息
- * @param object $object
- * @param string $mediaId 通过上传多媒体文件,得到的id
- * @return string
- */
- public static function generateVoiceMessage($FromUserName, $ToUserName, $mediaId)
- {
- $msgTpl = "<xml>
- <ToUserName><![CDATA[%s]]></ToUserName>
- <FromUserName><![CDATA[%s]]></FromUserName>
- <CreateTime>%s</CreateTime>
- <MsgType><![CDATA[voice]]></MsgType>
- <Voice>
- %s
- </Voice>
- </xml>";
- $mediaTpl = "<MediaId><![CDATA[%s]]></MediaId>";
- if(!is_array($mediaId)){
- $mediaIds = array($mediaId);
- }else{
- $mediaIds = $mediaId;
- }
- $media = '';
- foreach($mediaIds as $mediaId){
- $media .= sprintf($mediaTpl, $mediaId);
- }
- return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $media);
- }
- public function createVideoMessage($FromUserName, $ToUserName, $mediaId, $thumbMediaId=NULL){
- return self::generateVideoMessage($FromUserName, $ToUserName, $mediaId, $thumbMediaId);
- }
- /**
- * 创建视频消息
- * @param object $object
- * @param string|array $mediaId 通过上传多媒体文件,得到的id,可以传数组:Array('MediaId'=>mediaid, 'ThumbMediaId'=>thumbMediaId)
- * @param string $thumbMediaId 缩略图的媒体id,通过上传多媒体文件,得到的id,必填字段
- * @return string
- */
- public static function generateVideoMessage($FromUserName, $ToUserName, $mediaId, $thumbMediaId=NULL)
- {
- $msgTpl = "<xml>
- <ToUserName><![CDATA[%s]]></ToUserName>
- <FromUserName><![CDATA[%s]]></FromUserName>
- <CreateTime>%s</CreateTime>
- <MsgType><![CDATA[video]]></MsgType>
- <Video>
- %s
- </Video>
- </xml>";
- if(is_array($mediaId)){
- $mediaData = array(
- 'MediaId' => $mediaId['MediaId'],
- 'ThumbMediaId' => $mediaId['ThumbMediaId'],
- );
- }else{
- $mediaData = array(
- 'MediaId' => $mediaId,
- 'ThumbMediaId' => $thumbMediaId,
- );
- }
- $media = "";
- foreach ($mediaData as $n=>$d){
- $n = ucfirst($n);
- $mediaTpl = "<{$n}><![CDATA[%s]]></{$n}>";
- $media .= sprintf($mediaTpl, $d);
- }
- return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $media);
- }
- public function createMusicMessage($FromUserName, $ToUserName, $thumbMediaId, $title=NULL, $description=NULL, $musicUrl=NULL, $hqMusicUrl=NULL)
- {
- return self::generateMusicMessage($FromUserName, $ToUserName, $thumbMediaId, $title, $description, $musicUrl, $hqMusicUrl);
- }
- /**
- * 创建音乐消息
- * @param object $object
- * @param string|array $thumbMediaId 缩略图的媒体id,通过上传多媒体文件,得到的id,必填字段,传数组格式如:
- * array (
- "Title" => $title,
- "Description" => $description,
- "MusicURL" => $musicURL,
- "HQMusicUrl" => $hqMusicUrl,
- "ThumbMediaId" => $thumbMediaId,
- )
- * @param string $title 音乐标题
- * @param string $description 音乐描述
- * @param string $musicUrl 音乐链接
- * @param string $hqMusicUrl 高质量音乐链接,WIFI环境优先使用该链接播放音乐
- * @return string
- */
- public static function generateMusicMessage($FromUserName, $ToUserName, $thumbMediaId, $title=NULL, $description=NULL, $musicUrl=NULL, $hqMusicUrl=NULL)
- {
- $msgTpl = "<xml>
- <ToUserName><![CDATA[%s]]></ToUserName>
- <FromUserName><![CDATA[%s]]></FromUserName>
- <CreateTime>%s</CreateTime>
- <MsgType><![CDATA[music]]></MsgType>
- <Music>%s
- </Music>
- </xml>";
- $media = "";
- if (is_array ( $thumbMediaId )) {
- $mediaData = array (
- "Title" => $thumbMediaId['Title'],
- "Description" => $thumbMediaId['Description'],
- "MusicUrl" => $thumbMediaId['MusicUrl'],
- "HQMusicUrl" => $thumbMediaId['HQMusicUrl'],
- "ThumbMediaId" => $thumbMediaId['ThumbMediaId'],
- );
- } else {
- $mediaData = array (
- "Title" => $title,
- "Description" => $description,
- "MusicUrl" => $musicUrl,
- "HQMusicUrl" => $hqMusicUrl,
- "ThumbMediaId" => $thumbMediaId,
- );
- }
- foreach($mediaData as $n=>$d){
- if($d){
- $n = ucfirst($n);
- $mediaTpl = "<{$n}><![CDATA[%s]]></{$n}>";
- $media .= sprintf($mediaTpl, $d);
- }
- }
- return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $media);
- }
- public function createNewsMessage($FromUserName, $ToUserName, $title, $description=NULL, $picUrl=NULL, $url=NULL)
- {
- return self::generateNewsMessage($FromUserName, $ToUserName, $title, $description, $picUrl, $url);
- }
- /**
- * 创建图文消息
- * @param object $object
- * @param string|array $title 图文消息标题,传数组格式如:
- * array(
- "Title" => $title,
- "Description" => $description,
- "PicUrl" => $picUrl,
- "Url" => $url,
- )
- 或
- array( 0 => array(
- "Title" => $title,
- "Description" => $description,
- "PicUrl" => $picUrl,
- "Url" => $url,
- ))
- * @param string $description 图文消息描述
- * @param string $picUrl 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200
- * @param string $url 点击图文消息跳转链接
- * @return string
- */
- public static function generateNewsMessage($FromUserName, $ToUserName, $title, $description=NULL, $picUrl=NULL, $url=NULL)
- {
- $msgTpl = "<xml>
- <ToUserName><![CDATA[%s]]></ToUserName>
- <FromUserName><![CDATA[%s]]></FromUserName>
- <CreateTime>%s</CreateTime>
- <MsgType><![CDATA[news]]></MsgType>
- <ArticleCount>%s</ArticleCount>
- <Articles>
- %s
- </Articles>
- </xml>";
- $media = "";
- $items = array();
- if(is_array($title)){
- if(isset($title['Title']) || isset($title['Description']) || isset($title['PicUrl']) || isset($title['Url'])){
- $items[] = $title;
- }else{
- $items = $title;
- }
- }else{
- $items[] = array(
- "Title" => $title,
- "Description" => $description,
- "PicUrl" => $picUrl,
- "Url" => $url,
- );
- }
- $count = count($items);
- if($count>10){
- return WeixinApi::throw_exception("Over Max 10 news messages", WXAPI_ERR_CONFIG, array('items'=>$items), __FILE__, __LINE__);
- }
- $valid_item_tags = array('Title', 'Description', 'PicUrl', 'Url');
- foreach($items as $item){
- $media .= "<item>";
- foreach ( $item as $n => $d ) {
- if ($d && in_array($n, $valid_item_tags, true)) {
- $n = ucfirst($n);
- $mediaTpl = "<{$n}><![CDATA[%s]]></{$n}>";
- $media .= sprintf ( $mediaTpl, $d );
- }
- }
- $media .= "</item>";
- }
- return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $count, $media);
- }
- /**
- * 创建转发客服消息
- * @return string
- */
- public static function generateTransferCustomerServiceMessage($FromUserName, $ToUserName, $TransInfo_KfAccount=NULL)
- {
- $msgTpl = "<xml>
- <ToUserName><![CDATA[%s]]></ToUserName>
- <FromUserName><![CDATA[%s]]></FromUserName>
- <CreateTime>%s</CreateTime>
- <MsgType><![CDATA[transfer_customer_service]]></MsgType>";
- if($TransInfo_KfAccount){
- $msg .= "<TransInfo>
- <KfAccount>%s</KfAccount>
- </TransInfo>";
- }
- $msgTpl .= "</xml>";
- return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $TransInfo_KfAccount);
- }
- public function __get($c){
- if(substr($c, 0, 1)!='_'){
- if(in_array($c, array('Http'))){
- return parent::__get($c);
- }else{
- $n = '_' . $c;
- return $this->$n;
- }
- }
- }
- /**
- * 生成签名
- * @param string $msg_encrypt
- * @param string $nonce
- * @param string $timestamp
- * @param string $token
- * @return string
- */
- public static function genearteSignature($msg_encrypt, $nonce, $timestamp, $token){
- $tmpArr = array($token, $timestamp, $nonce, $msg_encrypt);
- sort($tmpArr, SORT_STRING);
- $tmpStr = implode( $tmpArr );
- return sha1( $tmpStr );
- }
- /**
- * 创建加密消息
- * @param string $encrypt_content 加密内容
- * @param string $nonce 随机数
- * @param int $timestamp 时间戳
- * @param string $signature 签名
- * @return string
- */
- public static function generateEncryptMessage($encrypt_content, $nonce, $timestamp, $signature)
- {
- $msgTpl = "<xml>
- <Encrypt><![CDATA[%s]]></Encrypt>
- <MsgSignature><![CDATA[%s]]></MsgSignature>
- <TimeStamp>%s</TimeStamp>
- <Nonce><![CDATA[%s]]></Nonce>
- </xml>
- ";
- return sprintf($msgTpl, $encrypt_content, $signature, $timestamp, $nonce);
- }
- /**
- * 加密响应消息
- * @return string
- */
- protected function _encrypt_response($msg, $encodingkey){
- $msg_encrypt = $this->_encryptMsg($msg, $encodingkey);
- $nonce = WeixinApi_Kit::gen_random_number(11);
- $timestamp = time();
- $signature = self::genearteSignature($msg_encrypt, $nonce, $timestamp, $this->Config->AppToken);
- return self::generateEncryptMessage($msg_encrypt, $nonce, $timestamp, $signature);
- }
- /**
- * 对明文进行加密
- * @param string $msg 需要加密的明文
- * @param string $encodingkey 加密私钥
- * @return string|false 加密得到的密文,失败返回flase
- */
- protected function _encryptMsg($msg, $encodingkey=NULL)
- {
- $AESKey = base64_decode(($encodingkey?$encodingkey:$this->Config->AppEncodingAESKey) . "=");
- // 获得16位随机字符串,填充到明文之前
- $random = WeixinApi_Kit::gen_random_string ( 16 );
- $msg = $random . pack ( "N", strlen ( $msg ) ) . $msg . $this->Config->AppId;
- // 网络字节序
- $size = mcrypt_get_block_size ( MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC );
- $module = mcrypt_module_open ( MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '' );
- if(false===$module){
- return $this->_throw_exception(
- "Cann't open an encryption descriptor"
- , WXAPI_ERR_BAD_ENCRYPT
- , $msg
- , __FILE__, __LINE__
- );
- }
- $iv = substr ( $AESKey, 0, 16 );
- // 使用自定义的填充方式对明文进行补位填充
- $msg = WeixinApi_Kit::pkcs7_encode ( $msg, 32 );
- $init = mcrypt_generic_init ( $module, $AESKey, $iv );
- if(false===$init){
- return $this->_throw_exception(
- "Cann't initialize buffers for encryption"
- , WXAPI_ERR_BAD_ENCRYPT
- , array('msg' => $msg, 'mcrypt_generic_init return' => $init)
- , __FILE__, __LINE__
- );
- }elseif(-3==$init){
- return $this->_throw_exception(
- "the key length was incorrect"
- , WXAPI_ERR_BAD_ENCRYPT
- , array('msg' => $msg, 'mcrypt_generic_init return' => $init)
- , __FILE__, __LINE__
- );
- }elseif(-4==$init){
- return $this->_throw_exception(
- "there was a memory allocation problem"
- , WXAPI_ERR_BAD_ENCRYPT
- , array('msg' => $msg, 'mcrypt_generic_init return' => $init)
- , __FILE__, __LINE__
- );
- }elseif($init<0){
- return $this->_throw_exception(
- "an unknown error occurred when initialize buffers for encryption"
- , WXAPI_ERR_BAD_ENCRYPT
- , array('msg' => $msg, 'mcrypt_generic_init return' => $init)
- , __FILE__, __LINE__
- );
- }
- // 加密
- $encrypted = mcrypt_generic ( $module, $msg );
- mcrypt_generic_deinit ( $module );
- mcrypt_module_close ( $module );
- // print(base64_encode($encrypted));
- // 使用BASE64对加密后的字符串进行编码
- return base64_encode ( $encrypted );
- }
- }
QQ:1542385235 (PHP、Java、安卓苹果app制作修改、页面切图、各类模板修改、仿站,数据库修复、WAP制作修改 。我们团队是专门做网站开发的,都是有3年以上工作经验。需要后台系统开发,网页页面制作,app制作,ui设计的请加我qq联系。非诚勿扰!!)
本人qq群也有许多的技术文档,希望可以为你提供一些帮助(非技术的勿加!)。
QQ群: 281442983 (点击链接加入群:http://jq.qq.com/?_wv=1027&k=29LoD19)