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

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

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

原文地址:https://www.cnblogs.com/piwefei/p/9172094.html

时间: 2024-11-05 16:12:10

微信公众号接口类(PHP版本)的相关文章

C#/ASP.NET MVC微信公众号接口开发之从零开发(二) 接收微信消息并且解析XML(附源码)

文章导读: C#微信公众号接口开发之从零开发(一) 接入微信公众平台 微信接入之后,微信通过我们接入的地址进行通信,其中的原理是微信用户发送消息给微信公众账号,微信服务器将消息以xml的形式发送到我们的绑定的地址上,通过解析XML数据,获取到微信用户发送的消息,让根据微信消息(文本:关键字,图片,语音等等)回复XML格式的数据给微信服务器,微信服务器再将接收到的消息返回给用户微信. 我们所需要做的:接收消息和返回消息 一.创建实体类 首先看文档http://mp.weixin.qq.com/wi

天纵智能开发平台与微信公众号接口指南

前言:利用天纵智能开发平台可以非常方便地与最新的微信公众号进行接入,开发者不必研究复杂啰嗦的微信接口规则,只需在天纵智能开发平台上配置一下业务逻辑即可完成开发. 下面介绍一下天纵智能开发平台中使用微信的操作过程和几个常用使用场合. ==微信接口申请和认证== 一.申请微信公众号 进入微信官网https://mp.weixin.qq.com  注册并申请微信公众号,为了能实现更多功能,请申请为"服务号"类型,并进行认证. 根据官网说明一步一步填写资料,上传证明文件,然后即可完成申请和认证

微信公众号接口添加菜单时错误(errcode":40017 invalid button type)

POST提交时总是报错: {"errcode":40017,"errmsg":"invalid button type"} 最后查出来是由于数据中有中文引起的 解决: data = {"button":[ {"name": u"会员服务", "sub_button":[ {"type":"click","name&qu

C#/ASP.NET MVC微信公众号接口开发之从零开发(三)回复消息 (附源码)

C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发(二) 接收微信消息并且解析XML(附源码) 一.拼凑回复的XML字符串 微信被动回复的形式有一下六种: 1 回复文本消息 2 回复图片消息 3 回复语音消息 4 回复视频消息 5 回复音乐消息 6 回复图文消息 分别对应不同的XML形式,这里以文本消息和图文为例,读者举一反三其他的类似,不再赘述:

C#微信公众号接口开发,灵活利用网页授权、带参数二维码、模板消息,提升用户体验之完成用户绑定个人微信及验证码获取

一.前言 当下微信公众号几乎已经是每个公司必备的,但是大部分微信公众账号用户体验都欠佳,特别是涉及到用户绑定等,需要用户进行复杂的操作才可以和网站绑定,或者很多公司直接不绑定,而是每次都让用户填写账号密码.作为微信接口开发人员我们知道网页授权可以用作微信网页用作安全登录,带参数二维码的使用用作记录用户来源,模板消息用作购物消费等消息的通知,但是很少看到有综合利用这些高级接口做出体验比较好的公众账号,这里分享一些我开发的用户绑定和验证码的一些心得.所需要的接口有基础的回复.网页授权.带参数二维码.

微信公众号接口相关操作

<?php /** * 微信公众号接口相关操作 */ class WeChat { private $_appid; private $_appsecret; private $_token; public function __construct($_appid,$_appsecret,$_token) { $this->_appid=$_appid; $this->_appsecret=$_appsecret; $this->_token=$_token; } //获得微信通信

微信公众号接口权限表汇总

微信公众号目前分为订阅号.服务号.企业号,订阅号和服务号又区分认证和非认证,部分权限又有细微差异,整理个列表方便大家检索和查询相关功能! 统计日期截止到 2014年12月04日 1.目前收集到的包含未认证服务号.已认证服务号.未认证订阅号,已认证订阅号还真没找到! 2.未认证订阅号与未认证服务号两者接口差异仅体现在自定义菜单上!注意这里说的是接口差异,如果想了解区别移步这里 微信公众平台 企业号与服务号.订阅号的区别 ! 3.点击接口中的超链可以直接跳转微信公众平台查看接口描述! 服务号未认证

我推荐阅读的微信公众号-IT类

微信,正深刻影响着我们的生活,每个使用微信的人,从微信这个窗口去了解这个世界. 微信公众号,微信生态圈是核心功能之一,每天都有大量的文章创作.传播.转发出来,海量的信息扑面而来,微信阅读成为微信使用者最重要的阅读方式. 另一方面,你有没有感觉有信息过载的焦虑,看到微信上大量的红色小点,强迫症者是不是有点抓狂,恨不能轻轻一刷,就全部消失. 总有人质疑这种浅阅读和碎片化阅读的方式,这种质疑从某种角度来说,就是抱残守缺,对纸质阅读的一种过分依恋.我以为电子阅读的时代已经全面开启,这是个大趋势,每个人都

微信公众号接口深入剖析与应用——多接口集合应用与重用技术开发(图文导航)

前几天有几个朋友叫我出一个关于公众号发送位置导航的教程,迫于时间压力才今天开始写写,我先说说思想 吧,首先我们知道微信发送位置可以朋友与朋友之间直接发送,但是在一个公司或者企业下,一个公众号下面可 能有很多个关注用户,那就涉及到了给陌生人发送位置的情况,我们知道微信给陌生人是发送不了信息的,需要 通过验证才行,那就涉及到了本篇要讲的第一个避开验证的方法 方法一:通过公众号转发位置给非好友的陌生人,绕过好友验证,点击导航 可能有人就会问,在48小时内如果用户未与公众号交互,信息就会发送失败,这就是