【项目需求】
通过微信提供的接口,实现微信公众号与后端的应用程序数据交互、消息响应等功能。
【项目疑难点】
- 理解接口工作方式,统一接口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 );
- }
- }
-