zoukankan      html  css  js  c++  java
  • 微信公众号接口类(PHP版本)

    【项目需求】

    通过微信提供的接口,实现微信公众号与后端的应用程序数据交互、消息响应等功能。

    【项目疑难点】

    • 理解接口工作方式,统一接口API,响应速度、安全性等
     
    【代码举例】
     
    WeixinApi.class.php  微信公众号接口基类
     
    [php] view plain copy
    1. <?php  
    2. /** 
    3.  * 微信API 公用方法 
    4.  *  
    5.  * PHP version 5 
    6.  *  
    7.  * @category    Lib 
    8.  * @package     COM 
    9.  * @subpackage  GZNC 
    10.  * @author      zhongyiwen 
    11.  * @version     SVN: $Id: WeixinApi.class.php 10 2013-10-08 01:34:05Z zhongyw $ 
    12.  */  
    13.   
    14. /** 
    15.  * 错误代码 
    16.  */  
    17. define('WXAPI_ERR_CONFIG', 1001); // 配置错误  
    18. define('WXAPI_ERR_HTTP', 1002); // 请求失败  
    19. define('WXAPI_ERR_LOGIN', 1003); // 登录失败  
    20. define('WXAPI_ERR_FETCH_DATA', 1004); // 获取数据失败  
    21. define('WXAPI_ERR_MISS_RESPONSE', 1005); // 缺少响应  
    22. define('WXAPI_ERR_BAD_SIGNATURE', 1006); // 签名校验失败  
    23. define('WXAPI_ERR_BAD_DECRYPT', 1007); // 消息解密失败  
    24. define('WXAPI_ERR_BAD_ENCRYPT', 1008); // 消息加密失败  
    25. define('WXAPI_ERR_ACCESS_TOKEN', 1009); // access token凭证错误  
    26.   
    27. /** 
    28.  * 日志级别 
    29.  */  
    30. define('WXAPI_LOG_EMERG', 'EMERG');  // 严重错误: 导致系统崩溃无法使用  
    31. define('WXAPI_LOG_ALERT', 'ALERT');  // 警戒性错误: 必须被立即修改的错误  
    32. define('WXAPI_LOG_CRIT', 'CRIT');  // 临界值错误: 超过临界值的错误,例如一天24小时,而输入的是25小时这样  
    33. define('WXAPI_LOG_ERR', 'ERR');  // 一般错误: 一般性错误  
    34. define('WXAPI_LOG_WARN', 'WARN');  // 警告性错误: 需要发出警告的错误  
    35. define('WXAPI_LOG_NOTICE', 'NOTIC');  // 通知: 程序可以运行但是还不够完美的错误  
    36. define('WXAPI_LOG_INFO', 'INFO');  // 信息: 程序输出信息  
    37. define('WXAPI_LOG_DEBUG', 'DEBUG');  // 调试: 调试信息  
    38. define('WXAPI_LOG_EXCEPTION', 'EXCEPTION'); // 异常信息  
    39.   
    40. /** 
    41.  * 微信接口默认常量值 
    42.  */  
    43. define('WXAPI_ACCESS_TOKEN_EXPIRE', 7100); // access token有效时间,设置比微信默认有效时间7200秒小,避免出现过期错误  
    44. define('WXAPI_ACCESS_TOKEN_LIMIT', 2000); // access token每日限制次数  
    45. define('WXAPI_JSAPI_TICKET_EXPIRE', 7100); // jsapi ticket有效时间,单位秒  
    46. define('WXAPI_JSAPI_TICKET_LIMIT', 2000); // jsapi ticket每日限制次数  
    47. define('WXAPI_QRCODE_MIN_SCENE', 1); // 二维码场景值最小值  
    48. define('WXAPI_QRCODE_MAX_SCENE', 2147483647); // 二维码场景值最大值, 32位非0整型  
    49. define('WXAPI_QRCODE_MAX_LIMIT_SCENE', 100000); // 永久二维码场景值最大值  
    50. define('WXAPI_QRCODE_EXPIRE', 1800); // 临时二维码有效时间,单位秒  
    51. define('WXAPI_GROUP_MIN_CUSTOM_ID', 100); // 用户自定义用户组起始id值  
    52.   
    53. /** 
    54.  * 微信暗语 
    55.  */  
    56. define('WXAPI_ARGOT_WHO_AM_I', 'show me your name'); // 显示当前4susername  
    57. define('WXAPI_ARGOT_DESTORY_SESSION', 'let me out'); // 清除当前用户session  
    58.   
    59. class WeixinApi{  
    60.     /** 
    61.      * 实例化对象 
    62.      *  
    63.      * @var array 
    64.      */  
    65.     protected static $_instance = array();  
    66.       
    67.   
    68.     /** 
    69.      * 是否启用缓存 
    70.      * @var bool 
    71.      */  
    72.     protected $_cache = false;  
    73.       
    74.     /** 
    75.      * 是否启用调试 
    76.      * @var bool 
    77.      */  
    78.     protected $_debug = false;  
    79.       
    80.     /** 
    81.      * 配置对象实例 
    82.      * @var object 
    83.      */  
    84.     public $Config;  
    85.       
    86.     /** 
    87.      * 错误信息 
    88.      * @var string 
    89.      */  
    90.     protected $_error = NULL;  
    91.       
    92.     public function __construct($Config=NULL){  
    93.         $this->Config = is_object($Config)?$Config:self::instance('WeixinApi_Config');  
    94.         $this->_cache = $this->Config->Cache;  
    95.     }  
    96.       
    97.     /** 
    98.      * 取得对象实例 支持调用类的静态方法 
    99.      * @param string $class 对象类名 
    100.      * @param string $method 类的静态方法名 
    101.      * @return object 
    102.      */  
    103.     public static function instance($class,$args=array()) {  
    104.         $identify   =   $class.md5(serialize($args));  
    105.         if(!isset(WeixinApi::$_instance[$identify])) {  
    106.             if(!class_exists($class)){  
    107.                 require $class . ".class.php";  
    108.             }  
    109.       
    110.             if(class_exists($class)){  
    111.                 $arg_str = '';  
    112.                 if($args && is_array($args)){  
    113.                     foreach ($args as $i=>$arg){  
    114.                         /* 
    115.                         if(is_object($arg) || is_array($arg)){ 
    116.                             return WeixinApi::throw_exception( 
    117.                                     "Cann't init class $class instanse with object argument" 
    118.                                     , WXAPI_ERR_CONFIG 
    119.                                     , array('class' => $class, 'args' => $args) 
    120.                                     , __FILE__, __LINE); 
    121.                         }else{ 
    122.                             $arg_str = "'" . implode("', '", array_map('addslashes', $args)) . "'"; 
    123.                         }*/  
    124.                           
    125.                         if(is_object($arg) || is_array($arg)){  
    126.                             $arg_param_name = 'arg_param' . $i;  
    127.                             $$arg_param_name = $arg;  
    128.                             $arg_str .= ", ${$arg_param_name}";  
    129.                         }else{  
    130.                             $arg_str .= ", '" . addcslashes($arg, "'") . "'";  
    131.                         }  
    132.                     }  
    133.                       
    134.                     if($arg_str){  
    135.                         $arg_str = substr($arg_str, 2);  
    136.                     }  
    137.                       
    138.                 }elseif($args && is_object($args)){  
    139.                     /* 
    140.                     return WeixinApi::throw_exception( 
    141.                             "Cann't init class $class instanse with object argument" 
    142.                             , WXAPI_ERR_CONFIG 
    143.                             , array('class' => $class, 'args' => $args) 
    144.                             , __FILE__, __LINE); 
    145.                     */  
    146.                     $arg_param_name = 'arg_param';  
    147.                     $$arg_param_name = $args;  
    148.                     $arg_str = "${$arg_param_name}";  
    149.                       
    150.                 }elseif($args){  
    151.                     $arg_str = "'" . addcslashes($args, "'") . "'";  
    152.                 }  
    153.                   
    154.                 $code = "return new " . $class . "(" . $arg_str . ");";  
    155.                 $o = eval($code);  
    156.                   
    157.                 if(!$o){  
    158.                     return WeixinApi::throw_exception(  
    159.                              "Cann't init class instanse: $class"  
    160.                             , WXAPI_ERR_CONFIG  
    161.                             , array('class' => $class, 'args' => $args)  
    162.                             , __FILE__, __LINE);  
    163.                 }  
    164.                 WeixinApi::$_instance[$identify] = $o;  
    165.             }  
    166.             else{  
    167.                 return WeixinApi::throw_exception(  
    168.                          "Cann't found class: $class file."  
    169.                         , WXAPI_ERR_CONFIG  
    170.                         , array('class' => $class, 'args' => $args)  
    171.                         , __FILE__, __LINE__);  
    172.             }  
    173.         }  
    174.         return self::$_instance[$identify];  
    175.     }  
    176.       
    177.     public static function throw_exception($message, $code=NULL, $data=NULL, $file=NULL, $line=NULL){  
    178.         if(!class_exists('WeixinApi_Exception')){  
    179.             require 'WeixinApi_Exception.class.php';  
    180.         }  
    181.   
    182.         // 只有配置错误才再次抛出异常  
    183.         //if($code==WXAPI_ERR_CONFIG){  
    184.             throw new WeixinApi_Exception($message, $code, $data, $file, $line);  
    185.         //}else{  
    186.         //  return false;  
    187.         //}  
    188.     }  
    189.       
    190.     protected function _throw_exception($message, $code=NULL, $data=NULL, $file=NULL, $line=NULL){  
    191.         try{  
    192.             WeixinApi::throw_exception($message, $code, $data, $file, $line);  
    193.         }catch(Exception $e){  
    194.             //$this->_error = $e->getMessage();  
    195.             $this->_setError($e->getMessage());  
    196.             $this->_log($e->__toString(), WXAPI_LOG_ERR);  
    197.               
    198.             // 只有配置错误才再次抛出异常  
    199.             if($code==WXAPI_ERR_CONFIG){  
    200.                 throw $e;  
    201.             }else{  
    202.                 return false;  
    203.             }  
    204.         }  
    205.     }  
    206.   
    207.     public function getError(){  
    208.         return is_array($this->_error)?implode(',', $this->_error):$this->_error;  
    209.     }  
    210.       
    211.     /** 
    212.      * 设置错误信息 
    213.      * @param string $error 
    214.      */  
    215.     protected function _setError($error){  
    216.         $this->_error[] = $error;  
    217.     }  
    218.       
    219.     public function __get($n){  
    220.         if(isset($this->$n)){  
    221.             return $this->$n;  
    222.         }else if(in_array($n, array('Http', 'Cache', 'Log'))){  
    223.             if('Http'==$n && !$this->Config->$n){  
    224.                 return $this->_throw_exception("$n is not setted in your config"  
    225.                         , WXAPI_ERR_CONFIG  
    226.                         , array('class'=>$n)  
    227.                         , __FILE__, __LINE__  
    228.                 );  
    229.             }elseif(!$this->Config->$n){  
    230.                 // Do Nothing  
    231.                 // Disabled Cache or Log  
    232.                 return false;  
    233.             }  
    234.               
    235.             if(is_object($this->Config->$n)){  
    236.                 return $this->Config->$n;  
    237.             }elseif(is_array($this->Config->$n)){  
    238.                 list($callback, $params) = $this->Config->$n;  
    239.                 if(!is_array($params)){  
    240.                     $params = array($params);  
    241.                 }  
    242.                 return call_user_func_array($callback, $params);  
    243.             }else{  
    244.                 return $this->$n = WeixinApi::instance($this->Config->$n);  
    245.             }  
    246.         }else{  
    247.             return false;  
    248.         }  
    249.     }  
    250.       
    251.     protected function _check_http_url($url){  
    252.         if(strcasecmp('http', substr($url, 0, 4))){  
    253.             $url = $this->Config->ApiGateway . $url;  
    254.         }  
    255.           
    256.         return $url;  
    257.     }  
    258.       
    259.     protected function _check_http_ssl($url){  
    260.         if(!strcasecmp('https://', substr($url, 0, 8))){  
    261.             $this->Http->setSsl();  
    262.               
    263.             // 指定ssl v3  
    264.             // 2014.09.05 zhongyw 微信API不能指定用ssl v3版本  
    265.             //$this->Http->setOpt(CURLOPT_SSLVERSION, 3);  
    266.               
    267.             // 指定TLS  
    268.             // 2014.10.31 zhongyw   
    269.             // 微信公众平台将关闭掉SSLv2、SSLv3版本支持,不再支持部分使用SSLv2、 SSLv3或更低版本的客户端调用。请仍在使用这些版本的开发者于11月30日前尽快修复升级。  
    270.             defined('CURL_SSLVERSION_TLSv1') || define('CURL_SSLVERSION_TLSv1', 1); // 兼容PHP<=5.3  
    271.             $this->Http->setOpt(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);  
    272.         }  
    273.           
    274.         return $url;  
    275.     }  
    276.       
    277.     protected function _check_http_data($data){  
    278.         return $data;  
    279.     }  
    280.       
    281.     /** 
    282.      * 发送GET请求 
    283.      *  
    284.      * @param string $url   链接 
    285.      * @param string|array $data    参数 
    286.      * @param bool $check   是否检查链接和参数 
    287.      * @return string 
    288.      */  
    289.     public function get($url, $data = null, $check=true) {  
    290.         if ($check) {  
    291.             $url = $this->_check_http_url ( $url );  
    292.             $url = $this->_check_http_ssl ( $url );  
    293.             $data = $this->_check_http_data ( $data );  
    294.         }  
    295.           
    296.         if(!($return = $this->Http->get($url, $data)) && ($error=$this->Http->getError())){  
    297.             return $this->_throw_exception(  
    298.                       $error  
    299.                     , WXAPI_ERR_HTTP  
    300.                     , array('url' => $url, 'data' => $data, 'method' => 'get', 'response' => $return)   
    301.                     , __FILE__, __LINE__);  
    302.         }  
    303.           
    304.   
    305.         return $return;  
    306.     }  
    307.       
    308.     /** 
    309.      * 发送POST请求 
    310.      * 
    311.      * @param string $url   链接 
    312.      * @param array $data   参数 
    313.      * @param bool $check   是否检查链接和参数 
    314.      * @return string 
    315.      */  
    316.     public function post($url, $data, $check=true) {  
    317.         if ($check) {  
    318.             $url = $this->_check_http_url ( $url );  
    319.             $url = $this->_check_http_ssl ( $url );  
    320.             $data = $this->_check_http_data ( $data );  
    321.         }  
    322.           
    323.         // 使用plainPost  
    324.         if(!($return = $this->Http->plainPost($url, $data)) && ($error=$this->Http->getError())){  
    325.             return $this->_throw_exception(  
    326.                       $error  
    327.                     , WXAPI_ERR_HTTP  
    328.                     , array('url' => $url, 'data' => $data, 'method' => 'post', 'response' => $return)   
    329.                     , __FILE__, __LINE__);  
    330.         }  
    331.       
    332.         return $return;  
    333.     }  
    334.       
    335.     public function setHttpOption($opt, $val=NULL){  
    336.         if(!$opt){  
    337.             return false;  
    338.         }  
    339.       
    340.         $options = array();  
    341.         if(!is_array($opt)){  
    342.             $options = array($opt=>$val);  
    343.         }else{  
    344.             $options = $opt;  
    345.         }  
    346.           
    347.         foreach($options as $opt=>$val){  
    348.             $this->Http->setOpt(constant($opt), $val);  
    349.         }  
    350.     }  
    351.       
    352.     /** 
    353.      * 运行回调函数 
    354.      *  
    355.      * 回调函数支持以下几种格式: 
    356.      * 1、直接函数:funcname,或带参数:array(funcname, params) 
    357.      * 2、静态方法:array(array('WeixinApi', 'methodname'), params) 
    358.      * 3、对象方法:array(Object, 'methodname') 或  array(array(Object, 'methodname'), params) 
    359.      * 4、二次回调,如: 
    360.      * array(array( 
    361.               array(array('WeixinApi', 'instance'), 'S4WeixinResponse') 
    362.                     , 'run') 
    363.             , '') 
    364.              
    365.             可以先调用Runder::instance()初始化S4Web实例后,再调用S4Web->apiglog_save()方法执行回调 
    366.      *  
    367.      * @param mixed $callback 回调函数 
    368.      * @param array $extra_params 回调参数 
    369.      * @return mixed 
    370.      */  
    371.     protected function _run_callback($callback, $extra_params=array(), &$callbackObject=NULL) {  
    372.         $extra_params = is_array ( $extra_params ) ? $extra_params : ($extra_params ? array (  
    373.                 $extra_params   
    374.         ) : array ());  
    375.           
    376.         $params = $extra_params;  
    377.           
    378.         if(is_object($callback)){  
    379.             return $this->_throw_exception(  
    380.                     "Object callback must set method"  
    381.                     , SCRIPT_ERR_CONFIG  
    382.                     , array('callback'=>$callback)  
    383.                     , __FILE__, __LINE__  
    384.             );  
    385.         }  
    386.         else if (is_array ( $callback )) {  
    387.             $func = $callback [0];  
    388.             if (! empty ( $callback [1] )) {  
    389.                 if (is_array ( $callback [1] )) {  
    390.                     $params = array_merge ( $extra_params, $callback [1] );  
    391.                 } else {  
    392.                     $params [] = $callback [1];  
    393.                 }  
    394.             }  
    395.               
    396.             if (is_object ( $func )) {  
    397.                 $callbackObject = $func;  
    398.                 // 注意:此处不需要传$params作为参数  
    399.                 return call_user_method_array ( $callback [1], $callback [0], $extra_params );  
    400.             } elseif (is_object ( $callback [0] [0] )) {  
    401.                 $callbackObject = $callback [0] [0];  
    402.                 return call_user_method_array ( $callback [0] [1], $callback [0] [0], $params);  
    403.             }  
    404.         } else {  
    405.             $func = $callback;  
    406.         }  
    407.           
    408.         if(is_array($func) && is_array($func[0])){  
    409.             $call = call_user_func_array($func[0][0], is_array($func[0][1])?$func[0][1]:array($func[0][1]));  
    410.             if($call===false){  
    411.                 return false;  
    412.             }  
    413.               
    414.             $func = array($call, $func[1]);  
    415.         }  
    416.           
    417.         if(is_array($func) && is_object($func[0])){  
    418.             $callbackObject = $func[0];  
    419.         }  
    420.           
    421.         return call_user_func_array ( $func, $params);  
    422.     }  
    423.       
    424.     /** 
    425.      * 是否缓存 
    426.      * @param bool|int $cache true = 启用缓存,false = 不缓存,-1 = 重新生成缓存,3600 = 设置缓存时间为3600秒 
    427.      * @return WeixinClient 
    428.      */  
    429.     public function cache($cache=true){  
    430.         $this->_cache = $cache;  
    431.         return $this;  
    432.     }  
    433.       
    434.     public function debug($debug=true){  
    435.         $this->_debug = $debug;  
    436.         return $this;  
    437.     }  
    438.       
    439.     /** 
    440.      * 写入或者获取缓存 
    441.      *  
    442.      * @param string $cache_id 缓存id 
    443.      * @param string $cache_data 缓存数据 
    444.      * @param int $cache_expire 缓存时间 
    445.      * @return mixed|boolean 
    446.      */  
    447.     protected function _cache($cache_id, $cache_data=NULL, $cache_expire=NULL){  
    448.         if($this->Config->Cache){  
    449.             // 保存缓存索引  
    450.             if($cache_id && (!is_null($cache_data) && $cache_data!==false && $cache_expire!==false)  
    451.             && $this->Config->CacheSaveIndex  
    452.             && strcasecmp($cache_id, $this->Config->CacheSaveIndex)  
    453.             ){  
    454.                 $index_cache_id = $this->Config->CacheSaveIndex;  
    455.                 $index_cache_expire = 315360000; // 永久保存: 3600*24*365*10  
    456.                       
    457.                 // 取已有的缓存  
    458.                 if(!($index_cache_data=$this->_cache($index_cache_id))){  
    459.                     $index_cache_data = array();  
    460.                 }  
    461.                   
    462.                 // 删除已过期索引  
    463.                 $now_time = time();  
    464.                 foreach($index_cache_data as $k=>$d){  
    465.                     if($d && $d['expire'] && $d['created'] && ($d['created']+$d['expire'])<$now_time){  
    466.                         unset($index_cache_data[$k]);  
    467.                     }  
    468.                 }  
    469.                       
    470.                 $index_cache_data[$cache_id] = array(  
    471.                         'created' => $now_time,  
    472.                         'expire' => $cache_expire,  
    473.                 );  
    474.                       
    475.                 //S4Web::debug_log("$index_cache_id=$index_cache_id");  
    476.                 //S4Web::debug_log("$index_cache_data=" . print_r($index_cache_data, true));  
    477.   
    478.                 $succ = $this->_cache($index_cache_id, $index_cache_data, $index_cache_expire);  
    479.                 $this->_log("Save cache id:  " . $cache_id . ' ' . ($succ?'Succ':'Failed') . '!', WXAPI_LOG_DEBUG);  
    480.             }  
    481.   
    482.             return $this->_run_callback($this->Config->Cache, array($cache_id, $cache_data, $cache_expire));  
    483.         }else{  
    484.             return false;  
    485.         }  
    486.     }  
    487.       
    488.     protected function _cache_id($url, $data = NULL, $cache = NULL) {  
    489.         if ($cache && $cache!==true && !is_numeric($cache)){  
    490.             if(is_string ( $cache )) {  
    491.                 $cache_id = $cache;  
    492.             } elseif (is_array ( $cache ) && isset($cache['cache_id'])) {  
    493.                 $cache_id = $cache ['cache_id'];  
    494.             } elseif (is_object ( $cache ) && isset($cache['cache_id'])) {  
    495.                 $cache_id = $cache->cache_id;  
    496.             }  
    497.               
    498.             // 添加缓存前缀  
    499.             /* 
    500.              注:由ThinkPHP处理缓存添加前缀:C('DATA_CACHE_PREFIX') 
    501.             if($cache_id && $this->Config->CacheBin){ 
    502.                 $cache_id = $this->Config->CacheBin . $cache_id; 
    503.             }*/  
    504.         }  
    505.       
    506.         if (!$cache_id) {  
    507.             $param = '';  
    508.             if ($data && is_array ( $data )) {  
    509.                 $param .= http_build_query ( $data );  
    510.             } else {  
    511.                 $param .= $data;  
    512.             }  
    513.             $cache_id = md5 ( $this->Config->AppId . $url . $param );  
    514.             //return $cache_id;  
    515.         }  
    516.   
    517.         return $cache_id;  
    518.     }  
    519.       
    520.     protected function _cache_expire($url, $data=NULL, $cache=NULL){  
    521.         if(!$cache){  
    522.             return 0;  
    523.         }elseif(is_numeric($cache) && $cache>0){  
    524.             $cache_expire = $cache;  
    525.         }elseif (is_array($cache) && isset($cache['cache_expire'])){  
    526.             $cache_expire = $cache['cache_expire'];  
    527.         }elseif (is_object($cache) && isset($cache->cache_expire)){  
    528.             $cache_expire = $cache->cache_expire;  
    529.         }  
    530.       
    531.         return $cache_expire?$cache_expire:$this->Config->CacheExpire;  
    532.     }  
    533.       
    534.     /** 
    535.      * 判断是否强制刷新缓存 
    536.      * @param unknown $url 
    537.      * @param string $data 
    538.      * @param string $cache 
    539.      * @return bool 
    540.      */  
    541.     protected function _cache_refresh($url, $data=NULL, $cache=NULL){  
    542.         $cache_refresh = false;  
    543.           
    544.         if ($cache && $cache!==true && !is_numeric($cache)){  
    545.             if (is_array ( $cache ) && isset($cache['cache_refresh'])) {  
    546.                 $cache_refresh = $cache ['cache_refresh'];  
    547.             } elseif (is_object ( $cache ) && isset($cache['cache_refresh'])) {  
    548.                 $cache_refresh = $cache->cache_refresh;  
    549.             }  
    550.         }  
    551.           
    552.         return $cache_refresh;  
    553.     }  
    554.       
    555.     /** 
    556.      * 写入日志 
    557.      * @param string $message 
    558.      * @param string $level 
    559.      * @return boolean 
    560.      */  
    561.     protected function _log($message, $level=WXAPI_LOG_INFO){  
    562.         if($this->Config->Log){  
    563.             static $aLogLevelMaps = array(  
    564.                     WXAPI_LOG_EMERG => 0,  
    565.                     WXAPI_LOG_ALERT => 1,  
    566.                     WXAPI_LOG_CRIT => 2,  
    567.                     WXAPI_LOG_ERR => 3,  
    568.                     WXAPI_LOG_WARN => 4,  
    569.                     WXAPI_LOG_NOTICE => 5,  
    570.                     WXAPI_LOG_INFO => 6,  
    571.                     WXAPI_LOG_DEBUG => 7,  
    572.             );  
    573.               
    574.             if($this->Config->LogLevel && $aLogLevelMaps[$level]>$aLogLevelMaps[$this->Config->LogLevel]){  
    575.                 return false;  
    576.             }  
    577.               
    578.             return $this->_run_callback($this->Config->Log, array($message, $level));  
    579.         }else{  
    580.             return false;  
    581.         }  
    582.     }  
    583.       
    584.       
    585.     /** 
    586.      * 写入支付日志 
    587.      * @param string $message 
    588.      * @param string $level 
    589.      * @return boolean 
    590.      */  
    591.     protected function _logpay($message, $level=WXAPI_LOG_INFO){  
    592.         if($this->Config->PayLog){  
    593.             static $aLogLevelMaps = array(  
    594.                     WXAPI_LOG_EMERG => 0,  
    595.                     WXAPI_LOG_ALERT => 1,  
    596.                     WXAPI_LOG_CRIT => 2,  
    597.                     WXAPI_LOG_ERR => 3,  
    598.                     WXAPI_LOG_WARN => 4,  
    599.                     WXAPI_LOG_NOTICE => 5,  
    600.                     WXAPI_LOG_INFO => 6,  
    601.                     WXAPI_LOG_DEBUG => 7,  
    602.             );  
    603.       
    604.             if($this->Config->PayLogLevel && $aLogLevelMaps[$level]>$aLogLevelMaps[$this->Config->PayLogLevel]){  
    605.                 return false;  
    606.             }  
    607.       
    608.             return $this->_run_callback($this->Config->PayLog, array($message, $level));  
    609.         }else{  
    610.             return false;  
    611.         }  
    612.     }  
    613.       
    614.     /** 
    615.      * 判断是否微信媒体文件id 
    616.      * @param string $mediaid 
    617.      * @return boolean 
    618.      */  
    619.     protected function _isMediaId($mediaid){  
    620.         // aSeyL8Ym_0mu3u1qeHixvCe54XU-b8teahDXHdYl1tOB_1mgyUxJgj0A8CJZRNzl  
    621.         //return is_file($mediaid)?false:true;  
    622.         if(preg_match('/.[a-z0-9]{1,4}$/i', $mediaid)){  
    623.             return false;  
    624.         }else{  
    625.             return true;  
    626.         }  
    627.     }  
    628.       
    629.     /** 
    630.      * 清空微信API所有缓存数据 
    631.      *  
    632.      * @return bool 
    633.      */  
    634.     public function clearCache(){  
    635.   
    636.         $this->_log("START Clear Cache...", WXAPI_LOG_INFO);  
    637.         if(!$this->Config->Cache || !$this->Config->CacheSaveIndex){  
    638.             $this->_log("Skipped, Cache or Save Cache index is disabled!", WXAPI_LOG_INFO);  
    639.             return false;  
    640.         }  
    641.           
    642.         // 取缓存的索引  
    643.         $index_cache_id = $this->Config->CacheSaveIndex;  
    644.         if(!($index_cache_data=$this->_cache($index_cache_id))){  
    645.             $this->_log("Skipped, Cache Index is Empty!", WXAPI_LOG_INFO);  
    646.             return false;  
    647.         }  
    648.           
    649.         $clear_succ = true;  
    650.           
    651.         foreach($index_cache_data as $cache_id=>$d){  
    652.             $succ = $this->_cache($cache_id, false, false);  
    653.             $this->_log("Delete cache id: " . $cache_id . " " . ($succ?'Succ':'Failed') . '!', WXAPI_LOG_DEBUG);  
    654.               
    655.             $clear_succ = $succ && $clear_succ;  
    656.         }  
    657.           
    658.         // 删除索引自身  
    659.         $succ = $this->_cache($index_cache_id, false, false);  
    660.         $clear_succ = $succ && $clear_succ;  
    661.           
    662.         $this->_log("Delete Index Cache Id: " . $index_cache_id . " " . ($succ?'Succ':'Failed') . '!', WXAPI_LOG_INFO);  
    663.           
    664.         $this->_log("END Clear Cache, "  . ($clear_succ?'Succ':'Failed') . '!', WXAPI_LOG_INFO);  
    665.           
    666.         return $clear_succ;  
    667.     }  
    668. }  

    WeixinReceive.class.php  微信接口接收类
     
    [php] view plain copy
    1. <?php  
    2. /** 
    3.  * 微信API 接收接口 
    4.  *  
    5.  * PHP version 5 
    6.  *  
    7.  * @category    Lib 
    8.  * @package     COM 
    9.  * @subpackage  GZNC 
    10.  * @author      zhongyiwen 
    11.  * @version     SVN: $Id: WeixinReceive.class.php 10 2013-10-08 01:34:05Z zhongyw $ 
    12.  */  
    13.   
    14. class WeixinReceive extends WeixinApi{  
    15.     protected $_rawget = NULL;  
    16.     protected $_rawpost = NULL;  
    17.       
    18.     protected $_postData = NULL;  
    19.     protected $_getData = NULL;  
    20.       
    21.     protected $_postObj = NULL; // 兼容旧程序  
    22.     protected $_getObj = NULL; // 兼容旧程序  
    23.       
    24.     protected $_responseMsg;  
    25.     protected $_responseObj;  
    26.       
    27.     /** 
    28.      * 消息体加密模式 
    29.      * @var string 
    30.      */  
    31.     protected $_msgEncodingMode = NULL;  
    32.       
    33.     /** 
    34.      * 消息加密私钥 
    35.      * @var string 
    36.      */  
    37.     protected $_msgEncodingKey = NULL;  
    38.       
    39.     /** 
    40.      * 原始加密消息 
    41.      * @var string 
    42.      */  
    43.     protected $_msgEncrypt = NULL;  
    44.       
    45.     /** 
    46.      * 解密后的消息原文 
    47.      * @var string 
    48.      */  
    49.     protected $_msgDecrypt = NULL;  
    50.       
    51.     /** 
    52.      * 解密后的消息数组 
    53.      * @var array 
    54.      */  
    55.     protected $_msgData = NULL;  
    56.       
    57.       
    58.       
    59.     /** 
    60.      * 检查消息签名 
    61.      * @param object $getObj 
    62.      * @return boolean 成功返回true,失败返回false 
    63.      */  
    64.     protected function _checkSignature($getData)  
    65.     {  
    66.         $signature = $getData['signature'];  
    67.         $timestamp = $getData['timestamp'];  
    68.         $nonce = $getData['nonce'];  
    69.       
    70.         $token = $this->Config->AppToken;  
    71.         $tmpArr = array($token, $timestamp, $nonce);  
    72.         sort($tmpArr, SORT_STRING);  
    73.         $tmpStr = implode( $tmpArr );  
    74.         $tmpStr = sha1( $tmpStr );  
    75.       
    76.         if( $tmpStr == $signature ){  
    77.             return true;  
    78.         }else{  
    79.             return false;  
    80.         }  
    81.     }  
    82.       
    83.     /** 
    84.      * 判断消息加密模式 
    85.      * @param object $getObj 
    86.      * @param object $postObj 
    87.      * @return string|false 
    88.      */  
    89.     protected function _checkEncodingMode($getData, $postData){  
    90.         if(!is_null($this->_msgEncodingMode)){  
    91.             return $this->_msgEncodingMode;  
    92.         }  
    93.           
    94.         if(empty($getData['encrypt_type']) || !strcasecmp($getData['encrypt_type'], 'raw')){  
    95.             $this->_msgEncodingMode = WXAPI_APP_ENCODING_CLEAR;  
    96.         }elseif(strlen($getData['msg_signature']) && !strcasecmp($getData['encrypt_type'], 'aes')){  
    97.             if(!empty($postData['MsgType']) && !empty($postData['FromUserName'])){  
    98.                 $this->_msgEncodingMode =  WXAPI_APP_ENCODING_COMPAT;  
    99.             }else{  
    100.                 $this->_msgEncodingMode =  WXAPI_APP_ENCODING_SECURE;  
    101.             }  
    102.         }else{  
    103.             $this->_msgEncodingMode = false;  
    104.         }  
    105.           
    106.         return $this->_msgEncodingMode;  
    107.     }  
    108.       
    109.     protected function _postData(){  
    110.         if(!is_null($this->_postData)){  
    111.             return $this->_postData;  
    112.         }  
    113.           
    114.         $this->_rawpost = file_get_contents("php://input");  
    115.           
    116.         if(!empty($this->_rawpost)){  
    117.             $postObj = simplexml_load_string(trim($this->_rawpost), 'SimpleXMLElement', LIBXML_NOCDATA);  
    118.               
    119.             $this->_postData = WeixinApi_Kit::get_object_vars_final($postObj);  
    120.               
    121.             // 2015.3.3 zhongyw 必须从postData转为object  
    122.             // simplexml_load_string()返回的为SimpleXMLElement Object,而不是stdClass Object,   
    123.             // 用is_string($postObj->FromUserName)判断时会返回false  
    124.             $this->_postObj = (object) $this->_postData; // 兼容旧程序  
    125.         }else{  
    126.             $this->_postData = false;  
    127.             $this->_postObj = false;  
    128.         }  
    129.           
    130.         return $this->_postData;  
    131.           
    132.     }  
    133.       
    134.     protected function _getData(){  
    135.         if(!is_null($this->_getData)){  
    136.             return $this->_getData;  
    137.         }  
    138.           
    139.         $this->_rawget = $_GET;  
    140.         if ($this->_rawget) {  
    141.             $getData = array (  
    142.                     'signature' => $_GET ["signature"],  
    143.                     'timestamp' => $_GET ["timestamp"],  
    144.                     'nonce' => $_GET ["nonce"]   
    145.             );  
    146.               
    147.             if (isset ( $_GET ['echostr'] )) { $getData ['echostr'] = $_GET ['echostr']; }  
    148.             if (isset ( $_GET ['encrypt_type'] )) { $getData ['encrypt_type'] = $_GET ['encrypt_type']; }  
    149.             if (isset ( $_GET ['msg_signature'] )) { $getData ['msg_signature'] = $_GET ['msg_signature']; }  
    150.               
    151.             $this->_getData = $getData;  
    152.               
    153.             // 兼容旧程序  
    154.             $this->_getObj = ( object ) $getData;  
    155.         }else{  
    156.             $this->_getData = false;  
    157.               
    158.             $this->_getObj = false;  
    159.         }  
    160.           
    161.         return $this->_getData;  
    162.     }  
    163.       
    164.     /** 
    165.      * 运行接收 
    166.      * @param mixed $responseObj 响应对象,可以传回调函数 
    167.      */  
    168.     public function run($responseObj=NULL){  
    169.         $request_url = WeixinApi_Kit::get_request_url();  
    170.         $client_ip = WeixinApi_Kit::get_client_ip();  
    171.           
    172.         $this->_log("--------------------------------------------------------");  
    173.         $this->_log("Received new request from {$client_ip}", WXAPI_LOG_INFO);  
    174.         $this->_log("Request URL: {$request_url}", WXAPI_LOG_INFO);  
    175.           
    176.         $this->_log("Get: " . print_r($_GET, true), WXAPI_LOG_DEBUG);  
    177.         $this->_log("Post: " . print_r($_POST, true), WXAPI_LOG_DEBUG);  
    178.           
    179.         $getData = $this->_getData();  
    180.           
    181.         // 验证签名  
    182.         if(!$getData || !$this->_checkSignature($getData)){  
    183.             // invalid request  
    184.             // log it? or do other things  
    185.             $this->_log("Bad Request, Check Signature Failed!", WXAPI_LOG_ERR);  
    186.             return false;  
    187.         }  
    188.           
    189.         $postData = $this->_postData();  
    190.           
    191.         // 消息体是否为空?  
    192.         if(false==$postData){  
    193.             $this->_log("Msg Body is Empty!", WXAPI_LOG_ERR);  
    194.             return false;  
    195.         }  
    196.           
    197.         $this->_log ( "rawPost: " . $this->_rawpost, WXAPI_LOG_DEBUG );  
    198.         $this->_log ( "postData: " . print_r ( $postData, true ), WXAPI_LOG_DEBUG );  
    199.           
    200.         // 判断消息加密模式  
    201.         $encodingMode = $this->_checkEncodingMode($getData, $postData);  
    202.         if(false==$encodingMode){  
    203.             $this->_log("Check Msg Encoding Mode Failed!", WXAPI_LOG_ERR);  
    204.             return false;  
    205.         }  
    206.           
    207.         $this->_log("MSG Encoding Mode is: " . $encodingMode, WXAPI_LOG_DEBUG);  
    208.           
    209.         // 解密消息  
    210.         switch($encodingMode){  
    211.             case WXAPI_APP_ENCODING_SECURE:  
    212.                 if(false===$this->_decodeMessage()){  
    213.                     $this->_log("Bad Request, Decode Message Failed!", WXAPI_LOG_ERR);  
    214.                     return false;  
    215.                 }else{  
    216.                     $this->_log("Decode Message Succ!", WXAPI_LOG_INFO);  
    217.                 }  
    218.                 break;  
    219.                   
    220.             case WXAPI_APP_ENCODING_COMPAT:  
    221.                 if(false===$this->_decodeMessage()){  
    222.                     $this->_log("Decode Message Failed!", WXAPI_LOG_ERR);  
    223.                 }else{  
    224.                     $this->_log("Decode Message Succ!", WXAPI_LOG_INFO);  
    225.                 }  
    226.                   
    227.                 break;  
    228.                   
    229.             default:  
    230.                 // DO NOTHING  
    231.                 break;  
    232.         }  
    233.   
    234.         if (empty ( $responseObj )) {  
    235.             $responseObj = $this->Config->Response;  
    236.         }  
    237.           
    238.         // get response  
    239.         $response = $this->_responseMsg = $this->_response ( $responseObj );  
    240.           
    241.         if ($response === false) {  
    242.             $this->_log ( "No Reponse Sent!", WXAPI_LOG_INFO );  
    243.               
    244.             // save message  
    245.             $this->_saveMessage ();  
    246.               
    247.             return false;  
    248.         }  
    249.           
    250.         // echo response  
    251.         echo $response;  
    252.         flush ();  
    253.           
    254.         // log  
    255.         $this->_log ( "Succ! Send Response: " . $response, WXAPI_LOG_INFO );  
    256.           
    257.         // save message  
    258.         $this->_saveMessage ();  
    259.           
    260.         // save response  
    261.         $this->_saveResponse ( $this->_responseObj );  
    262.           
    263.         return true;  
    264.     }  
    265.       
    266.     protected function _response($responseObj){  
    267.         if(is_object($responseObj)){  
    268.             $callback = array($responseObj, 'run');  
    269.         }else{  
    270.             $callback = $responseObj;  
    271.         }  
    272.           
    273.         return $this->_run_callback($callback, array($this), $this->_responseObj);  
    274.     }  
    275.       
    276.     /** 
    277.      * 保存消息 
    278.      * @return mixed|boolean 
    279.      */  
    280.     protected function _saveMessage(){  
    281.         if($this->Config->SaveMessage){  
    282.             return $this->_run_callback($this->Config->SaveMessage, array($this));  
    283.         }else{  
    284.             return false;  
    285.         }  
    286.     }  
    287.       
    288.     /** 
    289.      * 保存回复 
    290.      * @param mixed $responseObj 
    291.      * @return mixed|boolean 
    292.      */  
    293.     protected function _saveResponse($responseObj){  
    294.         if($this->Config->SaveResponse){  
    295.             return $this->_run_callback($this->Config->SaveResponse, array($this, $responseObj));  
    296.         }else{  
    297.             return false;  
    298.         }  
    299.     }  
    300.       
    301.     public function __get($c){  
    302.         if(substr($c, 0, 1)!='_'){  
    303.             if(in_array($c, array('Http'))){  
    304.                 return parent::__get($c);  
    305.             }else{  
    306.                 $n = '_' . $c;  
    307.                 return $this->$n;  
    308.             }  
    309.         }  
    310.     }  
    311.       
    312.     public function __isset($c){  
    313.         if(substr($c, 0, 1)!='_'){  
    314.             if(in_array($c, array('Http'))){  
    315.                 return parent::__isset($c);  
    316.             }else{  
    317.                 $n = '_' . $c;  
    318.                 return isset($this->$n);  
    319.             }  
    320.         }  
    321.     }  
    322.       
    323.     /** 
    324.      * 获取openid 
    325.      * @return string 
    326.      */  
    327.     public function parse_openid(){  
    328.         if(isset($this->_postObj->FromUserName) && !empty($this->_postObj->FromUserName)){  
    329.             return $this->_postObj->FromUserName;  
    330.         }else{  
    331.             return null;  
    332.         }  
    333.     }  
    334.       
    335.     /** 
    336.      * 对密文消息进行解密 
    337.      * @param string $msg_encrypt 需要解密的密文 
    338.      * @param string $encodingkey 加密私钥 
    339.      * @return string|false 解密得到的明文,失败返回flase 
    340.      */  
    341.     protected function _decryptMsg($msg_encrypt, $encodingkey=NULL)  
    342.     {  
    343.         $AESKey = base64_decode(($encodingkey?$encodingkey:$this->Config->AppEncodingAESKey) . "=");  
    344.       
    345.         //使用BASE64对需要解密的字符串进行解码  
    346.         $ciphertext_dec = base64_decode($msg_encrypt);  
    347.       
    348.         $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');  
    349.         if(false===$module){  
    350.             return $this->_throw_exception(  
    351.                     "Cann't open an encryption descriptor"  
    352.                     , WXAPI_ERR_BAD_ENCRYPT  
    353.                     , $msg_encrypt  
    354.                     , __FILE__, __LINE__  
    355.             );  
    356.         }  
    357.       
    358.         $iv = substr($AESKey, 0, 16);  
    359.         $init = mcrypt_generic_init($module, $AESKey, $iv);  
    360.         if(false===$init){  
    361.             return $this->_throw_exception(  
    362.                     "Cann't initialize buffers for encryption"  
    363.                     , WXAPI_ERR_BAD_ENCRYPT  
    364.                     , array('msg_encrypt' => $msg_encrypt, 'mcrypt_generic_init return' => $init)  
    365.                     , __FILE__, __LINE__  
    366.             );  
    367.         }elseif(-3==$init){  
    368.             return $this->_throw_exception(  
    369.                     "the key length was incorrect"  
    370.                     , WXAPI_ERR_BAD_ENCRYPT  
    371.                     , array('msg_encrypt' => $msg_encrypt, 'mcrypt_generic_init return' => $init)  
    372.                     , __FILE__, __LINE__  
    373.             );  
    374.         }elseif(-4==$init){  
    375.             return $this->_throw_exception(  
    376.                     "there was a memory allocation problem"  
    377.                     , WXAPI_ERR_BAD_ENCRYPT  
    378.                     , array('msg_encrypt' => $msg_encrypt, 'mcrypt_generic_init return' => $init)  
    379.                     , __FILE__, __LINE__  
    380.             );  
    381.         }elseif($init<0){  
    382.             return $this->_throw_exception(  
    383.                     "an unknown error occurred when initialize buffers for encryption"  
    384.                     , WXAPI_ERR_BAD_ENCRYPT  
    385.                     , array('msg_encrypt' => $msg_encrypt, 'mcrypt_generic_init return' => $init)  
    386.                     , __FILE__, __LINE__  
    387.             );  
    388.         }  
    389.       
    390.         //解密  
    391.         $decrypted = mdecrypt_generic($module, $ciphertext_dec);  
    392.         mcrypt_generic_deinit($module);  
    393.         mcrypt_module_close($module);  
    394.           
    395.         if(!$decrypted){  
    396.             return "";  
    397.         }  
    398.               
    399.         // 去除补位字符  
    400.         $result = WeixinApi_Kit::pkcs7_decode( $decrypted, 32 );  
    401.         // 去除16位随机字符串,网络字节序和AppId  
    402.         if (strlen ( $result ) < 16){  
    403.             return "";  
    404.         }  
    405.           
    406.         $content = substr ( $result, 16, strlen ( $result ) );  
    407.         $len_list = unpack ( "N", substr ( $content, 0, 4 ) );  
    408.         $xml_len = $len_list [1];  
    409.         $xml_content = substr ( $content, 4, $xml_len );  
    410.       
    411.         return $xml_content;  
    412.     }  
    413.       
    414.     /** 
    415.      * 返回微信发过来的加密消息 
    416.      * @return string 
    417.      */  
    418.     protected function _getMsgEncrypt(){  
    419.         if($this->_msgEncrypt){  
    420.             return $this->_msgEncrypt;  
    421.         }  
    422.       
    423.         if(!empty($this->_getData['echostr'])){  
    424.             $this->_msgEncrypt = $this->_getData['echostr'];  
    425.         }else{  
    426.             $this->_msgEncrypt = $this->_postData['Encrypt'];  
    427.         }  
    428.       
    429.         return $this->_msgEncrypt;  
    430.     }  
    431.       
    432.     protected function _msgData() {  
    433.         if (! is_null ( $this->_msgData )) {  
    434.             return $this->_msgData;  
    435.         }  
    436.       
    437.         $this->_msgData = false;  
    438.       
    439.         $msg_encrypt = $this->_getMsgEncrypt ();  
    440.         if ($msg_encrypt) {  
    441.             if(!empty($this->_getData['echostr'])){  
    442.                 $this->_msgData = array();  
    443.             }else{  
    444.                 $xml_content = false;  
    445.                   
    446.                 $encodingkey = $this->Config->AppEncodingAESKey;  
    447.                 if($encodingkey){  
    448.                     $xml_content = $this->_decryptMsg ( $msg_encrypt, $encodingkey );  
    449.                     if($xml_content){  
    450.                         $this->_msgEncodingKey = $encodingkey;  
    451.                         $this->_log("AES Key: Decode Succ! ", WXAPI_LOG_DEBUG);  
    452.                     }else{  
    453.                         $this->_log("AES Key: Decode Failed!", WXAPI_LOG_DEBUG);  
    454.                     }  
    455.                 }else{  
    456.                     $this->_log("Encoding AES Key is empty", WXAPI_LOG_DEBUG);  
    457.                 }  
    458.                   
    459.                 // 尝试旧密钥  
    460.                 if(!$xml_content && ($encodingkey = $this->Config->AppEncodingOLDKey)){  
    461.                     $xml_content = $this->_decryptMsg ( $msg_encrypt, $encodingkey );  
    462.                       
    463.                     $this->_log("Try to apply OLD Key", WXAPI_LOG_DEBUG);  
    464.                       
    465.                     if($xml_content){  
    466.                         $this->_msgEncodingKey = $encodingkey;  
    467.                         $this->_log("OLD Key: Decode Succ! ", WXAPI_LOG_DEBUG);  
    468.                     }else{  
    469.                         $this->_log("OLD Key: Decode Failed!", WXAPI_LOG_DEBUG);  
    470.                     }  
    471.                 }  
    472.                   
    473.                 if($xml_content){  
    474.                     $this->_msgDecrypt = $xml_content;  
    475.                       
    476.                     import ( 'COM.GZNC.WeixinApi.WeixinApi_Kit' );  
    477.                     $postObj = simplexml_load_string ( $xml_content, 'SimpleXMLElement', LIBXML_NOCDATA );  
    478.                     $this->_msgData = WeixinApi_Kit::get_object_vars_final ( $postObj );  
    479.                       
    480.                     $this->_log('Decoded MSG XML: ' . $this->_msgDecrypt, WXAPI_LOG_DEBUG);  
    481.                     $this->_log('Decoded MSG DATA: ' . print_r($this->_msgData, true), WXAPI_LOG_DEBUG);  
    482.                 }  
    483.             }  
    484.         }  
    485.           
    486.         return $this->_msgData;  
    487.     }  
    488.       
    489.     protected function _decodeMessage(){  
    490.         if(false===$this->_msgData()){  
    491.             return false;  
    492.         }  
    493.       
    494.         // 兼容旧程序  
    495.         $this->_postData = array_merge($this->_postData, $this->_msgData);  
    496.         $this->_postObj = (object) $this->_postData;  
    497.       
    498.         return true;  
    499.     }  
    500.       
    501.       
    502. }  

    WeixinResponse.class.php  微信接口响应类
     
    [php] view plain copy
    1. <?php  
    2. /** 
    3.  * 微信API 响应接口 
    4.  *  
    5.  * PHP version 5 
    6.  *  
    7.  * @category    Lib 
    8.  * @package     COM 
    9.  * @subpackage  GZNC 
    10.  * @author      zhongyiwen 
    11.  * @version     SVN: $Id: WeixinResponse.class.php 10 2013-10-08 01:34:05Z zhongyw $ 
    12.  */  
    13.   
    14. class WeixinResponse extends WeixinApi{  
    15.     const AS_ECHO = 'ECHO'; // ECHO消息  
    16.     const AS_EMPTY = 'EMPTY'; // 空消息  
    17.     const AS_COMMAND = 'COMMAND'; // 指令消息  
    18.     const AS_SUBSCRIBE = 'SUBSCRIBE'; // 订阅消息  
    19.     const AS_UNSUBSCRIBE = 'UNSUBSCRIBE'; // 取消订阅消息  
    20.     const AS_SCAN = 'SCAN'; // 扫描消息  
    21.     const AS_CLICK = 'CLICK'; // 点击菜单拉取消息事件  
    22.     const AS_VIEW = 'VIEW'; // 点击菜单跳转链接事件  
    23.     const AS_SCANCODE_PUSH = 'SCANCODE_PUSH'; // 扫码推事件  
    24.     const AS_SCANCODE_WAITMSG = 'AS_SCANCODE_WAITMSG'; // 扫码推事件且弹出“消息接收中”提示框  
    25.     const AS_PIC_SYSPHOTO = 'pic_sysphoto'; // 弹出系统拍照发图  
    26.     const AS_PIC_PHOTO_OR_ALBUM = 'PIC_PHOTO_OR_ALBUM'; // 弹出拍照或者相册发图  
    27.     const AS_PIC_WEIXIN = 'pic_weixin'; // 弹出微信相册发图器  
    28.     const AS_LOCATION_SELECT = 'location_select'; // 弹出地理位置选择器  
    29.     const AS_LOCATION = 'LOCATION'; // 地理位置消息  
    30.     const AS_MESSAGE = 'MESSAGE'; // 普通消息  
    31.     const AS_MASSSENDJOBFINISH = 'MASSSENDJOBFINISH'; // 群发消息  
    32.     const AS_TEMPLATESENDJOBFINISH = 'TEMPLATESENDJOBFINISH'; // 模板消息  
    33.       
    34.     const AS_UNKNOWN = 'UNKNOWN'; // 未知消息  
    35.       
    36.     protected $_responseType; // 响应类型,对应上面的常量  
    37.     protected $_responseKey; // 响应值,如菜单点击,值为菜单Key  
    38.     protected $_responseContent; // 响应的原始数据  
    39.     protected $_responseMessage; // 响应输出消息数据(数组)  
    40.     protected $_responseMedia; // 响应输出的媒体文件  
    41.   
    42.     /** 
    43.      * 运行 
    44.      * @param object $receiveObj 接收对象 
    45.      */  
    46.     public function run($receiveObj){  
    47.         try{  
    48.             return $this->_dispatchResponse($receiveObj);  
    49.         }catch (Exception $e){  
    50.             return false;  
    51.         }  
    52.     }  
    53.       
    54.     /** 
    55.      * 分发响应 
    56.      * 判断响应类型,并返回相应的响应内容 
    57.      * @param object $receiveObj 接收对象 
    58.      */  
    59.     protected function _dispatchResponse($receiveObj){  
    60.         // 可以直接判定消息类别,不需要依赖外部配置数据  
    61.         if($this->_isEcho($receiveObj)){  
    62.             $this->_responseType = WeixinResponse::AS_ECHO;  
    63.             $this->_responseKey = '';  
    64.             $this->_responseContent = $this->_responseEcho($receiveObj);  
    65.             $msgFormat = 'raw';  
    66.         }elseif($this->_isEmpty($receiveObj)){  
    67.             $this->_responseType = WeixinResponse::AS_EMPTY;  
    68.             $this->_responseKey = '';  
    69.             $this->_responseContent = $this->_responseEmpty($receiveObj);  
    70.             $msgFormat = 'raw';  
    71.         }elseif(($key=$this->_isView($receiveObj))){  
    72.             $this->_responseType = WeixinResponse::AS_VIEW;  
    73.             $this->_responseKey = $key===true?'':$key;  
    74.             $this->_responseContent = $this->_responseView($receiveObj);  
    75.             $msgFormat = 'xml';  
    76.         }  
    77.           
    78.         // 事件,需要加载外部配置数据  
    79.         // 注意:要先判断订阅事件,避免缓存误判  
    80.         elseif(($key=$this->_isSubscribe($receiveObj))){  
    81.             $this->_responseType = WeixinResponse::AS_SUBSCRIBE;  
    82.             $this->_responseKey = $key===true?'':$key;  
    83.             $this->_responseContent = $this->_responseSubscribe($receiveObj);  
    84.             $msgFormat = 'xml';  
    85.         }elseif(($key=$this->_isUnsubscribe($receiveObj))){  
    86.             $this->_responseType = WeixinResponse::AS_UNSUBSCRIBE;  
    87.             $this->_responseKey = $key===true?'':$key;  
    88.             $this->_responseContent = $this->_responseUnsubscribe($receiveObj);  
    89.             $msgFormat = 'xml';  
    90.         }elseif(($key=$this->_isClick($receiveObj))){   
    91.             $this->_responseType = WeixinResponse::AS_CLICK;  
    92.             $this->_responseKey = $key===true?'':$key;  
    93.             $this->_responseContent = $this->_responseClick($receiveObj);  
    94.             $msgFormat = 'xml';  
    95.         }elseif(($key=$this->_isScanCodePush($receiveObj))){   
    96.             $this->_responseType = WeixinResponse::AS_SCANCODE_PUSH;  
    97.             $this->_responseKey = $key===true?'':$key;  
    98.             $this->_responseContent = $this->_responseScancodePush($receiveObj);  
    99.             $msgFormat = 'xml';  
    100.         }elseif(($key=$this->_isScanCodeWaitMsg($receiveObj))){   
    101.             $this->_responseType = WeixinResponse::AS_SCANCODE_WAITMSG;  
    102.             $this->_responseKey = $key===true?'':$key;  
    103.             $this->_responseContent = $this->_responseScanCodeWaitMsg($receiveObj);  
    104.             $msgFormat = 'xml';  
    105.         }elseif(($key=$this->_isPicSysPhoto($receiveObj))){   
    106.             $this->_responseType = WeixinResponse::AS_PIC_SYSPHOTO;  
    107.             $this->_responseKey = $key===true?'':$key;  
    108.             $this->_responseContent = $this->_responsePicSysPhoto($receiveObj);  
    109.             $msgFormat = 'xml';  
    110.         }elseif(($key=$this->_isPicPhotoOrAlbum($receiveObj))){   
    111.             $this->_responseType = WeixinResponse::AS_PIC_PHOTO_OR_ALBUM;  
    112.             $this->_responseKey = $key===true?'':$key;  
    113.             $this->_responseContent = $this->_responsePicPhotoOrAlbum($receiveObj);  
    114.             $msgFormat = 'xml';  
    115.         }elseif(($key=$this->_isPicWeixin($receiveObj))){   
    116.             $this->_responseType = WeixinResponse::AS_PIC_WEIXIN;  
    117.             $this->_responseKey = $key===true?'':$key;  
    118.             $this->_responseContent = $this->_responsePicWeixin($receiveObj);  
    119.             $msgFormat = 'xml';  
    120.         }elseif(($key=$this->_isLocationSelect($receiveObj))){   
    121.             $this->_responseType = WeixinResponse::AS_LOCATION_SELECT;  
    122.             $this->_responseKey = $key===true?'':$key;  
    123.             $this->_responseContent = $this->_responseLocationSelect($receiveObj);  
    124.             $msgFormat = 'xml';  
    125.         }elseif(($key=$this->_isScan($receiveObj))){  
    126.             $this->_responseType = WeixinResponse::AS_SCAN;  
    127.             $this->_responseKey = $key===true?'':$key;  
    128.             $this->_responseContent = $this->_responseScan($receiveObj);  
    129.             $msgFormat = 'xml';  
    130.         }elseif(($key=$this->_isLocation($receiveObj))){  
    131.             $this->_responseType = WeixinResponse::AS_LOCATION;  
    132.             $this->_responseKey = $key===true?'':$key;  
    133.             $this->_responseContent = $this->_responseLocation($receiveObj);  
    134.             $msgFormat = 'xml';  
    135.         }elseif(($key=$this->_isMassSendJobFinish($receiveObj))){  
    136.             $this->_responseType = WeixinResponse::AS_MASSSENDJOBFINISH;  
    137.             $this->_responseKey = $key===true?'':$key;  
    138.             $this->_responseContent = $this->_responseMassSendJobFinish($receiveObj);  
    139.             $msgFormat = 'xml';  
    140.         }elseif(($key=$this->_isTemplateSendJobFinish($receiveObj))){  
    141.             $this->_responseType = WeixinResponse::AS_TEMPLATESENDJOBFINISH;  
    142.             $this->_responseKey = $key===true?'':$key;  
    143.             $this->_responseContent = $this->_responseTemplateSendJobFinish($receiveObj);  
    144.             $msgFormat = 'xml';  
    145.         }  
    146.           
    147.         // 文本消息  
    148.         elseif(($key=$this->_isCommand($receiveObj))){  
    149.             $this->_responseType = WeixinResponse::AS_COMMAND;  
    150.             $this->_responseKey = $key===true?'':$key;  
    151.             $this->_responseContent = $this->_responseCommand($receiveObj);  
    152.             $msgFormat = 'xml';  
    153.         }elseif(($key=$this->_isMessage($receiveObj))){  
    154.             $this->_responseType = WeixinResponse::AS_MESSAGE;  
    155.             $this->_responseKey = $key===true?'':$key;  
    156.             $this->_responseContent = $this->_responseMessage($receiveObj);  
    157.             $msgFormat = 'xml';  
    158.         }  
    159.           
    160.         // 未知,可能是新类型  
    161.         else{  
    162.             $this->_responseType = WeixinResponse::AS_UNKNOWN;  
    163.             $this->_responseKey = '';  
    164.             $this->_responseContent = $this->_responseUnknown($receiveObj);  
    165.             $msgFormat = 'raw';  
    166.         }  
    167.           
    168.         if($this->_responseContent===false){  
    169.             // 出错:未配置对事件或消息的响应  
    170.             return false;  
    171.         }  
    172.           
    173.         $this->_log("ResponseType: " . $this->_responseType, WXAPI_LOG_DEBUG);  
    174.         $this->_log("ResponseKey: " . $this->_responseKey, WXAPI_LOG_DEBUG);  
    175.         $this->_log("ResponseContent: " . print_r($this->_responseContent, true), WXAPI_LOG_DEBUG);  
    176.           
    177.         $this->_responseMessage = $this->_createResponseMessage(  
    178.                 $receiveObj,  
    179.                 $this->_responseContent,  
    180.                 $msgFormat  
    181.         );  
    182.   
    183.         $this->_log("Generated Response Message: " . print_r($this->_responseMessage, true), WXAPI_LOG_DEBUG);  
    184.           
    185.         return $this->_responseMessage['MsgContent'];  
    186.     }  
    187.       
    188.     /** 
    189.      * 是否空消息 
    190.      * @param object $receiveObj 接收对象 
    191.      * @return boolean 
    192.      */  
    193.     protected function _isEmpty($receiveObj){  
    194.         // 注意:empty($receiveObj->postObj)即使非空也返回true  
    195.         // When using empty() on inaccessible object properties, the __isset() overloading method will be called, if declared.  
    196.           
    197.         $postObj = $receiveObj->postObj;  
    198.         return empty($postObj)?true:false;  
    199.     }  
    200.       
    201.     /** 
    202.      * 是否验证消息 
    203.      * @param object $receiveObj 接收对象 
    204.      * @return boolean 
    205.      */  
    206.     protected function _isEcho($receiveObj){  
    207.         return isset($receiveObj->getObj->echostr) && $receiveObj->getObj->echostr;  
    208.     }  
    209.       
    210.     /** 
    211.      * 是否指令消息 
    212.      * @param object $receiveObj 接收对象 
    213.      * @return string|false 
    214.      */  
    215.     protected function _isCommand($receiveObj){  
    216.         $aMsgTypes = array(  
    217.                 'text'  
    218.         );  
    219.           
    220.         if(!in_array($receiveObj->postObj->MsgType, $aMsgTypes, false)){  
    221.             return false;  
    222.         }  
    223.           
    224.         $command = trim($receiveObj->postObj->Content);  
    225.         if(($c=$this->Config->Command) && !empty($c[$command])){  
    226.             return $command;  
    227.         }else{  
    228.             return false;  
    229.         }  
    230.     }  
    231.       
    232.     /** 
    233.      * 是否事件消息 
    234.      * @param object $receiveObj 接收对象 
    235.      * @return string|false 
    236.      */  
    237.     protected function _isEvent($receiveObj){  
    238.         return !strcasecmp($receiveObj->postObj->MsgType, 'event');  
    239.     }  
    240.       
    241.     /** 
    242.      * 是否订阅事件 
    243.      * @param object $receiveObj 接收对象 
    244.      * @return string|false 
    245.      */  
    246.     protected function _isSubscribe($receiveObj){  
    247.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'subscribe')){  
    248.             return isset($receiveObj->postObj->EventKey) && ($key=(string)$receiveObj->postObj->EventKey)?  
    249.             $key:true;  
    250.         }else{  
    251.             return false;  
    252.         }  
    253.     }  
    254.       
    255.     /** 
    256.      * 是否取消订阅事件 
    257.      * @param object $receiveObj 接收对象 
    258.      * @return boolean 
    259.      */  
    260.     protected function _isUnsubscribe($receiveObj){  
    261.         return $this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'unsubscribe');  
    262.     }  
    263.       
    264.     /** 
    265.      * 是否扫描事件 
    266.      * @param object $receiveObj 接收对象 
    267.      * @return array|false 
    268.      */  
    269.     protected function _isScan($receiveObj){  
    270.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'scan')){  
    271.             return array(  
    272.                     'EventKey' => $receiveObj->postObj->EventKey,  
    273.                     'Ticket' => $receiveObj->postObj->Ticket,  
    274.             );  
    275.         }else{  
    276.             return false;  
    277.         }  
    278.     }  
    279.       
    280.     /** 
    281.      * 是否地理位置消息 
    282.      * @param object $receiveObj 接收对象 
    283.      * @return array|false 
    284.      */  
    285.     protected function _isLocation($receiveObj){  
    286.         if(!strcasecmp($receiveObj->postObj->MsgType, 'location')){  
    287.             return array(  
    288.                     'Latitude' => $receiveObj->postObj->Location_Y,  
    289.                     'Longitude' => $receiveObj->postObj->Location_X,  
    290.                     'Precision' => $receiveObj->postObj->Scale,  
    291.                     'Label' => $receiveObj->postObj->Label,  
    292.             );  
    293.         }elseif($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'LOCATION')){  
    294.             return array(  
    295.                     'Latitude' => $receiveObj->postObj->Latitude,  
    296.                     'Longitude' => $receiveObj->postObj->Longitude,  
    297.                     'Precision' => $receiveObj->postObj->Precision,  
    298.                     'Label' => '',  
    299.             );  
    300.         }else{  
    301.             return false;  
    302.         }  
    303.     }  
    304.       
    305.     /** 
    306.      * 是否点击菜单拉取消息事件 
    307.      * @param object $receiveObj 接收对象 
    308.      * @return string|false 
    309.      */  
    310.     protected function _isClick($receiveObj){  
    311.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'CLICK')){  
    312.             return $receiveObj->postObj->EventKey;  
    313.         }else{  
    314.             return false;  
    315.         }  
    316.     }  
    317.       
    318.     /** 
    319.      * 是否点击菜单跳转链接事件 
    320.      * @param object $receiveObj 接收对象 
    321.      * @return string|false 
    322.      */  
    323.     protected function _isView($receiveObj){  
    324.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'VIEW')){  
    325.             return $receiveObj->postObj->EventKey;  
    326.         }else{  
    327.             return false;  
    328.         }  
    329.     }  
    330.       
    331.     /** 
    332.      * 是否:扫码推事件 
    333.      * @param object $receiveObj 接收对象 
    334.      * @return string|false 
    335.      */  
    336.     protected function _isScanCodePush($receiveObj){  
    337.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'scancode_push')){  
    338.             return $receiveObj->postObj->EventKey;  
    339.         }else{  
    340.             return false;  
    341.         }  
    342.     }  
    343.       
    344.     /** 
    345.      * 是否:扫码推事件且弹出“消息接收中”提示框 
    346.      * @param object $receiveObj 接收对象 
    347.      * @return string|false 
    348.      */  
    349.     protected function _isScanCodeWaitMsg($receiveObj){  
    350.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'scancode_waitmsg')){  
    351.             return $receiveObj->postObj->EventKey;  
    352.         }else{  
    353.             return false;  
    354.         }  
    355.     }  
    356.       
    357.     /** 
    358.      * 是否:弹出系统拍照发图 
    359.      * @param object $receiveObj 接收对象 
    360.      * @return string|false 
    361.      */  
    362.     protected function _isPicSysPhoto($receiveObj){  
    363.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'pic_sysphoto')){  
    364.             return $receiveObj->postObj->EventKey;  
    365.         }else{  
    366.             return false;  
    367.         }  
    368.     }  
    369.       
    370.     /** 
    371.      * 是否:弹出拍照或者相册发图 
    372.      * @param object $receiveObj 接收对象 
    373.      * @return string|false 
    374.      */  
    375.     protected function _isPicPhotoOrAlbum($receiveObj){  
    376.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'pic_photo_or_album')){  
    377.             return $receiveObj->postObj->EventKey;  
    378.         }else{  
    379.             return false;  
    380.         }  
    381.     }  
    382.       
    383.     /** 
    384.      * 是否:弹出微信相册发图器 
    385.      * @param object $receiveObj 接收对象 
    386.      * @return string|false 
    387.      */  
    388.     protected function _isPicWeixin($receiveObj){  
    389.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'pic_weixin')){  
    390.             return $receiveObj->postObj->EventKey;  
    391.         }else{  
    392.             return false;  
    393.         }  
    394.     }  
    395.       
    396.     /** 
    397.      * 是否:弹出地理位置选择器 
    398.      * @param object $receiveObj 接收对象 
    399.      * @return string|false 
    400.      */  
    401.     protected function _isLocationSelect($receiveObj){  
    402.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'location_select')){  
    403.             return $receiveObj->postObj->EventKey;  
    404.         }else{  
    405.             return false;  
    406.         }  
    407.     }  
    408.       
    409.     /** 
    410.      * 是否普通消息 
    411.      * @param object $receiveObj 接收对象 
    412.      * @return string|false 
    413.      */  
    414.     protected function _isMessage($receiveObj){  
    415.         $aMsgTypes = array(  
    416.                 'text', 'image', 'voice', 'video', 'link','shortvideo'  
    417.         );  
    418.           
    419.         return in_array($receiveObj->postObj->MsgType, $aMsgTypes, false)?$receiveObj->postObj->MsgType:false;  
    420.     }  
    421.       
    422.     /** 
    423.      * 是否群发消息通知事件 
    424.      * @param object $receiveObj 接收对象 
    425.      * @return string|false 返回消息id 
    426.      */  
    427.     protected function _isMassSendJobFinish($receiveObj){  
    428.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'MASSSENDJOBFINISH')){  
    429.             return $receiveObj->postObj->MsgID;  
    430.             /* 
    431.             return array( 
    432.                     'MsgID' => $receiveObj->postObj->MsgID, 
    433.                     'Status' => $receiveObj->postObj->Status, 
    434.                     'TotalCount' => $receiveObj->postObj->TotalCount, 
    435.                     'FilterCount' => $receiveObj->postObj->FilterCount, 
    436.                     'SentCount' => $receiveObj->postObj->SentCount, 
    437.                     'ErrorCount' => $receiveObj->postObj->ErrorCount, 
    438.             );*/  
    439.         }else{  
    440.             return false;  
    441.         }  
    442.     }  
    443.       
    444.     /** 
    445.      * 是否模板消息通知事件 
    446.      * @param object $receiveObj 接收对象 
    447.      * @return string|false 返回消息id 
    448.      */  
    449.     protected function _isTemplateSendJobFinish($receiveObj){  
    450.         if($this->_isEvent($receiveObj) && !strcasecmp($receiveObj->postObj->Event, 'TEMPLATESENDJOBFINISH')){  
    451.             return $receiveObj->postObj->MsgID;  
    452.         }else{  
    453.             return false;  
    454.         }  
    455.     }  
    456.       
    457.     /** 
    458.      * 创建响应消息 
    459.      * @param object $receiveObj 接收对象 
    460.      * @param mixed $responseContent 原始响应内容 
    461.      * @param string $msgFormat 消息格式 
    462.      * @return array|false 
    463.      */  
    464.     protected function _createResponseMessage($receiveObj, $responseContent, $msgFormat='xml'){  
    465.       
    466.         if(is_array($responseContent) && !empty($responseContent['Callback'])){  
    467.             $data = $this->_run_callback($responseContent['Callback'], array($receiveObj, $this));  
    468.               
    469.             if($data===false){  
    470.                 $this->_log("Run Callback : " . print_r($responseContent['Callback'], true) . " Failed", WXAPI_LOG_ERR);  
    471.                 return false;  
    472.             }  
    473.   
    474.             if(is_array($data)){  
    475.                 $t = $data;  
    476.                 $responseContent = array(  
    477.                         'MsgType' => $t['MsgType'],  
    478.                         'Content' => $t['Content'],  
    479.                 );  
    480.             }else{  
    481.                 $responseContent['Content'] = $data;  
    482.                 if($responseContent['MsgType']=='callback'){  
    483.                     $responseContent['MsgType'] = 'text';  
    484.                 }  
    485.             }  
    486.         }  
    487.   
    488.         if(is_string($responseContent)){  
    489.             $responseContent = array(  
    490.                     'MsgType' => 'text',  
    491.                     'Content' => $responseContent,  
    492.             );  
    493.         }elseif(!$responseContent['MsgType']){  
    494.             $responseContent['MsgType'] = 'text';  
    495.         }  
    496.           
    497.         if(!$responseContent['Content'] && !strlen($responseContent['Content'])  
    498.         && strcasecmp('transfer_customer_service', $responseContent['MsgType']) // 转发客服消息,允许Content为空     
    499.         ){  
    500.             return false;  
    501.         }  
    502.           
    503.         // 预处理消息  
    504.         if($msgFormat=='xml'){  
    505.             $responseContent = $this->_preprocessResponseMedia($responseContent);  
    506.         }  
    507.   
    508.         $msgContentOutput = self::generateMessage($receiveObj->postData['FromUserName'], $receiveObj->postData['ToUserName'], $responseContent);  
    509.   
    510.         // 根据加密类型生成相应响应消息  
    511.         switch($receiveObj->msgEncodingMode){  
    512.             // 兼容模式  
    513.             case WXAPI_APP_ENCODING_COMPAT:  
    514.                 // 未正确解密,使用明文返回  
    515.                 if(empty($receiveObj->msgEncodingKey)){  
    516.                     $msgEncoding = WXAPI_APP_ENCODING_CLEAR;  
    517.                       
    518.                     $this->_log("Encoding Response Msg in Clear Mode", WXAPI_LOG_DEBUG);  
    519.                     break;  
    520.                 }  
    521.                   
    522.             // 安全模式  
    523.             case WXAPI_APP_ENCODING_SECURE:  
    524.                 $msgContentOriginal = $msgContentOutput;  
    525.                 $msgContentOutput = self::_encrypt_response($msgContentOutput, $receiveObj->msgEncodingKey);  
    526.                 $msgEncoding = WXAPI_APP_ENCODING_SECURE;  
    527.                   
    528.                 $this->_log("Encoding Response Msg in Secure Mode", WXAPI_LOG_DEBUG);  
    529.                 break;  
    530.                   
    531.             // 明文模式  
    532.             case WXAPI_APP_ENCODING_CLEAR:  
    533.             default:  
    534.                 $msgEncoding = WXAPI_APP_ENCODING_CLEAR;  
    535.                   
    536.                 $this->_log("Encoding Response Msg In Clear Mode", WXAPI_LOG_DEBUG);  
    537.                 break;  
    538.         }  
    539.       
    540.         return array(  
    541.                 'MsgType' => $responseContent['MsgType'],  
    542.                 'MsgFormat' => $msgFormat,  
    543.                 'MsgContent' => $msgFormat=='xml'?$msgContentOutput: $responseContent['Content'],  
    544.                 'MsgOriginal' => $msgContentOriginal?$msgContentOriginal:NULL,  
    545.                 'MsgEncoding' => $msgEncoding,  
    546.                 'RawContent' => $responseContent['Content']  
    547.         );  
    548.     }  
    549.       
    550.     /** 
    551.      * 错误响应 
    552.      * @param object $receiveObj 接收对象 
    553.      * @return string 
    554.      */  
    555.     protected function _responseError($receiveObj){  
    556.         return "Error";  
    557.     }  
    558.       
    559.     /** 
    560.      * 未知响应 
    561.      * @param object $receiveObj 接收对象 
    562.      * @return string 
    563.      */  
    564.     protected function _responseUnknown($receiveObj){  
    565.         //return "Unknown";  
    566.     }  
    567.       
    568.     /** 
    569.      * 验证响应 
    570.      * @param object $receiveObj 接收对象 
    571.      * @return string 
    572.      */  
    573.     protected function _responseEcho($receiveObj){  
    574.         return $receiveObj->getObj->echostr;  
    575.     }  
    576.       
    577.     /** 
    578.      * 空消息响应 
    579.      * @param object $receiveObj 接收对象 
    580.      * @return string 
    581.      */  
    582.     protected function _responseEmpty($receiveObj){  
    583.         return "Empty";  
    584.     }  
    585.       
    586.     /** 
    587.      * 指令响应 
    588.      * @param object $receiveObj 接收对象 
    589.      * @return string 
    590.      */  
    591.     protected function _responseCommand($receiveObj){  
    592.         $command = trim($receiveObj->postObj->Content);  
    593.         $settings = $this->Config->getConfig('Command');  
    594.         if(!isset($settings[$command])){  
    595.             return $this->_throw_exception("Command {$command} not configured", WXAPI_ERR_MISS_RESPONSE, '', __FILE__, __LINE__);  
    596.         }  
    597.         $content = $settings[$command];  
    598.         return $content;  
    599.     }  
    600.       
    601.     /** 
    602.      * 事件响应 
    603.      * @param object $receiveObj 接收对象 
    604.      * @return string 
    605.      */  
    606.     protected function _responseEvent($receiveObj){  
    607.         $event = strtolower($receiveObj->postObj->Event);  
    608.   
    609.         $settings = $this->Config->getConfig('Event');  
    610.         if(!isset($settings[$event])){  
    611.             return $this->_throw_exception("Miss resoponse for Event {$event}", WXAPI_ERR_MISS_RESPONSE, '', __FILE__, __LINE__);  
    612.         }  
    613.           
    614.         if(isset($settings[$event]['MsgType'])){  
    615.             $content = $settings[$event];  
    616.         }elseif(isset($receiveObj->postObj->EventKey)){  
    617.             $eventkey = (string) $receiveObj->postObj->EventKey;  
    618.             if(!isset($settings[$event][$eventkey])){  
    619.                 return $this->_throw_exception("Miss response for Event {$event}, Key {$eventkey}", WXAPI_ERR_MISS_RESPONSE, '', __FILE__, __LINE__);  
    620.             }  
    621.               
    622.             $content = $settings[$event][$eventkey];  
    623.         }  
    624.           
    625.         return $content;  
    626.     }  
    627.       
    628.     /** 
    629.      * 订阅事件响应 
    630.      * @param object $receiveObj 接收对象 
    631.      * @return string 
    632.      */  
    633.     protected function _responseSubscribe($receiveObj){  
    634.         return $this->_responseEvent($receiveObj);  
    635.     }  
    636.       
    637.     /** 
    638.      * 取消订阅事件响应 
    639.      * @param object $receiveObj 接收对象 
    640.      * @return string 
    641.      */  
    642.     protected function _responseUnsubscribe($receiveObj){  
    643.         return $this->_responseEvent($receiveObj);  
    644.     }  
    645.       
    646.     /** 
    647.      * 扫描事件响应 
    648.      * @param object $receiveObj 接收对象 
    649.      * @return string 
    650.      */  
    651.     protected function _responseScan($receiveObj){  
    652.         return $this->_responseEvent($receiveObj);  
    653.     }  
    654.       
    655.     /** 
    656.      * 地理位置消息响应 
    657.      * @param object $receiveObj 接收对象 
    658.      * @return string 
    659.      */  
    660.     protected function _responseLocation($receiveObj){  
    661.         if(!strcasecmp($receiveObj->postObj->MsgType, 'event')){  
    662.             return $this->_responseEvent($receiveObj);  
    663.         }else if(!strcasecmp($receiveObj->postObj->MsgType, 'location')){  
    664.             // 普通位置消息  
    665.             // @todo 处理接收到的普通位置消息  
    666.         }  
    667.     }  
    668.       
    669.     /** 
    670.      * 点击菜单拉取消息事件响应 
    671.      * @param object $receiveObj 接收对象 
    672.      * @return string 
    673.      */  
    674.     protected function _responseClick($receiveObj){  
    675.         return $this->_responseEvent($receiveObj);  
    676.     }  
    677.       
    678.     /** 
    679.      * 点击菜单跳转链接事件响应 
    680.      * @param object $receiveObj 接收对象 
    681.      * @return string 
    682.      */  
    683.     protected function _responseView($receiveObj){  
    684.         //return $this->_responseEvent($receiveObj);  
    685.     }  
    686.       
    687.     /** 
    688.      * 响应:扫码推事件 
    689.      * @param object $receiveObj 接收对象 
    690.      * @return string 
    691.      */  
    692.     protected function _responseScanCodePush($receiveObj){  
    693.         return $this->_responseEvent($receiveObj);  
    694.     }  
    695.       
    696.     /** 
    697.      * 响应:扫码推事件且弹出“消息接收中”提示框 
    698.      * @param object $receiveObj 接收对象 
    699.      * @return string 
    700.      */  
    701.     protected function _responseScanCodeWaitMsg($receiveObj){  
    702.         return $this->_responseEvent($receiveObj);  
    703.     }  
    704.       
    705.     /** 
    706.      * 响应:弹出系统拍照发图 
    707.      * @param object $receiveObj 接收对象 
    708.      * @return string 
    709.      */  
    710.     protected function _responsePicSysPhoto($receiveObj){  
    711.         return $this->_responseEvent($receiveObj);  
    712.     }  
    713.       
    714.     /** 
    715.      * 响应:弹出拍照或者相册发图 
    716.      * @param object $receiveObj 接收对象 
    717.      * @return string 
    718.      */  
    719.     protected function _responsePicPhotoOrAlbum($receiveObj){  
    720.         return $this->_responseEvent($receiveObj);  
    721.     }  
    722.       
    723.     /** 
    724.      * 响应:弹出微信相册发图器 
    725.      * @param object $receiveObj 接收对象 
    726.      * @return string 
    727.      */  
    728.     protected function _responsePicWeixin($receiveObj){  
    729.         return $this->_responseEvent($receiveObj);  
    730.     }  
    731.       
    732.     /** 
    733.      * 响应:弹出地理位置选择器 
    734.      * @param object $receiveObj 接收对象 
    735.      * @return string 
    736.      */  
    737.     protected function _responseLocationSelect($receiveObj){  
    738.         return $this->_responseEvent($receiveObj);  
    739.     }  
    740.       
    741.     /** 
    742.      * 普通消息响应 
    743.      * @param object $receiveObj 接收对象 
    744.      * @return string 
    745.      */  
    746.     protected function _responseMessage($receiveObj){  
    747.         // write your code, such as save message and remind customer service  
    748.           
    749.         // 通关密语  
    750.         if(($msg=$this->_responseArgot($receiveObj))!==false){  
    751.             return $msg;  
    752.         }  
    753.           
    754.         // 转发客服消息到微信多客服系统  
    755.         elseif($this->Config->TransferCustomerService){  
    756.             return array(  
    757.                 'MsgType' => 'transfer_customer_service',  
    758.             );  
    759.         }  
    760.     }  
    761.       
    762.     /** 
    763.      * 响应暗语 
    764.      *  
    765.      * @param object $receiveObj 
    766.      * @return false|string 返回false表示非暗语处理,可以由其它逻辑处理 
    767.      */  
    768.     protected function _responseArgot($receiveObj){  
    769.         if(!isset($receiveObj->postObj->Content) || !($msg=$receiveObj->postObj->Content)){  
    770.             return false;  
    771.         }  
    772.           
    773.         $msg = trim($msg);  
    774.           
    775.         if(defined('WXAPI_ARGOT_WHO_AM_I') && WXAPI_ARGOT_WHO_AM_I && !strcasecmp(WXAPI_ARGOT_WHO_AM_I, $msg)){  
    776.             return "OH LORD, MY 4susername is " . $this->Config->AppName . ", AppId: " . $this->Config->AppId . ",  Server: " . $_SERVER['HTTP_HOST'] ." .";  
    777.         }  
    778.           
    779.         elseif(defined('WXAPI_ARGOT_DESTORY_SESSION') && WXAPI_ARGOT_DESTORY_SESSION && !strcasecmp(WXAPI_ARGOT_DESTORY_SESSION, $msg)){  
    780.             $openid = $receiveObj->parse_openid();  
    781.             if($openid && class_exists('WeixinUserModel') && method_exists('WeixinUserModel', 'destroy_session')){  
    782.                 $oWeixinUserModel = new WeixinUserModel();  
    783.                 $succ = $oWeixinUserModel->destroy_session($openid);  
    784.                 return $succ?"Your session has been destoryed Successfully!":"Failed destroy your session!";  
    785.             }else{  
    786.                 return false;  
    787.             }  
    788.         }  
    789.           
    790.         else{  
    791.             return false;  
    792.         }  
    793.     }  
    794.       
    795.     /** 
    796.      * 群发消息通知响应 
    797.      * @param object $receiveObj 接收对象 
    798.      * @return string 
    799.      */  
    800.     protected function _responseMassSendJobFinish($receiveObj){  
    801.         // write your code, such as save message and remind customer service  
    802.       
    803.     }  
    804.       
    805.     /** 
    806.      * 模板消息通知响应 
    807.      * @param object $receiveObj 接收对象 
    808.      * @return string 
    809.      */  
    810.     protected function _responseTemplateSendJobFinish($receiveObj){  
    811.         // write your code, such as save message and remind customer service  
    812.       
    813.     }  
    814.       
    815.     /** 
    816.      * 预处理多媒体文件 
    817.      * 可以根据消息类型,调用微信接口,将消息中的图片、音频等多媒体文件上传到微信服务器, 
    818.      * 得到MediaId,并替换掉原来的多媒体文件 
    819.      * @param mixed $Content 
    820.      * @return mixed 
    821.      */  
    822.     protected function _preprocessResponseMedia($Content){  
    823.         if(!is_array($Content) || empty($Content['MsgType'])){  
    824.             return $Content;  
    825.         }  
    826.           
    827.         $msgtype = strtolower($Content['MsgType']);  
    828.         switch ($msgtype){  
    829.             case 'image':  
    830.                 $mediaField = 'MediaId';  
    831.                 if(is_string($Content['Content']) && !$this->_isMediaId($Content['Content'])){  
    832.                     $mediaFile = $Content['Content'];  
    833.                     $oClient = WeixinApi::instance($this->Config->Client, $this->Config);  
    834.   
    835.                     $mediaId = $oClient->upload_media($mediaFile, 'image');  
    836.                       
    837.                     $Content['Content'] = $mediaId;  
    838.                 }else{  
    839.                     $mediaId = $Content['Content'];  
    840.                     $mediaFile = '';  
    841.                 }  
    842.                 $this->_responseMedia[$mediaField] = array($mediaId, 'image', $mediaFile);  
    843.                   
    844.                 break;  
    845.             case 'voice':  
    846.                 $mediaField = 'MediaId';  
    847.                 if(is_string($Content['Content']) && !$this->_isMediaId($Content['Content'])){  
    848.                     $mediaFile = $Content['Content'];  
    849.                       
    850.                     $oClient = WeixinApi::instance($this->Config->Client, $this->Config);  
    851.                     $mediaId = $oClient->upload_media($mediaFile, 'voice');  
    852.                       
    853.                     $Content['Content'] = $mediaId;  
    854.                 }else{  
    855.                     $mediaId = $Content['Content'];  
    856.                     $mediaFile = '';  
    857.                 }  
    858.                 $this->_responseMedia[$mediaField] = array($mediaId, 'voice', $mediaFile);  
    859.                   
    860.                 break;  
    861.             case 'video':  
    862.                 $mediaField = 'MediaId';  
    863.                 if(!$this->_isMediaId($Content['Content']['MediaId'])){  
    864.                     $mediaFile = $Content['Content']['MediaId'];  
    865.                     $mediaField = 'MediaId';  
    866.                       
    867.                     $oClient = WeixinApi::instance($this->Config->Client, $this->Config);  
    868.                     $mediaId = $oClient->upload_media($mediaFile, 'video');  
    869.                       
    870.                     $Content['Content']['MediaId'] = $mediaId;  
    871.                 }else{  
    872.                     $mediaId = $Content['Content']['MediaId'];  
    873.                     $mediaFile = '';  
    874.                 }  
    875.                 $this->_responseMedia[$mediaField] = array($mediaId, 'video', $mediaFile);  
    876.                   
    877.                 $thumbMediaField = 'ThumbMediaId';  
    878.                 if(!$this->_isMediaId($Content['Content']['ThumbMediaId'])){  
    879.                     $thumbMediaFile = $Content['Content']['ThumbMediaId'];  
    880.                       
    881.                     $oClient = WeixinApi::instance($this->Config->Client, $this->Config);  
    882.                     $thumbMediaId = $oClient->upload_media($thumbMediaFile, 'thumb');  
    883.   
    884.                     $Content['Content']['ThumbMediaId'] = $thumbMediaId;  
    885.                       
    886.                 }else{  
    887.                     $thumbMediaId = $Content['Content']['ThumbMediaId'];  
    888.                     $thumbMediaFile = '';  
    889.                 }  
    890.                 $this->_responseMedia[$thumbMediaField] = array($thumbMediaId, 'thumb', $thumbMediaFile);  
    891.                 break;  
    892.             case 'music':  
    893.                 $thumbMediaField = 'ThumbMediaId';  
    894.                 if(is_array($Content['Content'])){  
    895.                     if(!$this->_isMediaId($Content['Content']['ThumbMediaId'])){  
    896.                         $thumbMediaFile = $Content['Content']['ThumbMediaId'];  
    897.                       
    898.                         $oClient = WeixinApi::instance($this->Config->Client, $this->Config);  
    899.                         $thumbMediaId = $oClient->upload_media($thumbMediaFile, 'thumb');  
    900.                           
    901.                         $Content['Content']['ThumbMediaId'] = $thumbMediaId;  
    902.                     }else{  
    903.                         $thumbMediaId = $Content['Content']['ThumbMediaId'];  
    904.                         $thumbMediaFile = '';  
    905.                     }  
    906.                 }else{  
    907.                     if(!$this->_isMediaId($Content['Content'])){  
    908.                         $thumbMediaFile = $Content['Content'];  
    909.                         $oClient = WeixinApi::instance($this->Config->Client, $this->Config);  
    910.                         $thumbMediaId = $oClient->upload_media($thumbMediaFile, 'thumb');  
    911.                           
    912.                         $Content['Content'] = $thumbMediaId;  
    913.                     }else{  
    914.                         $thumbMediaId = $Content['Content'];  
    915.                         $thumbMediaFile = '';  
    916.                     }  
    917.                 }  
    918.                 $this->_responseMedia[$thumbMediaField] = array($thumbMediaId, 'thumb', $thumbMediaFile);  
    919.                   
    920.                 break;        
    921.             default:  
    922.                 // other type, do nothing                         
    923.         }  
    924.           
    925.         return $Content;  
    926.     }  
    927.       
    928.     public function createMessage($FromUserName, $ToUserName, $Content){  
    929.         return self::generateMessage($FromUserName, $ToUserName, $Content);  
    930.     }  
    931.       
    932.     /** 
    933.      * 根据消息类型,创建消息 
    934.      * @param string $FromUserName 发送者 
    935.      * @param string $ToUserName    接收者 
    936.      * @param string|array $Content 发送内容,默认为文本消息,传数组可设定消息类型,格式为:aray('MsgType' => 'image', 'Content' => '消息内容') 
    937.      * @return string 返回XML格式消息 
    938.      */  
    939.     public static function generateMessage($FromUserName, $ToUserName, $Content){  
    940.         $aMsgTypes = array(  
    941.             'text', 'image', 'voice', 'video', 'music', 'news', 'transfer_customer_service'  
    942.         );  
    943.           
    944.         if(is_array($Content)){  
    945.             $type = $Content['MsgType'];  
    946.             $data = $Content['Content'];  
    947.         }else{  
    948.             $type = 'text';  
    949.             $data = $Content;  
    950.         }  
    951.           
    952.         if(!in_array($type, $aMsgTypes, false)){  
    953.             return WeixinApi::throw_exception("Unknown MsgType: $type", WXAPI_ERR_CONFIG, $Content, __FILE__, __LINE__);  
    954.         }  
    955.           
    956.         if(!strcasecmp($type, 'transfer_customer_service')){  
    957.             $method = 'generateTransferCustomerServiceMessage';  
    958.         }else{  
    959.             $method = 'generate' . ucfirst(strtolower($type)) . 'Message';  
    960.         }  
    961.         return self::$method($FromUserName, $ToUserName, $data);  
    962.     }  
    963.       
    964.     public function createTextMessage($FromUserName, $ToUserName, $content){  
    965.         return self::generateTextMessage($FromUserName, $ToUserName, $content);  
    966.     }  
    967.       
    968.     /** 
    969.      * 创建文本消息 
    970.      * @param object $object 
    971.      * @param string $content 文本内容,支持换行 
    972.      * @return string 
    973.      */  
    974.     public static function generateTextMessage($FromUserName, $ToUserName, $content)  
    975.     {  
    976.         $msgTpl = "<xml>  
    977. <ToUserName><![CDATA[%s]]></ToUserName>  
    978. <FromUserName><![CDATA[%s]]></FromUserName>  
    979. <CreateTime>%s</CreateTime>  
    980. <MsgType><![CDATA[text]]></MsgType>  
    981. <Content><![CDATA[%s]]></Content>  
    982. </xml>";  
    983.         return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $content);  
    984.     }  
    985.       
    986.     public function createImageMessage($FromUserName, $ToUserName, $mediaId){  
    987.         return self::generateImageMessage($FromUserName, $ToUserName, $mediaId);  
    988.     }  
    989.       
    990.     /** 
    991.      * 创建图片消息 
    992.      * @param object $object 
    993.      * @param string $mediaId 通过上传多媒体文件,得到的id 
    994.      * @return string 
    995.      */  
    996.     public static function generateImageMessage($FromUserName, $ToUserName, $mediaId)  
    997.     {  
    998.         $msgTpl = "<xml>  
    999. <ToUserName><![CDATA[%s]]></ToUserName>  
    1000. <FromUserName><![CDATA[%s]]></FromUserName>  
    1001. <CreateTime>%s</CreateTime>  
    1002. <MsgType><![CDATA[image]]></MsgType>  
    1003. <Image>  
    1004. %s  
    1005. </Image>  
    1006. </xml>";  
    1007.         $mediaTpl = "<MediaId><![CDATA[%s]]></MediaId>";  
    1008.         if(!is_array($mediaId)){  
    1009.             $mediaIds = array($mediaId);  
    1010.         }else{  
    1011.             $mediaIds = $mediaId;  
    1012.         }  
    1013.           
    1014.         $media = '';  
    1015.         foreach($mediaIds as $mediaId){  
    1016.             $media .= sprintf($mediaTpl, $mediaId);  
    1017.         }  
    1018.           
    1019.         return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $media);  
    1020.     }  
    1021.       
    1022.     public function createVoiceMessage($FromUserName, $ToUserName, $mediaId){  
    1023.         return self::generateVoiceMessage($FromUserName, $ToUserName, $mediaId);  
    1024.     }  
    1025.       
    1026.     /** 
    1027.      * 创建语音消息 
    1028.      * @param object $object 
    1029.      * @param string $mediaId 通过上传多媒体文件,得到的id  
    1030.      * @return string 
    1031.      */  
    1032.     public static function generateVoiceMessage($FromUserName, $ToUserName, $mediaId)  
    1033.     {  
    1034.         $msgTpl = "<xml>  
    1035. <ToUserName><![CDATA[%s]]></ToUserName>  
    1036. <FromUserName><![CDATA[%s]]></FromUserName>  
    1037. <CreateTime>%s</CreateTime>  
    1038. <MsgType><![CDATA[voice]]></MsgType>  
    1039. <Voice>  
    1040. %s  
    1041. </Voice>  
    1042. </xml>";  
    1043.         $mediaTpl = "<MediaId><![CDATA[%s]]></MediaId>";  
    1044.         if(!is_array($mediaId)){  
    1045.             $mediaIds = array($mediaId);  
    1046.         }else{  
    1047.             $mediaIds = $mediaId;  
    1048.         }  
    1049.       
    1050.         $media = '';  
    1051.         foreach($mediaIds as $mediaId){  
    1052.             $media .= sprintf($mediaTpl, $mediaId);  
    1053.         }  
    1054.       
    1055.         return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $media);  
    1056.     }  
    1057.       
    1058.     public function createVideoMessage($FromUserName, $ToUserName, $mediaId, $thumbMediaId=NULL){  
    1059.         return self::generateVideoMessage($FromUserName, $ToUserName, $mediaId, $thumbMediaId);  
    1060.     }  
    1061.       
    1062.     /** 
    1063.      * 创建视频消息 
    1064.      * @param object $object 
    1065.      * @param string|array $mediaId  通过上传多媒体文件,得到的id,可以传数组:Array('MediaId'=>mediaid, 'ThumbMediaId'=>thumbMediaId) 
    1066.      * @param string $thumbMediaId 缩略图的媒体id,通过上传多媒体文件,得到的id,必填字段 
    1067.      * @return string 
    1068.      */  
    1069.     public static function generateVideoMessage($FromUserName, $ToUserName, $mediaId, $thumbMediaId=NULL)  
    1070.     {  
    1071.         $msgTpl = "<xml>  
    1072. <ToUserName><![CDATA[%s]]></ToUserName>  
    1073. <FromUserName><![CDATA[%s]]></FromUserName>  
    1074. <CreateTime>%s</CreateTime>  
    1075. <MsgType><![CDATA[video]]></MsgType>  
    1076. <Video>  
    1077. %s  
    1078. </Video>   
    1079. </xml>";  
    1080.           
    1081.         if(is_array($mediaId)){  
    1082.             $mediaData = array(  
    1083.                     'MediaId' => $mediaId['MediaId'],  
    1084.                     'ThumbMediaId' => $mediaId['ThumbMediaId'],  
    1085.             );  
    1086.         }else{  
    1087.             $mediaData = array(  
    1088.                     'MediaId' => $mediaId,  
    1089.                     'ThumbMediaId' => $thumbMediaId,  
    1090.             );  
    1091.         }  
    1092.           
    1093.         $media = "";  
    1094.         foreach ($mediaData as $n=>$d){  
    1095.             $n = ucfirst($n);  
    1096.             $mediaTpl = "<{$n}><![CDATA[%s]]></{$n}>";  
    1097.             $media .= sprintf($mediaTpl, $d);  
    1098.         }  
    1099.       
    1100.         return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $media);  
    1101.     }  
    1102.       
    1103.     public function createMusicMessage($FromUserName, $ToUserName, $thumbMediaId, $title=NULL, $description=NULL, $musicUrl=NULL, $hqMusicUrl=NULL)  
    1104.     {  
    1105.         return self::generateMusicMessage($FromUserName, $ToUserName, $thumbMediaId, $title, $description, $musicUrl, $hqMusicUrl);  
    1106.     }  
    1107.       
    1108.     /** 
    1109.      * 创建音乐消息 
    1110.      * @param object $object 
    1111.      * @param string|array $thumbMediaId 缩略图的媒体id,通过上传多媒体文件,得到的id,必填字段,传数组格式如: 
    1112.      * array ( 
    1113.                     "Title" => $title, 
    1114.                     "Description" => $description, 
    1115.                     "MusicURL" => $musicURL, 
    1116.                     "HQMusicUrl" => $hqMusicUrl, 
    1117.                     "ThumbMediaId" => $thumbMediaId, 
    1118.             )  
    1119.      * @param string $title 音乐标题  
    1120.      * @param string $description 音乐描述  
    1121.      * @param string $musicUrl 音乐链接 
    1122.      * @param string $hqMusicUrl 高质量音乐链接,WIFI环境优先使用该链接播放音乐  
    1123.      * @return string 
    1124.      */  
    1125.     public static function generateMusicMessage($FromUserName, $ToUserName, $thumbMediaId, $title=NULL, $description=NULL, $musicUrl=NULL, $hqMusicUrl=NULL)  
    1126.     {  
    1127.         $msgTpl = "<xml>  
    1128. <ToUserName><![CDATA[%s]]></ToUserName>  
    1129. <FromUserName><![CDATA[%s]]></FromUserName>  
    1130. <CreateTime>%s</CreateTime>  
    1131. <MsgType><![CDATA[music]]></MsgType>  
    1132. <Music>%s  
    1133. </Music>  
    1134. </xml>";  
    1135.   
    1136.         $media = "";  
    1137.         if (is_array ( $thumbMediaId )) {  
    1138.             $mediaData = array (  
    1139.                     "Title" => $thumbMediaId['Title'],  
    1140.                     "Description" => $thumbMediaId['Description'],  
    1141.                     "MusicUrl" => $thumbMediaId['MusicUrl'],  
    1142.                     "HQMusicUrl" => $thumbMediaId['HQMusicUrl'],  
    1143.                     "ThumbMediaId" => $thumbMediaId['ThumbMediaId'],  
    1144.             );  
    1145.         } else {  
    1146.             $mediaData = array (  
    1147.                     "Title" => $title,  
    1148.                     "Description" => $description,  
    1149.                     "MusicUrl" => $musicUrl,  
    1150.                     "HQMusicUrl" => $hqMusicUrl,  
    1151.                     "ThumbMediaId" => $thumbMediaId,  
    1152.             );  
    1153.         }  
    1154.         foreach($mediaData as $n=>$d){  
    1155.             if($d){  
    1156.                 $n = ucfirst($n);  
    1157.                 $mediaTpl = "<{$n}><![CDATA[%s]]></{$n}>";  
    1158.                 $media .= sprintf($mediaTpl, $d);  
    1159.             }  
    1160.         }  
    1161.       
    1162.         return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $media);  
    1163.     }  
    1164.       
    1165.     public function createNewsMessage($FromUserName, $ToUserName, $title, $description=NULL, $picUrl=NULL, $url=NULL)  
    1166.     {  
    1167.         return self::generateNewsMessage($FromUserName, $ToUserName, $title, $description, $picUrl, $url);  
    1168.     }  
    1169.       
    1170.     /** 
    1171.      * 创建图文消息 
    1172.      * @param object $object 
    1173.      * @param string|array $title 图文消息标题,传数组格式如: 
    1174.      * array( 
    1175.                     "Title" => $title, 
    1176.                     "Description" => $description, 
    1177.                     "PicUrl" => $picUrl, 
    1178.                     "Url" => $url, 
    1179.             ) 
    1180.             或 
    1181.         array( 0 => array( 
    1182.                     "Title" => $title, 
    1183.                     "Description" => $description, 
    1184.                     "PicUrl" => $picUrl, 
    1185.                     "Url" => $url, 
    1186.             )) 
    1187.      * @param string $description 图文消息描述  
    1188.      * @param string $picUrl 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200  
    1189.      * @param string $url 点击图文消息跳转链接  
    1190.      * @return string 
    1191.      */  
    1192.     public static function generateNewsMessage($FromUserName, $ToUserName, $title, $description=NULL, $picUrl=NULL, $url=NULL)  
    1193.     {  
    1194.         $msgTpl = "<xml>  
    1195. <ToUserName><![CDATA[%s]]></ToUserName>  
    1196. <FromUserName><![CDATA[%s]]></FromUserName>  
    1197. <CreateTime>%s</CreateTime>  
    1198. <MsgType><![CDATA[news]]></MsgType>  
    1199. <ArticleCount>%s</ArticleCount>  
    1200. <Articles>  
    1201. %s  
    1202. </Articles>  
    1203. </xml>";  
    1204.           
    1205.         $media = "";  
    1206.         $items = array();  
    1207.         if(is_array($title)){  
    1208.             if(isset($title['Title']) || isset($title['Description']) || isset($title['PicUrl']) || isset($title['Url'])){  
    1209.                 $items[] = $title;  
    1210.             }else{  
    1211.                 $items = $title;  
    1212.             }  
    1213.         }else{  
    1214.             $items[] = array(  
    1215.                     "Title" => $title,  
    1216.                     "Description" => $description,  
    1217.                     "PicUrl" => $picUrl,  
    1218.                     "Url" => $url,  
    1219.             );  
    1220.         }  
    1221.           
    1222.         $count = count($items);  
    1223.           
    1224.         if($count>10){  
    1225.             return WeixinApi::throw_exception("Over Max 10 news messages", WXAPI_ERR_CONFIG, array('items'=>$items), __FILE__, __LINE__);  
    1226.         }  
    1227.           
    1228.         $valid_item_tags = array('Title', 'Description', 'PicUrl', 'Url');  
    1229.         foreach($items as $item){  
    1230.             $media .= "<item>";  
    1231.             foreach ( $item as $n => $d ) {  
    1232.                 if ($d && in_array($n, $valid_item_tags, true)) {  
    1233.                     $n = ucfirst($n);  
    1234.                     $mediaTpl = "<{$n}><![CDATA[%s]]></{$n}>";  
    1235.                     $media .= sprintf ( $mediaTpl, $d );  
    1236.                 }  
    1237.             }  
    1238.             $media .= "</item>";  
    1239.         }  
    1240.       
    1241.         return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $count, $media);  
    1242.     }  
    1243.       
    1244.     /** 
    1245.      * 创建转发客服消息 
    1246.      * @return string 
    1247.      */  
    1248.     public static function generateTransferCustomerServiceMessage($FromUserName, $ToUserName, $TransInfo_KfAccount=NULL)  
    1249.     {  
    1250.         $msgTpl = "<xml>  
    1251. <ToUserName><![CDATA[%s]]></ToUserName>  
    1252. <FromUserName><![CDATA[%s]]></FromUserName>  
    1253. <CreateTime>%s</CreateTime>  
    1254. <MsgType><![CDATA[transfer_customer_service]]></MsgType>";  
    1255.           
    1256.         if($TransInfo_KfAccount){  
    1257.             $msg .= "<TransInfo>  
    1258.         <KfAccount>%s</KfAccount>  
    1259.     </TransInfo>";  
    1260.         }  
    1261.           
    1262.         $msgTpl .= "</xml>";  
    1263.   
    1264.         return sprintf($msgTpl, $FromUserName, $ToUserName, time(), $TransInfo_KfAccount);  
    1265.     }  
    1266.       
    1267.     public function __get($c){  
    1268.         if(substr($c, 0, 1)!='_'){  
    1269.             if(in_array($c, array('Http'))){  
    1270.                 return parent::__get($c);  
    1271.             }else{  
    1272.                 $n = '_' . $c;  
    1273.                 return $this->$n;  
    1274.             }  
    1275.         }  
    1276.     }  
    1277.       
    1278.     /** 
    1279.      * 生成签名 
    1280.      * @param string $msg_encrypt 
    1281.      * @param string $nonce 
    1282.      * @param string $timestamp 
    1283.      * @param string $token 
    1284.      * @return string 
    1285.      */  
    1286.     public static function genearteSignature($msg_encrypt, $nonce, $timestamp, $token){  
    1287.         $tmpArr = array($token, $timestamp, $nonce, $msg_encrypt);  
    1288.         sort($tmpArr, SORT_STRING);  
    1289.         $tmpStr = implode( $tmpArr );  
    1290.         return sha1( $tmpStr );  
    1291.     }  
    1292.       
    1293.     /** 
    1294.      * 创建加密消息 
    1295.      * @param string $encrypt_content 加密内容 
    1296.      * @param string $nonce 随机数 
    1297.      * @param int $timestamp 时间戳 
    1298.      * @param string $signature 签名 
    1299.      * @return string 
    1300.      */  
    1301.     public static function generateEncryptMessage($encrypt_content, $nonce, $timestamp, $signature)  
    1302.     {  
    1303.         $msgTpl = "<xml>  
    1304. <Encrypt><![CDATA[%s]]></Encrypt>  
    1305. <MsgSignature><![CDATA[%s]]></MsgSignature>  
    1306. <TimeStamp>%s</TimeStamp>  
    1307. <Nonce><![CDATA[%s]]></Nonce>  
    1308. </xml>  
    1309. ";  
    1310.         return sprintf($msgTpl, $encrypt_content, $signature, $timestamp, $nonce);  
    1311.     }  
    1312.       
    1313.     /** 
    1314.      * 加密响应消息 
    1315.      * @return string 
    1316.      */  
    1317.     protected function _encrypt_response($msg, $encodingkey){  
    1318.         $msg_encrypt = $this->_encryptMsg($msg, $encodingkey);  
    1319.         $nonce = WeixinApi_Kit::gen_random_number(11);  
    1320.         $timestamp = time();  
    1321.         $signature = self::genearteSignature($msg_encrypt, $nonce, $timestamp, $this->Config->AppToken);  
    1322.       
    1323.         return self::generateEncryptMessage($msg_encrypt, $nonce, $timestamp, $signature);  
    1324.     }  
    1325.       
    1326.     /** 
    1327.      * 对明文进行加密 
    1328.      * @param string $msg 需要加密的明文 
    1329.      * @param string $encodingkey 加密私钥 
    1330.      * @return string|false 加密得到的密文,失败返回flase 
    1331.      */  
    1332.     protected function _encryptMsg($msg, $encodingkey=NULL)  
    1333.     {  
    1334.         $AESKey = base64_decode(($encodingkey?$encodingkey:$this->Config->AppEncodingAESKey) . "=");  
    1335.       
    1336.         // 获得16位随机字符串,填充到明文之前  
    1337.         $random = WeixinApi_Kit::gen_random_string ( 16 );  
    1338.         $msg = $random . pack ( "N", strlen ( $msg ) ) . $msg . $this->Config->AppId;  
    1339.         // 网络字节序  
    1340.         $size = mcrypt_get_block_size ( MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC );  
    1341.         $module = mcrypt_module_open ( MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '' );  
    1342.         if(false===$module){  
    1343.             return $this->_throw_exception(  
    1344.                     "Cann't open an encryption descriptor"  
    1345.                     , WXAPI_ERR_BAD_ENCRYPT  
    1346.                     , $msg  
    1347.                     , __FILE__, __LINE__  
    1348.             );  
    1349.         }  
    1350.       
    1351.         $iv = substr ( $AESKey, 0, 16 );  
    1352.           
    1353.         // 使用自定义的填充方式对明文进行补位填充  
    1354.         $msg = WeixinApi_Kit::pkcs7_encode ( $msg, 32 );  
    1355.         $init = mcrypt_generic_init ( $module, $AESKey, $iv );  
    1356.         if(false===$init){  
    1357.             return $this->_throw_exception(  
    1358.                     "Cann't initialize buffers for encryption"  
    1359.                     , WXAPI_ERR_BAD_ENCRYPT  
    1360.                     , array('msg' => $msg, 'mcrypt_generic_init return' => $init)  
    1361.                     , __FILE__, __LINE__  
    1362.             );  
    1363.         }elseif(-3==$init){  
    1364.             return $this->_throw_exception(  
    1365.                     "the key length was incorrect"  
    1366.                     , WXAPI_ERR_BAD_ENCRYPT  
    1367.                     , array('msg' => $msg, 'mcrypt_generic_init return' => $init)  
    1368.                     , __FILE__, __LINE__  
    1369.             );  
    1370.         }elseif(-4==$init){  
    1371.             return $this->_throw_exception(  
    1372.                     "there was a memory allocation problem"  
    1373.                     , WXAPI_ERR_BAD_ENCRYPT  
    1374.                     , array('msg' => $msg, 'mcrypt_generic_init return' => $init)  
    1375.                     , __FILE__, __LINE__  
    1376.             );  
    1377.         }elseif($init<0){  
    1378.             return $this->_throw_exception(  
    1379.                     "an unknown error occurred when initialize buffers for encryption"  
    1380.                     , WXAPI_ERR_BAD_ENCRYPT  
    1381.                     , array('msg' => $msg, 'mcrypt_generic_init return' => $init)  
    1382.                     , __FILE__, __LINE__  
    1383.             );  
    1384.         }  
    1385.       
    1386.         // 加密  
    1387.         $encrypted = mcrypt_generic ( $module, $msg );  
    1388.         mcrypt_generic_deinit ( $module );  
    1389.         mcrypt_module_close ( $module );  
    1390.       
    1391.         // print(base64_encode($encrypted));  
    1392.         // 使用BASE64对加密后的字符串进行编码  
    1393.         return base64_encode ( $encrypted );  
    1394.     }  
    1395. }  
    1396. 个人公众号谢谢各位老铁支持

  • 相关阅读:
    【译】可扩展前端2  —  常见模式
    【译】可扩展前端1  —  架构基础
    【译】The Clean Architecture
    获取页面元素位置
    vue高价组件的使用
    gif动态图片转精灵图
    消除 transition 闪屏
    移动端 -- 如何去掉元素被触摸时产生的半透明灰色遮罩?
    解决手机移动端触屏版web页面长时间按住页面出现闪退的问题
    移动端滑动慢,卡顿
  • 原文地址:https://www.cnblogs.com/piwefei/p/9172094.html
Copyright © 2011-2022 走看看