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. 个人公众号谢谢各位老铁支持

  • 相关阅读:
    openldap
    Java实现 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He…
    Java实现 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He…
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P2141 珠心算测验
    Java实现 洛谷 P1567 统计天数
    Java实现 洛谷 P1567 统计天数
  • 原文地址:https://www.cnblogs.com/piwefei/p/9172094.html
Copyright © 2011-2022 走看看