wechat.class.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. <?php
  2. /**
  3. * 微信公众平台PHP-SDK, 官方API部分
  4. * @author dodge <dodgepudding@gmail.com>
  5. * @link https://github.com/dodgepudding/wechat-php-sdk
  6. * @version 1.1
  7. * usage:
  8. * $options = array(
  9. * 'token'=>'tokenaccesskey' //填写你设定的key
  10. * );
  11. * $weObj = new Wechat($options);
  12. * $weObj->valid();
  13. * $type = $weObj->getRev()->getRevType();
  14. * switch($type) {
  15. * case Wechat::MSGTYPE_TEXT:
  16. * $weObj->text("hello, I'm wechat")->reply();
  17. * exit;
  18. * break;
  19. * case Wechat::MSGTYPE_EVENT:
  20. * ....
  21. * break;
  22. * case Wechat::MSGTYPE_IMAGE:
  23. * ...
  24. * break;
  25. * default:
  26. * $weObj->text("help info")->reply();
  27. * }
  28. */
  29. class Wechat
  30. {
  31. const MSGTYPE_TEXT = 'text';
  32. const MSGTYPE_IMAGE = 'image';
  33. const MSGTYPE_LOCATION = 'location';
  34. const MSGTYPE_LINK = 'link';
  35. const MSGTYPE_EVENT = 'event';
  36. const MSGTYPE_MUSIC = 'music';
  37. const MSGTYPE_NEWS = 'news';
  38. const MSGTYPE_VOICE = 'voice';
  39. const MSGTYPE_VIDEO = 'video';
  40. private $token;
  41. private $_msg;
  42. private $_funcflag = false;
  43. private $_receive;
  44. public $debug = false;
  45. private $_logcallback;
  46. public function __construct($options)
  47. {
  48. $this->token = isset($options['token'])?$options['token']:'';
  49. $this->debug = isset($options['debug'])?$options['debug']:false;
  50. $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;
  51. }
  52. /**
  53. * For weixin server validation
  54. */
  55. private function checkSignature()
  56. {
  57. $signature = isset($_GET["signature"])?$_GET["signature"]:'';
  58. $timestamp = isset($_GET["timestamp"])?$_GET["timestamp"]:'';
  59. $nonce = isset($_GET["nonce"])?$_GET["nonce"]:'';
  60. $token = $this->token;
  61. $tmpArr = array($token, $timestamp, $nonce);
  62. sort($tmpArr);
  63. $tmpStr = implode( $tmpArr );
  64. $tmpStr = sha1( $tmpStr );
  65. if( $tmpStr == $signature ){
  66. return true;
  67. }else{
  68. return false;
  69. }
  70. }
  71. /**
  72. * For weixin server validation
  73. * @param bool $return 是否返回
  74. */
  75. public function valid($return=false)
  76. {
  77. $echoStr = isset($_GET["echostr"]) ? $_GET["echostr"]: '';
  78. if ($return) {
  79. if ($echoStr) {
  80. if ($this->checkSignature())
  81. return $echoStr;
  82. else
  83. return false;
  84. } else
  85. return $this->checkSignature();
  86. } else {
  87. if ($echoStr) {
  88. if ($this->checkSignature())
  89. die($echoStr);
  90. else
  91. die('no access');
  92. } else {
  93. if ($this->checkSignature())
  94. return true;
  95. else
  96. die('no access');
  97. }
  98. }
  99. return false;
  100. }
  101. /**
  102. * 设置发送消息
  103. * @param array $msg 消息数组
  104. * @param bool $append 是否在原消息数组追加
  105. */
  106. public function Message($msg = '',$append = false){
  107. if (is_null($msg)) {
  108. $this->_msg =array();
  109. }elseif (is_array($msg)) {
  110. if ($append)
  111. $this->_msg = array_merge($this->_msg,$msg);
  112. else
  113. $this->_msg = $msg;
  114. return $this->_msg;
  115. } else {
  116. return $this->_msg;
  117. }
  118. }
  119. public function setFuncFlag($flag) {
  120. $this->_funcflag = $flag;
  121. return $this;
  122. }
  123. private function log($log){
  124. if ($this->debug && function_exists($this->_logcallback)) {
  125. if (is_array($log)) $log = print_r($log,true);
  126. return call_user_func($this->_logcallback,$log);
  127. }
  128. }
  129. /**
  130. * 获取微信服务器发来的信息
  131. */
  132. public function getRev()
  133. {
  134. $postStr = file_get_contents("php://input");
  135. $this->log($postStr);
  136. if (!empty($postStr)) {
  137. $this->_receive = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
  138. }
  139. return $this;
  140. }
  141. /**
  142. * 获取消息发送者
  143. */
  144. public function getRevFrom() {
  145. if ($this->_receive)
  146. return $this->_receive['FromUserName'];
  147. else
  148. return false;
  149. }
  150. /**
  151. * 获取消息接受者
  152. */
  153. public function getRevTo() {
  154. if ($this->_receive)
  155. return $this->_receive['ToUserName'];
  156. else
  157. return false;
  158. }
  159. /**
  160. * 获取接收消息的类型
  161. */
  162. public function getRevType() {
  163. if (isset($this->_receive['MsgType']))
  164. return $this->_receive['MsgType'];
  165. else
  166. return false;
  167. }
  168. /**
  169. * 获取消息ID
  170. */
  171. public function getRevID() {
  172. if (isset($this->_receive['MsgId']))
  173. return $this->_receive['MsgId'];
  174. else
  175. return false;
  176. }
  177. /**
  178. * 获取消息发送时间
  179. */
  180. public function getRevCtime() {
  181. if (isset($this->_receive['CreateTime']))
  182. return $this->_receive['CreateTime'];
  183. else
  184. return false;
  185. }
  186. /**
  187. * 获取接收消息内容正文
  188. */
  189. public function getRevContent(){
  190. if (isset($this->_receive['Content']))
  191. return $this->_receive['Content'];
  192. else
  193. return false;
  194. }
  195. /**
  196. * 获取接收消息图片
  197. */
  198. public function getRevPic(){
  199. if (isset($this->_receive['PicUrl']))
  200. return $this->_receive['PicUrl'];
  201. else
  202. return false;
  203. }
  204. /**
  205. * 获取接收消息链接
  206. */
  207. public function getRevLink(){
  208. if (isset($this->_receive['Url'])){
  209. return array(
  210. 'url'=>$this->_receive['Url'],
  211. 'title'=>$this->_receive['Title'],
  212. 'description'=>$this->_receive['Description']
  213. );
  214. } else
  215. return false;
  216. }
  217. /**
  218. * 获取接收地理位置
  219. */
  220. public function getRevGeo(){
  221. if (isset($this->_receive['Location_X'])){
  222. return array(
  223. 'x'=>$this->_receive['Location_X'],
  224. 'y'=>$this->_receive['Location_Y'],
  225. 'scale'=>$this->_receive['Scale'],
  226. 'label'=>$this->_receive['Label']
  227. );
  228. } else
  229. return false;
  230. }
  231. /**
  232. * 获取接收事件推送
  233. */
  234. public function getRevEvent(){
  235. if (isset($this->_receive['Event'])){
  236. return array(
  237. 'event'=>$this->_receive['Event'],
  238. 'key'=>$this->_receive['EventKey'],
  239. );
  240. } else
  241. return false;
  242. }
  243. /**
  244. * 获取接收语言推送
  245. */
  246. public function getRevVoice(){
  247. if (isset($this->_receive['MediaId'])){
  248. return array(
  249. 'mediaid'=>$this->_receive['MediaId'],
  250. 'format'=>$this->_receive['Format'],
  251. );
  252. } else
  253. return false;
  254. }
  255. public static function xmlSafeStr($str)
  256. {
  257. return '<![CDATA['.preg_replace("/[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]/",'',$str).']]>';
  258. }
  259. /**
  260. * 数据XML编码
  261. * @param mixed $data 数据
  262. * @return string
  263. */
  264. public static function data_to_xml($data) {
  265. $xml = '';
  266. foreach ($data as $key => $val) {
  267. is_numeric($key) && $key = "item id=\"$key\"";
  268. $xml .= "<$key>";
  269. $xml .= ( is_array($val) || is_object($val)) ? self::data_to_xml($val) : self::xmlSafeStr($val);
  270. list($key, ) = explode(' ', $key);
  271. $xml .= "</$key>";
  272. }
  273. return $xml;
  274. }
  275. /**
  276. * XML编码
  277. * @param mixed $data 数据
  278. * @param string $root 根节点名
  279. * @param string $item 数字索引的子节点名
  280. * @param string $attr 根节点属性
  281. * @param string $id 数字索引子节点key转换的属性名
  282. * @param string $encoding 数据编码
  283. * @return string
  284. */
  285. public function xml_encode($data, $root='xml', $item='item', $attr='', $id='id', $encoding='utf-8') {
  286. if(is_array($attr)){
  287. $_attr = array();
  288. foreach ($attr as $key => $value) {
  289. $_attr[] = "{$key}=\"{$value}\"";
  290. }
  291. $attr = implode(' ', $_attr);
  292. }
  293. $attr = trim($attr);
  294. $attr = empty($attr) ? '' : " {$attr}";
  295. $xml = "<{$root}{$attr}>";
  296. $xml .= self::data_to_xml($data, $item, $id);
  297. $xml .= "</{$root}>";
  298. return $xml;
  299. }
  300. /**
  301. * 设置回复消息
  302. * Examle: $obj->text('hello')->reply();
  303. * @param string $text
  304. */
  305. public function text($text='')
  306. {
  307. $FuncFlag = $this->_funcflag ? 1 : 0;
  308. $msg = array(
  309. 'ToUserName' => $this->getRevFrom(),
  310. 'FromUserName'=>$this->getRevTo(),
  311. 'MsgType'=>self::MSGTYPE_TEXT,
  312. 'Content'=>$text,
  313. 'CreateTime'=>time(),
  314. 'FuncFlag'=>$FuncFlag
  315. );
  316. $this->Message($msg);
  317. return $this;
  318. }
  319. /**
  320. * 设置回复音乐
  321. * @param string $title
  322. * @param string $desc
  323. * @param string $musicurl
  324. * @param string $hgmusicurl
  325. */
  326. public function music($title,$desc,$musicurl,$hgmusicurl='') {
  327. $FuncFlag = $this->_funcflag ? 1 : 0;
  328. $msg = array(
  329. 'ToUserName' => $this->getRevFrom(),
  330. 'FromUserName'=>$this->getRevTo(),
  331. 'CreateTime'=>time(),
  332. 'MsgType'=>self::MSGTYPE_MUSIC,
  333. 'Music'=>array(
  334. 'Title'=>$title,
  335. 'Description'=>$desc,
  336. 'MusicUrl'=>$musicurl,
  337. 'HQMusicUrl'=>$hgmusicurl
  338. ),
  339. 'FuncFlag'=>$FuncFlag
  340. );
  341. $this->Message($msg);
  342. return $this;
  343. }
  344. /**
  345. * 设置回复图文
  346. * @param array $newsData
  347. * 数组结构:
  348. * array(
  349. * [0]=>array(
  350. * 'Title'=>'msg title',
  351. * 'Description'=>'summary text',
  352. * 'PicUrl'=>'http://www.domain.com/1.jpg',
  353. * 'Url'=>'http://www.domain.com/1.html'
  354. * ),
  355. * [1]=>....
  356. * )
  357. */
  358. public function news($newsData=array())
  359. {
  360. $FuncFlag = $this->_funcflag ? 1 : 0;
  361. $count = count($newsData);
  362. $msg = array(
  363. 'ToUserName' => $this->getRevFrom(),
  364. 'FromUserName'=>$this->getRevTo(),
  365. 'MsgType'=>self::MSGTYPE_NEWS,
  366. 'CreateTime'=>time(),
  367. 'ArticleCount'=>$count,
  368. 'Articles'=>$newsData,
  369. 'FuncFlag'=>$FuncFlag
  370. );
  371. $this->Message($msg);
  372. return $this;
  373. }
  374. /**
  375. *
  376. * 回复微信服务器, 此函数支持链式操作
  377. * @example $this->text('msg tips')->reply();
  378. * @param string $msg 要发送的信息, 默认取$this->_msg
  379. * @param bool $return 是否返回信息而不抛出到浏览器 默认:否
  380. */
  381. public function reply($msg=array(),$return = false)
  382. {
  383. if (empty($msg))
  384. $msg = $this->_msg;
  385. $xmldata= $this->xml_encode($msg);
  386. $this->log($xmldata);
  387. if ($return)
  388. return $xmldata;
  389. else
  390. echo $xmldata;
  391. }
  392. }