[
  {
    "path": ".gitignore",
    "content": "/nbproject/private/\n.buildpath\n/pages/\n/my/"
  },
  {
    "path": "Codeigniter/CI_Wechat.php",
    "content": "<?php\ndefined('BASEPATH') OR exit('No direct script access allowed');\n\n/**\n *  微信公众平台PHP-SDK, Codeigniter实例\n *  @author nigelvon@gmail.com\n *  @link https://github.com/dodgepudding/wechat-php-sdk\n *  usage:\n *  $this->load->library('CI_Wechat');\n *  $this->ci_wechat->valid();\n *  ...\n *\n */\nrequire_once(dirname(__FILE__) . '/wechat-php-sdk/wechat.class.php');\n\nclass CI_Wechat extends Wechat {\n    protected $_CI;\n    public function __construct() {\n        $this->_CI =& get_instance();\n        $this->_CI->config->load('wechat');\n        $options = $this->_CI->config->item('wechat');\n\n        $this->_CI->load->driver('cache', array('adapter' => 'apc', 'backup' => 'file'));\n\n        parent::__construct($options);\n    }\n\n    /**\n     * 重载设置缓存\n     * @param string $cachename\n     * @param mixed $value\n     * @param int $expired\n     * @return boolean\n     */\n    protected function setCache($cachename, $value, $expired) {\n        return $this->_CI->cache->save($cachename, $value, $expired);\n    }\n\n    /**\n     * 重载获取缓存\n     * @param string $cachename\n     * @return mixed\n     */\n    protected function getCache($cachename) {\n        return $this->_CI->cache->get($cachename);\n    }\n\n    /**\n     * 重载清除缓存\n     * @param string $cachename\n     * @return boolean\n     */\n    protected function removeCache($cachename) {\n        return $this->_CI->cache->delete($cachename);\n    }\n}\n\n/* End of file CI_Wechat.php */\n/* Location: ./application/libraries/CI_Wechat.php */\n"
  },
  {
    "path": "README.md",
    "content": "wechat-php-sdk\n==============\n\n微信公众平台php开发包,细化各项接口操作,支持链式调用,欢迎Fork此项目  \nweixin developer SDK.\n项目地址：**https://github.com/dodgepudding/wechat-php-sdk**  \n项目blog：**http://binsee.github.io/wechat-php-sdk**  \n\n## 使用详解\n使用前需先打开微信帐号的开发模式，详细步骤请查看微信公众平台接口使用说明：  \n微信公众平台： http://mp.weixin.qq.com/wiki/\n微信企业平台： http://qydev.weixin.qq.com/wiki/\n\n微信支付接入文档：\nhttps://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl&lang=zh_CN\n\n微信多客服：http://dkf.qq.com\n\n\n## 目录 \n> **[wechat.class.php 官方API类库](#user-content-1-wechatclassphp-官方api类库)**  \n> **[qywechat.class.php 企业号API类库](#user-content-6-qywechatclassphp-企业号api类库)**  \n> **[errCode.php|qyerrCode.php 全局返回码类](#user-content-5-errcodephp-全局返回码类)**  \n> **[old_version/wechatpay.class.php 旧版微信支付V2接口类库](#user-content-7-wechatpayclassphp-旧版微信支付V2接口类库)**  \n> ~~**[old_version/wechatext.class.php 非官方扩展API(停止维护)](#user-content-2-wechatextclassphp-非官方扩展api)**~~  \n> ~~**[old_version/wechatauth.class.php 授权登陆(停止维护)](#user-content-3-wechatauthclassphp-授权登陆)**~~  \n> ~~**[old_version/wechat.js 内嵌JS(已废弃)](#user-content-4-wechatjs-内嵌js)**~~  \n> **[为开发框架进行适配](#user-content-为开发框架进行适配)**  \n> **[调用示例](#user-content-调用示例)**  \n\n----------\n\n## 1. wechat.class.php 官方API类库\n调用官方API，具有更灵活的消息分类响应方式，支持链式调用操作 ； \n\n### 主要功能 \n- 接入验证 **（初级权限）**\n- 自动回复（文本、图片、语音、视频、音乐、图文） **（初级权限）**\n- 菜单操作（查询、创建、删除） **（菜单权限）**\n- 客服消息（文本、图片、语音、视频、音乐、图文） **（认证权限）**\n- 二维码（创建临时、永久二维码，获取二维码URL） **（服务号、认证权限）**\n- 长链接转短链接接口 **（服务号、认证权限）**\n- 分组操作（查询、创建、修改、移动用户到分组） **（认证权限）**\n- 网页授权（基本授权，用户信息授权） **（服务号、认证权限）**\n- 用户信息（查询用户基本信息、获取关注者列表） **（认证权限）**\n- 多客服功能（客服管理、获取客服记录、客服会话管理） **（认证权限）**\n- 媒体文件（上传、获取） **（认证权限）**\n- 高级群发 **（认证权限）**\n- 模板消息（设置所属行业、添加模板、发送模板消息） **（服务号、认证权限）**\n- 卡券管理（创建、修改、删除、发放、门店管理等） **（认证权限）**\n- 语义理解 **（服务号、认证权限）**\n- 获取微信服务器IP列表 **（初级权限）**  \n- 微信JSAPI授权(获取ticket、获取签名) **（初级权限）**  \n- 数据统计(用户、图文、消息、接口分析数据) **（认证权限）**  \n> 备注：  \n> 初级权限：基本权限，任何正常的公众号都有此权限  \n> 菜单权限：正常的服务号、认证后的订阅号拥有此权限  \n> 认证权限：分为订阅号、服务号认证，如前缀服务号则仅认证的服务号有此权限，否则为认证后的订阅号、服务号都有此权限  \n> 支付权限：仅认证后的服务号可以申请此权限  \n\n\n### 初始化动作 \n```php\n $options = array(\n\t'token'=>'tokenaccesskey', //填写你设定的key\n\t'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey\n\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询\n\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥\n\t);\n $weObj = new Wechat($options); //创建实例对象\n //TODO：调用$weObj各实例方法\n```\n\n### 被动接口方法:   \n* valid() 验证连接，被动接口处于加密模式时必须调用\n* \n* getRev() 获取微信服务器发来信息(不返回结果)，被动接口必须调用\n* getRevData() 返回微信服务器发来的信息（数组）\n* getRevFrom()  返回消息发送者的userid\n* getRevTo()  返回消息接收者的id（即公众号id）\n* getRevType() 返回接收消息的类型\n* getRevID() 返回消息id\n* getRevCtime() 返回消息发送时间\n* getRevContent() 返回消息内容正文或语音识别结果（文本型）\n* getRevPic() 返回图片信息（图片型信息） 返回数组{'mediaid'=>'','picurl'=>''}\n* getRevLink() 接收消息链接（链接型信息） 返回数组{'url'=>'','title'=>'','description'=>''}\n* getRevGeo() 返回地理位置（位置型信息） 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''}\n* getRevEventGeo() 返回事件地理位置（事件型信息） 返回数组{'x'=>'','y'=>'','precision'=>''}\n* getRevEvent() 返回事件类型（事件型信息） 返回数组{'event'=>'','key'=>''}\n* getRevScanInfo() 获取自定义菜单的扫码推事件信息，事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123')\n* getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明\n* getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送，事件类型为`location_select` 数组结构见php文件内方法说明\n* getRevVoice() 返回语音信息（语音型信息） 返回数组{'mediaid'=>'','format'=>''}\n* getRevVideo() 返回视频信息（视频型信息） 返回数组{'mediaid'=>'','thumbmediaid'=>''}\n* getRevTicket() 返回接收TICKET（扫描带参数二维码,关注或SCAN事件） 返回二维码的ticket值\n* getRevSceneId() 返回二维码的场景值（扫描带参数二维码的关注事件） 返回二维码的参数值\n* getRevTplMsgID() 返回主动推送的消息ID（群发或模板消息事件） 返回MsgID值\n* getRevStatus() 返回模板消息发送状态（模板消息事件） 返回文本：success(成功)|failed:user block(用户拒绝接收)|failed: system failed(发送失败（非用户拒绝）)\n* getRevResult() 返回群发或模板消息发送结果（群发或模板消息事件） 返回数组，内容依事件类型而不同，参考开发文档中群发、模板消息推送事件\n* getRevKFCreate() 返回多客服-接入会话的客服账号（多客服-接入会话事件） 返回文本型\n* getRevKFClose() 返回多客服-处理会话的客服账号（多客服-接入会话事件） 返回文本型\n* getRevKFSwitch() 返回多客服-转接会话信息（多客服-转接会话事件） 返回数组\t{'FromKfAccount' => '','ToKfAccount' => ''}\n* getRevCardPass() 返回卡券-审核通过的卡券ID（卡券-卡券审核事件） 返回文本型\n* getRevCardGet() 返回卡券-用户领取卡券的相关信息（卡券-领取卡券事件） 返回数组{'CardId' => '','IsGiveByFriend' => '','UserCardCode' => ''}\n* getRevCardDel() 返回卡券-用户删除卡券的相关信息（卡券-删除卡券事件） 返回数组{'CardId' => '','UserCardCode' => ''}\n* \n* text($text) 设置文本型消息，参数：文本内容\n* image($mediaid) 设置图片型消息，参数：图片的media_id\n* voice($mediaid) 设置语音型消息，参数：语音的media_id\n* video($mediaid='',$title,$description) 设置视频型消息，参数：视频的media_id、标题、摘要\n* music($title,$desc,$musicurl,$hgmusicurl='',$thumbmediaid='') 设置回复音乐，参数：音乐标题、音乐描述、音乐链接、高音质链接、缩略图的媒体id\n* news($newsData) 设置图文型消息，参数：数组。数组结构见php文件内方法说明\n* Message($msg = '',$append = false) 设置发送的消息（一般不需要调用这个方法）\n* transfer_customer_service($customer_account = '') 转接多客服，如不指定客服可不提供参数，参数：指定客服的账号\n* reply() 将以上已经设置好的消息，回复给微信服务器\n\n### 预定义常量列表：\n```php\n////消息类型，使用实例调用getRevType()方法取得\nconst MSGTYPE_TEXT = 'text';\nconst MSGTYPE_IMAGE = 'image';\nconst MSGTYPE_LOCATION = 'location';\nconst MSGTYPE_LINK = 'link';\nconst MSGTYPE_EVENT = 'event';\nconst MSGTYPE_MUSIC = 'music';\nconst MSGTYPE_NEWS = 'news';\nconst MSGTYPE_VOICE = 'voice';\nconst MSGTYPE_VIDEO = 'video';\n////事件类型，使用实例调用getRevEvent()方法取得\nconst EVENT_SUBSCRIBE = 'subscribe';       //订阅\nconst EVENT_UNSUBSCRIBE = 'unsubscribe';   //取消订阅\nconst EVENT_SCAN = 'SCAN';                 //扫描带参数二维码\nconst EVENT_LOCATION = 'LOCATION';         //上报地理位置\nconst EVENT_MENU_VIEW = 'VIEW';                     //菜单 - 点击菜单跳转链接\nconst EVENT_MENU_CLICK = 'CLICK';                   //菜单 - 点击菜单拉取消息\nconst EVENT_MENU_SCAN_PUSH = 'scancode_push';       //菜单 - 扫码推事件(客户端跳URL)\nconst EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL)\nconst EVENT_MENU_PIC_SYS = 'pic_sysphoto';          //菜单 - 弹出系统拍照发图\nconst EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album';  //菜单 - 弹出拍照或者相册发图\nconst EVENT_MENU_PIC_WEIXIN = 'pic_weixin';         //菜单 - 弹出微信相册发图器\nconst EVENT_MENU_LOCATION = 'location_select';      //菜单 - 弹出地理位置选择器\nconst EVENT_SEND_MASS = 'MASSSENDJOBFINISH';        //发送结果 - 高级群发完成\nconst EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果\nconst EVENT_KF_SEESION_CREATE = 'kfcreatesession';  //多客服 - 接入会话\nconst EVENT_KF_SEESION_CLOSE = 'kfclosesession';    //多客服 - 关闭会话\nconst EVENT_KF_SEESION_SWITCH = 'kfswitchsession';  //多客服 - 转接会话\nconst EVENT_CARD_PASS = 'card_pass_check';          //卡券 - 审核通过\nconst EVENT_CARD_NOTPASS = 'card_not_pass_check';   //卡券 - 审核未通过\nconst EVENT_CARD_USER_GET = 'user_get_card';        //卡券 - 用户领取卡券\nconst EVENT_CARD_USER_DEL = 'user_del_card';        //卡券 - 用户删除卡券\n```\n\n### 主动接口方法:   \n *  checkAuth($appid,$appsecret,$token) 此处传入公众后台高级接口提供的appid和appsecret, 或者手动指定$token为access_token。函数将返回access_token操作令牌\n *  resetAuth($appid='') 删除验证数据\n *  resetJsTicket($appid='') 删除JSAPI授权TICKET\n *  getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET\n *  getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组，可只提供url地址 \n *  createMenu($data) 创建菜单 $data菜单结构详见 **[自定义菜单创建接口](http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口)**\n *  getServerIp() 获取微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1')\n *  getMenu() 获取菜单 \n *  deleteMenu() 删除菜单 \n *  uploadMedia($data, $type) 上传临时素材，有效期为3天(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时)\n *  getMedia($media_id,$is_video=false) 获取临时素材（含接收到的音频、视频媒体文件）\n *  uploadForeverMedia($data, $type,$is_video=false,$video_info=array()) 上传永久素材，可以在公众平台官网素材管理模块中看到\n *  uploadForeverArticles($data) 上传永久图文素材\n *  updateForeverArticles($media_id,$data,$index=0) 修改永久图文素材(认证后的订阅号可用)\n *  getForeverMedia($media_id,$is_video=false) 获取永久素材\n *  delForeverMedia($media_id) 删除永久素材\n *  getForeverList($type,$offset,$count) 获取永久素材列表(认证后的订阅号可用)\n *  getForeverCount() 获取永久素材总数\n *  uploadMpVideo($data) 上传视频素材，当需要群发视频时，必须使用此方法得到的MediaID，否则无法显示\n *  uploadArticles($data) 上传图文消息素材\n *  sendMassMessage($data) 高级群发消息\n *  sendGroupMassMessage($data) 高级群发消息（全体或分组群发）\n *  deleteMassMessage($msg_id) 删除群发图文消息\n *  previewMassMessage($data) 预览群发消息\n *  queryMassMessage($msg_id) 查询群发消息发送状态\n *  getQRCode($scene_id,$type=0,$expire=1800) 获取推广二维码ticket字串 \n *  getQRUrl($ticket) 获取二维码图片地址\n *  getShortUrl($long_url) 长链接转短链接接口\n *  getUserList($next_openid) 批量获取关注用户列表 \n *  getUserInfo($openid) 获取关注者详细信息 \n *  updateUserRemark($openid,$remark) 设置用户备注名\n *  getGroup() 获取用户分组列表 \n *  getUserGroup($openid) 获取用户所在分组\n *  createGroup($name) 新增自定分组 \n *  updateGroup($groupid,$name) 更改分组名称 \n *  updateGroupMembers($groupid,$openid) 移动用户分组  \n *  batchUpdateGroupMembers($groupid,$openid_list) 批量移动用户分组 \n *  sendCustomMessage($data) 发送客服消息  \n *  getOauthRedirect($callback,$state,$scope) 获取网页授权oAuth跳转地址  \n *  getOauthAccessToken() 通过回调的code获取网页授权access_token  \n *  getOauthRefreshToken($refresh_token) 通过refresh_token对access_token续期  \n *  getOauthUserinfo($access_token,$openid) 通过网页授权的access_token获取用户资料  \n *  getOauthAuth($access_token,$openid)  检验授权凭证access_token是否有效\n *  getSignature($arrdata,'sha1') 生成签名字串  \n *  generateNonceStr($length=16) 获取随机字串  \n *  setTMIndustry($id1,$id2='') 模板消息，设置所属行业\n *  addTemplateMessage($tpl_id) 模板消息，添加消息模板\n *  sendTemplateMessage($data) 发送模板消息\n *  \n *  多客服接口：\n *  getCustomServiceMessage($data) 获取多客服会话记录\n *  transfer_customer_service($customer_account) 转发多客服消息\n *  getCustomServiceKFlist() 获取多客服客服基本信息\n *  getCustomServiceOnlineKFlist() 获取多客服在线客服接待信息\n *  createKFSession($openid,$kf_account,$text='') 创建指定多客服会话\n *  closeKFSession($openid,$kf_account,$text='') 关闭指定多客服会话\n *  getKFSession($openid) 获取用户会话状态\n *  getKFSessionlist($kf_account) 获取指定客服的会话列表\n *  getKFSessionWait() 获取未接入会话列表\n *  addKFAccount($account,$nickname,$password) 添加客服账号\n *  updateKFAccount($account,$nickname,$password) 修改客服账号信息\n *  deleteKFAccount($account) 删除客服账号\n *  setKFHeadImg($account,$imgfile) 上传客服头像\n *  \n *  querySemantic($uid,$query,$category,$latitude=0,$longitude=0,$city=\"\",$region=\"\") 语义理解接口 参数含义及返回的json内容请查看 **[微信语义理解接口](http://mp.weixin.qq.com/wiki/index.php?title=语义理解)**\n *  getDatacube($type,$subtype,$begin_date,$end_date='') 获取统计数据 参数需注意$type与$subtype的定义\n> 获取统计数据方法 参数定义\n> \n| 数据分类 | $type值(字符串)  | 数据子分类 | $subtype值(字符串) | 时间跨度(天) |\n| --------- | :-------:  | --------- | :------: | ----: |\n| 用户分析 | 'user' | 获取用户增减数据 | 'summary' | 7 |\n| 用户分析 | 'user' | 获取累计用户数据 | 'cumulate' | 7 |\n| 图文分析 | 'article' | 获取图文群发每日数据 | 'summary' | 1 |\n| 图文分析 | 'article' | 获取图文群发总数据 | 'total' | 1 |\n| 图文分析 | 'article' | 获取图文统计数据 | 'read' | 3 |\n| 图文分析 | 'article' | 获取图文统计分时数据 | 'readhour' | 1 |\n| 图文分析 | 'article' | 获取图文分享转发数据 | 'share' | 7 |\n| 图文分析 | 'article' | 获取图文分享转发分时数据 | 'sharehour' | 1 |\n| 消息分析 | 'upstreammsg' | 获取消息发送概况数据 | 'summary' | 7 |\n| 消息分析 | 'upstreammsg' | 获取消息分送分时数据 | 'hour' | 1 |\n| 消息分析 | 'upstreammsg' | 获取消息发送周数据 | 'week' | 30 |\n| 消息分析 | 'upstreammsg' | 获取消息发送月数据 | 'month' | 30 |\n| 消息分析 | 'upstreammsg' | 获取消息发送分布数据 | 'dist' | 15 |\n| 消息分析 | 'upstreammsg' | 获取消息发送分布周数据 | 'distweek' | 30 |\n| 消息分析 | 'upstreammsg' | 获取消息发送分布月数据 | 'distmonth' | 30 |\n| 接口分析 | 'interface' | 获取接口分析数据 | 'summary' | 30 |\n| 接口分析 | 'interface' | 获取接口分析分时数据 | 'summaryhour' | 1 |\n需要注意 `begin_date`和`end_date`的差值需小于“最大时间跨度”（比如最大时间跨度为1时，`begin_date`和`end_date`的差值只能为0，才能小于1）\n\n *  \n *  卡券接口：\n *  createCard($data) 创建卡券\n *  updateCard($data) 修改卡券\n *  delCard($card_id) 删除卡券\n *  getCardInfo($card_id) 查询卡券详情\n *  getCardColors() 获取颜色列表\n *  getCardLocations() 拉取门店列表\n *  addCardLocations($data) 批量导入门店信息\n *  createCardQrcode($card_id) 生成卡券二维码\n *  consumeCardCode($code) 消耗 code\n *  decryptCardCode($encrypt_code) code 解码\n *  checkCardCode($code) 获取 code 的有效性\n *  getCardIdList($data) 批量查询卡列表\n *  updateCardCode($code,$card_id,$new_code) 更改 code\n *  unavailableCardCode($code,$card_id='') 设置卡券失效**(不可逆)**\n *  modifyCardStock($data) 库存修改\n *  activateMemberCard($data) 激活/绑定会员卡，参数结构请参看卡券开发文档(6.1.1 激活/绑定会员卡)章节\n *  updateMemberCard($data) 会员卡交易，参数结构请参看卡券开发文档(6.1.2 会员卡交易)章节\n *  updateLuckyMoney($code,$balance,$card_id='') 更新红包金额\n *  setCardTestWhiteList($openid=array(),$user=array()) 设置卡券测试白名单\n *  \n *  摇一摇周边接口：\n *  applyShakeAroundDevice($data) 申请设备ID\n *  updateShakeAroundDevice($data) 编辑设备的备注信息\n *  searchShakeAroundDevice($data) 查询设备列表\n *  bindLocationShakeAroundDevice($device_id,$poi_id,$uuid='',$major=0,$minor=0) 配置设备与门店的关联关系\n *  bindPageShakeAroundDevice($device_id,$page_ids=array(),$bind=1,$append=1,$uuid='',$major=0,$minor=0) 配置设备与页面的关联关系\n *  uploadShakeAroundMedia($data) 上传在摇一摇页面展示的图片素材\n *  addShakeAroundPage($title,$description,$icon_url,$page_url,$comment='') 新增摇一摇出来的页面信息\n *  updateShakeAroundPage($page_id,$title,$description,$icon_url,$page_url,$comment='') 编辑摇一摇出来的页面信息\n *  searchShakeAroundPage($page_ids=array(),$begin=0,$count=1) 查询摇一摇已有的页面\n *  deleteShakeAroundPage($page_ids=array()) 删除摇一摇已有的页面，必须是未与设备关联的页面\n *  getShakeInfoShakeAroundUser($ticket) 获取摇周边的设备及用户信息\n *  deviceShakeAroundStatistics($device_id,$begin_date,$end_date,$uuid='',$major=0,$minor=0) 以设备为维度的数据统计接口\n *  pageShakeAroundStatistics($page_id,$begin_date,$end_date) 以页面为维度的数据统计接口\n \n## ~~2. wechatext.class.php 非官方扩展API~~  \n**此扩展类库已经不再更新，原因是官方对公众号开放了众多接口，此类库继续维护的意义不大**  \n非官方扩展API，需要配置公众平台账户和密码，能实现对已关注用户的点对点微信，此方式不保证长期有效。  \n类方法里提及的用户id在接口返回结构里表述为FakeId, 属同一概念, 在下面wechatauth类里则表示为Uin, 用户id对应的微信号必须通过getInfo()方法通过返回数组的Username值获取, 但非关注关系用户资料不能获取.  \n调用下列方法前必须经过login()方法和checkValid()验证方法才能获得调用权限. 有的账户无法通过登陆可能因为要求提供验证码, 可以手动登陆后把获取到的cookie写进程序存放cookie的文件解决.  \n程序使用了经过修改的snoopy兼容式HTTP类方法, 在类似BAE/SAE云服务器上可能不能正常运行, 因为云服务的curl方法是经过重写的, 某些header参数如网站来源参数不被支持.  \n\n### 类主要方法:\n *  send($id,$content) 向某用户id发送微信文字信息 \n *  sendNews($id,$msgid) 发送图文消息, 可通过getNewsList获取$msgid\n *  getUserList($page,$pagesize,$groupid) 获取用户信息\n *  getGroupList($page,$pagesize) 获取群组信息\n *  getNewsList($page,$pagesize) 获取图文信息列表 \n *  uploadFile($filepath,$type) 上传附件,包括图片/音频/视频/缩略图\n *  getFileList($type,$page,$pagesize) 获取素材库文件列表\n *  sendImage($id,$fid) 发送图片消息\n *  sendAudio($id,$fid) 发送音频消息\n *  sendVideo($id,$fid) 发送视频消息 \n *  getInfo($id) 根据id获取用户资料,注: 非关注关系用户资料不能获取  \n *  getNewMsgNum($lastid) 获取从$lastid算起新消息的数目  \n *  getTopMsg() 获取最新一条消息的数据, 此方法获取的消息id可以作为检测新消息的$lastid依据  \n *  getMsg($lastid,$offset=0,$perpage=50,$day=0,$today=0,$star=0) 获取最新的消息列表, 列表将返回消息id, 用户id, 消息类型, 文字消息等参数  \n *  消息返回结构:  {\"id\":\"消息id\",\"type\":\"类型号(1为文字,2为图片,3为语音)\",\"fileId\":\"0\",\"hasReply\":\"0\",\"fakeId\":\"用户uid\",\"nickName\":\"昵称\",\"dateTime\":\"时间戳\",\"content\":\"文字内容\"}   \n *  getMsgImage($msgid,$mode='large') 若消息type类型为2, 调用此方法获取图片数据  \n *  getMsgVoice($msgid) 若消息type类型为3, 调用此方法获取语音数据  \n\n## ~~3. wechatauth.class.php 授权登陆~~\n**此扩展类库已经不再更新，原因是官方开放平台对网站应用开放的有授权登陆接口，更标准，更好用。请查看：[微信开放平台](http://open.weixin.qq.com)**  \n通过微信二维码登陆微信的API, 能实现第三方网站同步登陆, 首先程序分别通过get_login_code和get_code_image方法获取授权二维码图片, 然后利用微信手机客户端扫描二维码图片后将自动跳出授权页面, 用户点击授权后即可获取对应的用户资料和头像信息. 详细验证步骤请看test3.php例子.   \n### 类主要方法:\n *  get_login_code() 获取登陆授权码, 通过授权码才能获取二维码  \n *  get_code_image($code='') 将上面获取的授权码转换为图片二维码  \n *  verify_code() 鉴定是否登陆成功,返回200为最终授权成功.  \n *  get_login_info() 鉴定成功后调用此方法即可获取用户基本信息  \n *  get_avatar($url) 获取用户头像图片数据  \n *  logout() 注销登陆  \n\n## ~~4. wechat.js 内嵌JS~~\n**此JS脚本已经废弃不再更新，原因是官方在微信6.0.2版本开放了全新的JSAPI接口，更全面好用。请查看：[微信公众平台WIKI](http://mp.weixin.qq.com/wiki)**\n### 微信内嵌网页特殊功能js调用：\n * WeixinJS.hideOptionMenu() 隐藏右上角按钮\n * WeixinJS.showOptionMenu() 显示右上角按钮\n * WeixinJS.hideToolbar() 隐藏工具栏\n * WeixinJS.showToolbar() 显示工具栏\n * WeixinJS.getNetworkType() 获取网络状态\n * WeixinJS.closeWindow() 关闭窗口\n * WeixinJS.scanQRCode() 扫描二维码\n * WeixinJS.openUrlByExtBrowser(url) 使用浏览器打开网址\n * WeixinJS.jumpToBizProfile(username) 跳转到指定公众账号页面\n * WeixinJS.sendEmail(title,content) 发送邮件\n * WeixinJS.openProductView(latitude,longitude,name,address,scale,infoUrl) 查看地图\n * WeixinJS.addContact(username) 添加微信账号\n * WeixinJS.imagePreview(urls,current) 调出微信内图片预览\n * WeixinJS.payCallback(appId,package,timeStamp,nonceStr,signType,paySign,callback) 微信JsApi支付接口\n * WeixinJS.editAddress(appId,addrSign,timeStamp,nonceStr,callback) 微信JsApi支付接口\n * 通过定义全局变量dataForWeixin配置触发分享的内容：\n ```javascript\n var dataForWeixin={\n\t   appId:\"\",\n\t   MsgImg:\"消息图片路径\",\n\t   TLImg:\"时间线图路径\",\n\t   url:\"分享url路径\",\n\t   title:\"标题\",\n\t   desc:\"描述\",\n\t   fakeid:\"\",\n\t   callback:function(){}\n\t};\n ```\n\n## 5. errCode.php 全局返回码类\n当调用API接口失败时，可以用此类来获取失败原因的中文说明。  \n注意：微信公众号引用`errCode.php`，企业号引用`qyerrCode.php`。\n\n### 使用方法：\n```php\ninclude \"errCode.php\";  //或 qyerrCode.php\n\n$ret=ErrCode::getErrText(48001); //错误码可以通过公众号类库的公开变量errCode得到\n\nif ($ret) \n\techo $ret;\nelse \n    echo \"未找到对应的内容\";\n\n```\n\n## 6. qywechat.class.php 企业号API类库 \n调用官方API，具有更灵活的消息分类响应方式，支持链式调用操作 ； \n\n### 主要功能 \n- 接入验证\n- 自动回复（文本、图片、语音、视频、音乐、图文）\n- 菜单操作（查询、创建、删除）\n- 部门管理（创建、更新、删除、获取部门列表）\n- 成员管理（创建、更新、删除、获取成员信息，获取部门成员列表）\n- 标签管理（创建、更新、删除、获取成员、添加成员、删除成员,获取标签列表）\n- 媒体文件管理（上传、获取）\n- 二次验证\n- OAuth2（生成授权url、获取成员信息）\n- 获取企业微信服务器IP列表\n- 微信JSAPI授权(获取ticket、获取签名)\n\n\n### 初始化动作 \n```php\n$options = array(\n  'token'=>'tokenaccesskey', //填写应用接口的Token\n  'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey\n  'appid'=>'wxdk1234567890', //填写高级调用功能的app id\n  'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥\n  'agentid'=>'1', //应用的id\n  'debug'=>false, //调试开关\n  '_logcallback'=>'logg', //调试输出方法，需要有一个string类型的参数\n);\n $weObj = new Wechat($options); //创建实例对象\n //TODO：调用$weObj各实例方法\n\n```\n\n### 被动接口方法:   \n* valid() 验证连接，被动接口必须调用\n* \n* getRev() 获取微信服务器发来信息(不返回结果)，被动接口必须调用\n* getRevData() 返回微信服务器发来的信息（数组）\n* getRevPostXml() 返回微信服务器发来的原始加密xml信息\n* getRevFrom()  返回消息发送者的userid\n* getRevTo()  返回消息接收者的id（即公众号id，一般与等同appid一致）\n* getRevAgentID() 返回接收消息的应用id\n* getRevType() 返回接收消息的类型\n* getRevID() 返回消息id\n* getRevCtime() 返回消息发送事件\n* getRevContent() 返回消息内容正文（文本型消息）\n* getRevPic() 返回图片信息（图片型信息） 返回数组{'mediaid'=>'','picurl'=>''}\n* getRevGeo() 返回地理位置（位置型信息） 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''}\n* getRevEventGeo() 返回事件地理位置（事件型信息） 返回数组{'x'=>'','y'=>'','precision'=>''}\n* getRevEvent() 返回事件类型（事件型信息） 返回数组{'event'=>'','key'=>''}\n* getRevScanInfo() 获取自定义菜单的扫码推事件信息，事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123')\n* getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明\n* getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送，事件类型为`location_select` 数组结构见php文件内方法说明\n* getRevVoice() 返回语音信息（语音型信息） 返回数组{'mediaid'=>'','format'=>''}\n* getRevVideo() 返回视频信息（视频型信息） 返回数组{'mediaid'=>'','thumbmediaid'=>''}\n* \n* text($text) 设置文本型消息，参数：文本内容\n* image($mediaid) 设置图片型消息，参数：图片的media_id\n* voice($mediaid) 设置语音型消息，参数：语音的media_id\n* video($mediaid='',$title,$description) 设置视频型消息，参数：视频的media_id、标题、摘要\n* news($newsData) 设置图文型消息，参数：数组。数组结构见php文件内方法说明\n* image($mediaid) 设置图片型消息，参数：图片的media_id\n* Message($msg = '',$append = false) 设置发送的消息（一般不需要调用这个方法）\n* reply() 将已经设置好的消息，回复给微信服务器\n  \n### 预定义常量列表：\n```php\n////消息类型，使用实例调用getRevType()方法取得\n    const MSGTYPE_TEXT = 'text';\n    const MSGTYPE_IMAGE = 'image';\n    const MSGTYPE_LOCATION = 'location';\n    const MSGTYPE_LINK = 'link';    //暂不支持\n    const MSGTYPE_EVENT = 'event';\n    const MSGTYPE_MUSIC = 'music';    //暂不支持\n    const MSGTYPE_NEWS = 'news';\n    const MSGTYPE_VOICE = 'voice';\n    const MSGTYPE_VIDEO = 'video';\n////事件类型，使用实例调用getRevEvent()方法取得\n    const EVENT_SUBSCRIBE = 'subscribe';       //订阅\n    const EVENT_UNSUBSCRIBE = 'unsubscribe';   //取消订阅\n    const EVENT_LOCATION = 'LOCATION';         //上报地理位置\n    const EVENT_ENTER_AGENT = 'enter_agent';   //用户进入应用\n    const EVENT_MENU_VIEW = 'VIEW';                     //菜单 - 点击菜单跳转链接\n    const EVENT_MENU_CLICK = 'CLICK';                   //菜单 - 点击菜单拉取消息\n    const EVENT_MENU_SCAN_PUSH = 'scancode_push';       //菜单 - 扫码推事件(客户端跳URL)\n    const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL)\n    const EVENT_MENU_PIC_SYS = 'pic_sysphoto';          //菜单 - 弹出系统拍照发图\n    const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album';  //菜单 - 弹出拍照或者相册发图\n    const EVENT_MENU_PIC_WEIXIN = 'pic_weixin';         //菜单 - 弹出微信相册发图器\n    const EVENT_MENU_LOCATION = 'location_select';      //菜单 - 弹出地理位置选择器\n    const EVENT_SEND_MASS = 'MASSSENDJOBFINISH';        //发送结果 - 高级群发完成\n    const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果\n```\n\n### 主动接口方法：\n* checkAuth($appid='',$appsecret='',$token='') 通用auth验证方法,也用来换取ACCESS_TOKEN 。仅在需要手动指定access_token时才用`$token`\n* resetAuth($appid='') 清除记录的ACCESS_TOKEN\n* resetJsTicket($appid='') 删除JSAPI授权TICKET\n* getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET\n* getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组，可只提供url地址 \n* getSignature($arrdata,'sha1') 生成签名字串  \n* generateNonceStr($length=16) 获取随机字串  \n* createMenu($data,$agentid='') 创建菜单,参数:菜单内容数组,要创建菜单应用id\n* getMenu($agentid='') 获取菜单内容,参数:要获取菜单内容的应用id\n* deleteMenu($agentid='') 删除菜单,参数:要删除菜单的应用id\n* uploadMedia($data, $type) 上传媒体文件,参数请看php文件内方法说明(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时)\n* getMedia($media_id) 根据媒体文件ID获取媒体文件,参数:媒体id\n* getServerIp() 获取企业微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1')\n* createDepartment($data) 创建部门,参数: array(\"name\"=>\"邮箱产品组\",\"parentid\"=>\"1\",\"order\" =>  \"1\")\n* updateDepartment($data) 更新部门,参数: array(\"id\"=>\"1\"，\"name\"=>\"邮箱产品组\",\"parentid\"=>\"1\",\"order\" =>  \"1\")\n* deleteDepartment($id) 删除部门,参数：要删除的部门id\n* moveDepartment($data) 移动部门,参数：array(\"department_id\" => \"5\",\"to_parentid\" => \"2\",\"to_position\" => \"1\")\n* getDepartment() 获取部门列表，返回部门数组。其中department部门列表数据。以部门的order字段从小到大排列\n* createUser($data) 创建成员，参数请看php文件内方法说明\n* updateUser($data) 更新成员，参数请看php文件内方法说明\n* deleteUser($userid) 删除成员，参数：员工UserID\n* deleteUsers($userids) 批量删除成员，参数：员工UserID数组\n* getUserInfo($userid) 获取成员信息，参数：员工UserID\n* getUserList($department_id,$fetch_child=0,$status=0) 获取部门成员，参数：部门id，是否递归获取子部门，获取类型。\n> 0获取全部员工，1获取已关注成员列表，2获取禁用成员列表，4获取未关注成员列表。status可叠加\n* getUserListInfo($department_id,$fetch_child=0,$status=0) 获取部门成员详情，参数同上\n* getUserId($code,$agentid) 根据code获取员工UserID与手机设备号，参数：Oauth2.0或者二次验证返回的code值，跳转链接时所在的企业应用ID\n* sendInvite($userid,$invite_tips='') 邀请成员关注\n* createTag($data) 创建标签，参数：array(\"tagname\" => \"UI\")\n* updateTag($data) 更新标签，参数：array(\"tagid\" => \"1\",\"tagname\" => \"UI\")\n* deleteTag($tagid) 删除标签，参数：标签TagID\n* getTag($tagid) 获取标签成员，参数：标签TagID\n* addTagUser($data) 增加标签成员，参数请看php文件内方法说明\n* delTagUser($data) 删除标签成员，参数请看php文件内方法说明\n* getTagList() 获取标签列表，返回标签数组\n* sendMessage($data) 主动发送信息接口，参数请看php文件内方法说明\n* authSucc($userid) 二次验证，参数： 员工UserID\n* getOauthRedirect($callback,$state='STATE',$scope='snsapi_base') 组合授权跳转接口url\n\n\n## 7. wechatpay.class.php 旧版微信支付V2接口类库\n旧版微信支付类库(微信支付V2)，已移动至old_version目录下。  \n自2014年8月开始申请到的微信支付都是V3接口，据官方说V2的会陆续升级为V3接口，但时间及升级渠道未确认。\n\n### 主要功能 \n- 获取access_token **（初级权限）**\n- 调用地址组件 **（支付权限）**\n- 生成订单签名数据 **（支付权限）**\n- 订单成功回调 **（支付权限）**\n- 发货通知 **（支付权限）**\n- 支付订单查询 **（支付权限）**  \n> 备注：  \n> 初级权限：基本权限，任何正常的公众号都有此权限  \n> 菜单权限：正常的服务号、认证后的订阅号拥有此权限  \n> 认证权限：分为订阅号、服务号认证，如前缀服务号则仅认证的服务号有此权限，否则为认证后的订阅号、服务号都有此权限  \n> 支付权限：仅认证后的服务号可以申请此权限  \n\n\n### 初始化动作 \n```php\n $options = array(\n\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询\n\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥\n\t'partnerid'=>'88888888', //财付通商户身份标识，支付权限专用，没有可不填\n\t'partnerkey'=>'', //财付通商户权限密钥Key，支付权限专用\n\t'paysignkey'=>'' //商户签名密钥Key，支付权限专用\n\t);\n $weObj = new Wechat($options); //创建实例对象\n //TODO：调用$weObj各实例方法\n```\n\n### 主动接口方法:   \n *  checkAuth($appid='',$appsecret='',$token='') 获取access_token。可根据appid和appsecret获取，或手动指定access_token\n *  resetAuth($appid='') 删除验证数据\n *  getSignature($arrdata,'sha1') 生成签名字串  \n *  generateNonceStr($length=16) 获取随机字串  \n *  createNativeUrl($productid) 生成原生支付url\n *  createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type=1,$bank_type=\"WX\",$input_charset=\"UTF-8\",$time_start=\"\",$time_expire=\"\",$transport_fee=\"\",$product_fee=\"\",$goods_tag=\"\",$attach=\"\") 生成订单package字符串  \n *  getPaySign($package, $timeStamp, $nonceStr) 支付签名(paySign)生成方法  \n *  checkOrderSignature($orderxml='') 回调通知签名验证  \n *  sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok') 发货通知  \n *  getPayOrder($out_trade_no) 查询订单信息  \n *  setUserToken($user_token) 设置用户授权密钥\n *  getAddrSign($url, $timeStamp, $nonceStr, $user_token='') 获取收货地址JS的签名\n\n\n## 为开发框架进行适配\n为不同的开发框架进行适配缓存操作(保存access_token、jsapi_ticket)，及输出调试日志。\n\n由于微信api需要缓存access_token与jsapi_ticket，而在不同框架下的缓存方式不同，所以原先在Wechat.class.php和QYWechat.class.php中缓存代码做了TODO标志。\n需要各位在使用不同框架时再进行修改，但确实很麻烦，因为对结构进行了修改。\n\n>取消了原先同步维护的Thinkphp版本，为Wechat类增加操作缓存3个重载方法`setCache`, `getCache`, `removeCache`，以及修改`log`方法可以重载。\n>分别来实现在不同开发框架下的设置缓存、读取缓存、清除缓存、日志输出4个功能。  \n\n在不同的开发框架下使用Wechat类库，请继承Wechat类，根据需要实现这4个方法。  \n可参考Thinkphp版的`TPWechat.class.php`为不同框架进行适配。\n欢迎提交其他框架的适配文件到项目库来。  \n\n为Thinkphp进行适配的示例如下：\n```php\n/**\n *\t微信公众平台PHP-SDK, ThinkPHP实例\n *  @author dodgepudding@gmail.com\n *  @link https://github.com/dodgepudding/wechat-php-sdk\n *  @version 1.2\n *  usage:\n *   $options = array(\n *\t\t\t'token'=>'tokenaccesskey', //填写你设定的key\n *\t\t\t'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey\n *\t\t\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id\n *\t\t\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥\n *\t\t);\n *\t $weObj = new TPWechat($options);\n *   $weObj->valid();\n *   ...\n *  \n */\nclass TPWechat extends Wechat\n{\n\t/**\n\t * log overwrite\n\t * @see Wechat::log()\n\t */\n\tprotected function log($log){\n\t\tif ($this->debug) {\n\t\t\tif (function_exists($this->logcallback)) {\n\t\t\t\tif (is_array($log)) $log = print_r($log,true);\n\t\t\t\treturn call_user_func($this->logcallback,$log);\n\t\t\t}elseif (class_exists('Log')) {\n\t\t\t\tLog::write('wechat：'.$log, Log::DEBUG);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 重载设置缓存\n\t * @param string $cachename\n\t * @param mixed $value\n\t * @param int $expired\n\t * @return boolean\n\t */\n\tprotected function setCache($cachename,$value,$expired){\n\t\treturn S($cachename,$value,$expired);\n\t}\n\t\n\t/**\n\t * 重载获取缓存\n\t * @param string $cachename\n\t * @return mixed\n\t */\n\tprotected function getCache($cachename){\n\t\treturn S($cachename);\n\t}\n\t\n\t/**\n\t * 重载清除缓存\n\t * @param string $cachename\n\t * @return boolean\n\t */\n\tprotected function removeCache($cachename){\n\t\treturn S($cachename,null);\n\t}\n}\n```\n\n\n\n# 调用示例\n----------\n\n## 官方Wechat调用示例：\n```php\n//test1.php\ninclude \"wechat.class.php\";\n$options = array(\n\t\t'token'=>'tokenaccesskey', //填写你设定的key\n        'encodingaeskey'=>'encodingaeskey' //填写加密用的EncodingAESKey，如接口为明文模式可忽略\n\t);\n$weObj = new Wechat($options);\n$weObj->valid();//明文或兼容模式可以在接口验证通过后注释此句，但加密模式一定不能注释，否则会验证失败\n$type = $weObj->getRev()->getRevType();\nswitch($type) {\n\tcase Wechat::MSGTYPE_TEXT:\n\t\t\t$weObj->text(\"hello, I'm wechat\")->reply();\n\t\t\texit;\n\t\t\tbreak;\n\tcase Wechat::MSGTYPE_EVENT:\n\t\t\tbreak;\n\tcase Wechat::MSGTYPE_IMAGE:\n\t\t\tbreak;\n\tdefault:\n\t\t\t$weObj->text(\"help info\")->reply();\n}\n```\n\n## 企业号API类库调用示例：\n可参考**test**目录下的**qydemo.php**\n```php\ninclude \"wechat.class.php\";\n$options = array(\n        'token'=>'9Ixxxxxxx',\t//填写应用接口的Token\n        'encodingaeskey'=>'d4o9WVg8sxxxxxxxxxxxxxxxxxxxxxx',//填写加密用的EncodingAESKey\n        'appid'=>'wxa07979baxxxxxxxx',\t//填写高级调用功能的appid\n);\n$weObj = new Wechat($options);\n$weObj->valid(); //注意, 企业号与普通公众号不同，必须打开验证，不要注释掉\n$type = $weObj->getRev()->getRevType();\nswitch($type) {\n\tcase Wechat::MSGTYPE_TEXT:\n\t\t\t$weObj->text(\"hello, I'm wechat\")->reply();\n\t\t\texit;\n\t\t\tbreak;\n\tcase Wechat::MSGTYPE_EVENT:\n\t\t\tbreak;\n\tcase Wechat::MSGTYPE_IMAGE:\n\t\t\tbreak;\n\tdefault:\n\t\t\t$weObj->text(\"help info\")->reply();\n}\n```\n\n## 扩展包Wechatext调用示例: \n```php\n// old_version/test/test2.php \n\tinclude \"wechatext.class.php\";\n\t\n\tfunction logdebug($text){\n\t\tfile_put_contents('./data/log.txt',$text.\"\\n\",FILE_APPEND);\t\t\n\t};\n\t\n\t$options = array(\n\t\t'account'=>'demo@domain.com',\n\t\t'password'=>'demo',\n\t\t'datapath'=>'./data/cookie_',\n\t\t\t'debug'=>true,\n\t\t\t'logcallback'=>'logdebug'\t\n\t); \n\t$wechat = new Wechatext($options);\n\tif ($wechat->checkValid()) {\n\t\t// 获取用户信息\n\t\t$data = $wechat->getInfo('3974255');\n\t\tvar_dump($data);\n\t\t// 获取最新一条消息\n\t\t$topmsg = $wechat->getTopMsg();\n\t\tvar_dump($topmsg);\n\t\t// 主动回复消息\n\t\tif ($topmsg && $topmsg['has_reply']==0)\n\t\t$wechat->send($topmsg['fakeid'],'hi '.$topmsg['nick_name'].',rev:'.$topmsg['content']);\t\n\t}\n```\n\n## 微信二维码Wechatauth登陆示例: \n```php\n// old_version/test/test3.php\n\tinclude \"../wechatauth.class.php\";\n\tsession_start();\n\t$sid  = session_id();\n\t$options = array(\n\t\t'account'=>$sid,\n\t\t'datapath'=>'../data/cookiecode_',\n\t); \n\t$wechat = new Wechatauth($options);\n\t\n\tif (isset($_POST['code'])) {\n\t\t$logincode = $_POST['code'];\n\t\t$vres = $wechat->set_login_code($logincode)->verify_code();\n\t\tif ($vres===false) {\n\t\t\t$result = array('status'=>0);\n\t\t} else {\n\t\t\t$result = array('status'=>$vres);\n\t\t\tif ($vres==200) {\n\t\t\t\t$result['info'] = $wechat->get_login_info();\n\t\t\t\t$result['cookie'] = $wechat->get_login_cookie(true);\n\t\t\t}\n\t\t}\n\t\t\n\t\tdie(json_encode($result));\t\n\t}\n\t$logincode =  $wechat->get_login_code(); //获取授权码\n\t$qrimg = $wechat->get_code_image(); //待输出的二维码图片\n```\nHTML部分请看old_version/test/test3.php, 主要是定时ajax查询是否已经授权成功\n\n## 新版微信JSAPI调用DEMO: \n请看test/jsapi目录\n\nLicense\n-------\nThis is licensed under the GNU LGPL, version 2.1 or later.   \nFor details, see: http://creativecommons.org/licenses/LGPL/2.1/\n"
  },
  {
    "path": "Thinkphp/EasyWechat.class.php",
    "content": "<?php\n/**\n *    微信公众平台PHP-SDK, 简单缓存实例\n *  @author binsee@163.com\n *  @link https://github.com/binsee/wechat-php-sdk\n *  @version 0.1\n *  usage:\n *   $options = array(\n *            'token'=>'tokenaccesskey', //填写你设定的key\n *            'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey\n *            'appid'=>'wxdk1234567890', //填写高级调用功能的app id\n *            'cachedir'=>'./cache/', //填写缓存目录，默认为当前运行目录的子目录cache下\n *            'logfile'=>'run.log' //填写日志输出文件，可选项。如果没有提供logcallback回调，且设置了输出文件则将日志输出至此文件，如果省略则不输出\n *        );\n *     $weObj = new EasyWechat($options);\n *   $weObj->valid();\n *   ...\n *\n */\nclass EasyWechat extends Wechat\n{\n    private $cachedir = '';\n    private $logfile = '';\n\n    public function __construct($options)\n    {\n        $this->cachedir = isset($options['cachedir']) ? dirname($options['cachedir'].'/.cache') . '/' : 'cache/';\n        $this->logfile = isset($options['logfile']) ? $options['logfile'] : '';\n        if ($this->cachedir) $this->checkDir($this->cachedir);\n        parent::__construct($options);\n    }\n\n    /**\n     * log overwrite\n     * @param string|array $log\n     */\n    protected function log($log){\n        if (is_array($log)) $log = print_r($log,true);\n        if ($this->debug) {\n            if (function_exists($this->logcallback)) {\n                return call_user_func($this->logcallback,$log);\n            }elseif ($this->logfile) {\n                return file_put_contents($this->logfile, $log.\"\\n\", FILE_APPEND) > 0 ? true : false;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 重载设置缓存\n     * @param string $cachename\n     * @param mixed $value\n     * @param int $expired 缓存秒数，如果为0则为长期缓存\n     * @return boolean\n     */\n    protected function setCache($cachename,$value,$expired=0){\n        $file = $this->cachedir . $cachename . '.cache';\n        $data = array(\n                'value' => $value,\n                'expired' => $expired ? time() + $expired : 0\n        );\n        return file_put_contents( $file, serialize($data) ) ? true : false;\n    }\n\n    /**\n     * 重载获取缓存\n     * @param string $cachename\n     * @return mixed\n     */\n    protected function getCache($cachename){\n        $file = $this->cachedir . $cachename . '.cache';\n        if (!is_file($file)) {\n           return false;\n        }\n        $data = unserialize(file_get_contents( $file ));\n        if (!is_array($data) || !isset($data['value']) || (!empty($data['value']) && $data['expired']<time())) {\n            @unlink($file);\n            return false;\n        }\n        return $data['value'];\n    }\n\n    /**\n     * 重载清除缓存\n     * @param string $cachename\n     * @return boolean\n     */\n    protected function removeCache($cachename){\n        $file = $this->cachedir . $cachename . '.cache';\n        if ( is_file($file) ) {\n            @unlink($file);\n        }\n        return true;\n    }\n\n    private function checkDir($dir, $mode=0777) {\n        if (!$dir)  return false;\n        if(!is_dir($dir)) {\n            if (!file_exists($dir) && @mkdir($dir, $mode, true))\n                return true;\n            return false;\n        }\n        return true;\n    }\n}\n\n\n\n"
  },
  {
    "path": "Thinkphp/JsSdkPay.class.php",
    "content": "<?php\n/**\n * 官方文档：http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html\n * 微信支付：http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=9_1#\n * 官方示例：http://demo.open.weixin.qq.com/jssdk/sample.zip\n * UCToo示例:http://git.oschina.net/uctoo/uctoo/blob/master/Addons/Jssdk/Controller/JssdkController.class.php\n * \n * 微信JSSDK支付类,主要实现了微信JSSDK支付，参考官方提供的示例文档，\n * @命名空间版本\n * @author uctoo (www.uctoo.com)\n * @date 2015-5-15 14:10\n */\nnamespace Com;\n\nclass JsSdkPay {\n  private $appId;\n  private $appSecret;\n  public $debug = false;\n  public $weObj;      //微信类实例\n  public $parameters;//获取prepay_id时的请求参数\n  //受理商ID，身份标识\n  public $MCHID = '';\n  //商户支付密钥Key。审核通过后，在微信商户平台中查看 https://pay.weixin.qq.com\n  public $KEY = '';\n\n  //=======【JSAPI路径设置】===================================\n  //获取access_token过程中的跳转uri，通过跳转将code传入jsapi支付页面\n  public $JS_API_CALL_URL = '';\n\n  //=======【证书路径设置】=====================================\n  //证书路径,注意应该填写绝对路径\n  public $SSLCERT_PATH = '/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_cert.pem';\n  public $SSLKEY_PATH = '/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_key.pem';\n\n  //=======【异步通知url设置】===================================\n  //异步通知url，商户根据实际开发过程设定\n  //C('url').\"admin.php/order/notify_url.html\";\n  public $NOTIFY_URL = '';\n\n  //=======【curl超时设置】===================================\n  //本例程通过curl使用HTTP POST方法，此处可修改其超时时间，默认为30秒\n  public  $CURL_TIMEOUT = 30;\n\n  public  $prepay_id;\n\n  public function __construct($options) {\n    $this->appId = $options['appid'];\n    $this->appSecret = $options['appsecret'];\n    $this->weObj = new TPWechat($options);\n  }\n\n  //微信支付相关方法\n  /**\n   * \t作用：格式化参数，签名过程需要使用\n   */\n  function formatBizQueryParaMap($paraMap, $urlencode)\n  {\n    $buff = \"\";\n    ksort($paraMap);\n    foreach ($paraMap as $k => $v)\n    {\n      if($urlencode)\n      {\n        $v = urlencode($v);\n      }\n      //$buff .= strtolower($k) . \"=\" . $v . \"&\";\n      $buff .= $k . \"=\" . $v . \"&\";\n    }\n    $reqPar = \"\";\n    if (strlen($buff) > 0)\n    {\n      $reqPar = substr($buff, 0, strlen($buff)-1);\n    }\n    return $reqPar;\n  }\n  /**\n   * \t作用：设置jsapi的参数\n   */\n  public function getParameters()\n  {\n    $jsApiObj[\"appId\"] = $this->appId;           //请求生成支付签名时需要,js调起支付参数中不需要\n    $timeStamp = time();\n    $jsApiObj[\"timeStamp\"] = \"$timeStamp\";      //用大写的timeStamp参数请求生成支付签名\n    $jsParamObj[\"timestamp\"] = $timeStamp;      //用小写的timestamp参数生成js支付参数，还要注意数据类型，坑！\n    $jsParamObj[\"nonceStr\"] = $jsApiObj[\"nonceStr\"] = $this->weObj->generateNonceStr();\n    $jsParamObj[\"package\"] = $jsApiObj[\"package\"] = \"prepay_id=$this->prepay_id\";\n    $jsParamObj[\"signType\"] = $jsApiObj[\"signType\"] = \"MD5\";\n    $jsParamObj[\"paySign\"] = $jsApiObj[\"paySign\"] = $this->getSign($jsApiObj);\n\n    $jsParam = json_encode($jsParamObj);\n\n    return $jsParam;\n  }\n\n  /**\n   * 获取prepay_id\n   */\n  function getPrepayId()\n  {\n    $result = $this->xmlToArray($this->postXml());\n    $prepay_id = $result[\"prepay_id\"];\n    return $prepay_id;\n  }\n  /**\n   * \t作用：将xml转为array\n   */\n  public function xmlToArray($xml)\n  {\n    //将XML转为array\n    $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);\n    return $array_data;\n  }\n  /**\n   * \t作用：post请求xml\n   */\n  function postXml()\n  {\n    $xml = $this->createXml();\n    return  $this->postXmlCurl($xml,\"https://api.mch.weixin.qq.com/pay/unifiedorder\",$this->CURL_TIMEOUT);\n\n  }\n  /**\n   * \t作用：以post方式提交xml到对应的接口url\n   */\n  public function postXmlCurl($xml,$url,$second=30)\n  {\n    //初始化curl\n    $ch = curl_init();\n    //设置超时\n    curl_setopt($ch,CURLOP_TIMEOUT, $this->CURL_TIMEOUT);\n    //这里设置代理，如果有的话\n    //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');\n    //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);\n    curl_setopt($ch,CURLOPT_URL, $url);\n    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);\n    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);\n    //设置header\n    curl_setopt($ch, CURLOPT_HEADER, FALSE);\n    //要求结果为字符串且输出到屏幕上\n    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);\n    //post提交方式\n    curl_setopt($ch, CURLOPT_POST, TRUE);\n    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);\n    //运行curl\n    $data = curl_exec($ch);\n    curl_close($ch);\n    //返回结果\n    if($data)\n    {\n      curl_close($ch);\n      return $data;\n    }\n    else\n    {\n      $error = curl_errno($ch);\n      echo \"curl出错，错误码:$error\".\"<br>\";\n      echo \"<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>\";\n      curl_close($ch);\n      return false;\n    }\n  }\n  /**\n   * \t作用：设置标配的请求参数，生成签名，生成接口参数xml\n   */\n  function createXml()\n  {\n    $this->parameters[\"appid\"] = $this->appId;//公众账号ID\n    $this->parameters[\"mch_id\"] = $this->MCHID;//商户号\n    $this->parameters[\"nonce_str\"] = $this->weObj->generateNonceStr();//随机字符串\n    $this->parameters[\"sign\"] = $this->getSign($this->parameters);//签名\n    return  $this->arrayToXml($this->parameters);\n  }\n   /**\n   * \t作用：array转xml\n   */\n  function arrayToXml($arr)\n  {\n    $xml = \"<xml>\";\n    foreach ($arr as $key=>$val)\n    {\n      if (is_numeric($val))\n      {\n        $xml.=\"<\".$key.\">\".$val.\"</\".$key.\">\";\n\n      }\n      else\n        $xml.=\"<\".$key.\"><![CDATA[\".$val.\"]]></\".$key.\">\";\n    }\n    $xml.=\"</xml>\";\n    return $xml;\n  }\n  /**\n   * \t作用：生成签名\n   */\n  public function getSign($Obj)\n  {\n    foreach ($Obj as $k => $v)\n    {\n      $Parameters[$k] = $v;\n    }\n    //签名步骤一：按字典序排序参数\n    ksort($Parameters);\n    $String = $this->formatBizQueryParaMap($Parameters, false);\n    //echo '【string1】'.$String.'</br>';\n    //签名步骤二：在string后加入KEY\n    $String = $String.\"&key=\".$this->KEY;\n    //echo \"【string2】\".$String.\"</br>\";\n    //签名步骤三：MD5加密\n    $String = md5($String);\n    //echo \"【string3】 \".$String.\"</br>\";\n    //签名步骤四：所有字符转为大写\n    $result_ = strtoupper($String);\n    //echo \"【result】 \".$result_.\"</br>\";\n    return $result_;\n  }\n\t\n}\n\n"
  },
  {
    "path": "Thinkphp/TPWechat.class.php",
    "content": "<?php\n/**\n *\t微信公众平台PHP-SDK, ThinkPHP实例\n *  @author dodgepudding@gmail.com\n *  @link https://github.com/dodgepudding/wechat-php-sdk\n *  @version 1.2\n *  usage:\n *   $options = array(\n *\t\t\t'token'=>'tokenaccesskey', //填写你设定的key\n *\t\t\t'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey\n *\t\t\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id\n *\t\t\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥\n *\t\t);\n *\t $weObj = new TPWechat($options);\n *   $weObj->valid();\n *   ...\n *\n */\nclass TPWechat extends Wechat\n{\n\t/**\n\t * log overwrite\n\t * @see Wechat::log()\n\t */\n\tprotected function log($log){\n\t\tif ($this->debug) {\n\t\t\tif (function_exists($this->logcallback)) {\n\t\t\t\tif (is_array($log)) $log = print_r($log,true);\n\t\t\t\treturn call_user_func($this->logcallback,$log);\n\t\t\t}elseif (class_exists('Log')) {\n\t\t\t\tLog::write('wechat：'.$log, Log::DEBUG);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 重载设置缓存\n\t * @param string $cachename\n\t * @param mixed $value\n\t * @param int $expired\n\t * @return boolean\n\t */\n\tprotected function setCache($cachename,$value,$expired){\n\t\treturn S($cachename,$value,$expired);\n\t}\n\n\t/**\n\t * 重载获取缓存\n\t * @param string $cachename\n\t * @return mixed\n\t */\n\tprotected function getCache($cachename){\n\t\treturn S($cachename);\n\t}\n\n\t/**\n\t * 重载清除缓存\n\t * @param string $cachename\n\t * @return boolean\n\t */\n\tprotected function removeCache($cachename){\n\t\treturn S($cachename,null);\n\t}\n\n}\n\n\n\n"
  },
  {
    "path": "Thinkphp/Wxauth.class.php",
    "content": "<?php\n/**\n * 微信oAuth认证示例\n * 官方文档：http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html\n * UCToo示例:http://git.oschina.net/uctoo/uctoo/blob/master/Addons/Ucuser/UcuserAddon.class.php\n *\n * 微信oAuth认证类,适配Thinkphp框架，\n * @命名空间版本\n * @author uctoo (www.uctoo.com)\n * @date 2015-5-15 14:10\n */\nnamespace Com;\n\nclass Wxauth {\n\tprivate $options;\n\tpublic $open_id;\n\tpublic $wxuser;\n\t\n\tpublic function __construct($options){\n\t\t$this->options = $options;\n\t\t$this->wxoauth();\n\t}\n\t\n\tpublic function wxoauth(){\n\t\t$scope = 'snsapi_base';\n\t\t$code = isset($_GET['code'])?$_GET['code']:'';\n\t\t$token_time = isset($_SESSION['token_time'])?$_SESSION['token_time']:0;\n\t\tif(!$code && isset($_SESSION['open_id']) && isset($_SESSION['user_token']) && $token_time>time()-3600)\n\t\t{\n\t\t\tif (!$this->wxuser) {\n\t\t\t\t$this->wxuser = $_SESSION['wxuser'];\n\t\t\t}\n\t\t\t$this->open_id = $_SESSION['open_id'];\n\t\t\treturn $this->open_id;\n\t\t}\n\t\telse\n\t\t{\n\n\t\t\t$options = array(\n\t\t\t\t\t'token'=>$this->options[\"token\"], //填写你设定的key\n                    'encodingaeskey'=>$this->options[\"encodingaeskey\"], //填写加密用的EncodingAESKey\n\t\t\t\t\t'appid'=>$this->options[\"appid\"], //填写高级调用功能的app id\n\t\t\t\t\t'appsecret'=>$this->options[\"appsecret\"] //填写高级调用功能的密钥\n\t\t\t);\n\t\t\t$we_obj = new TPWechat($options);\n\t\t\tif ($code) {\n\t\t\t\t$json = $we_obj->getOauthAccessToken();\n\t\t\t\tif (!$json) {\n\t\t\t\t\tunset($_SESSION['wx_redirect']);\n\t\t\t\t\tdie('获取用户授权失败，请重新确认');\n\t\t\t\t}\n\t\t\t\t$_SESSION['open_id'] = $this->open_id = $json[\"openid\"];\n\t\t\t\t$access_token = $json['access_token'];\n\t\t\t\t$_SESSION['user_token'] = $access_token;\n\t\t\t\t$_SESSION['token_time'] = time();\n\t\t\t\t$userinfo = $we_obj->getUserInfo($this->open_id);\n\t\t\t\tif ($userinfo && !empty($userinfo['nickname'])) {\n\t\t\t\t\t$this->wxuser = array(\n\t\t\t\t\t\t\t'open_id'=>$this->open_id,\n\t\t\t\t\t\t\t'nickname'=>$userinfo['nickname'],\n\t\t\t\t\t\t\t'sex'=>intval($userinfo['sex']),\n\t\t\t\t\t\t\t'location'=>$userinfo['province'].'-'.$userinfo['city'],\n\t\t\t\t\t\t\t'avatar'=>$userinfo['headimgurl']\n\t\t\t\t\t);\n\t\t\t\t} elseif (strstr($json['scope'],'snsapi_userinfo')!==false) {\n\t\t\t\t\t$userinfo = $we_obj->getOauthUserinfo($access_token,$this->open_id);\n\t\t\t\t\tif ($userinfo && !empty($userinfo['nickname'])) {\n\t\t\t\t\t\t$this->wxuser = array(\n\t\t\t\t\t\t\t\t'open_id'=>$this->open_id,\n\t\t\t\t\t\t\t\t'nickname'=>$userinfo['nickname'],\n\t\t\t\t\t\t\t\t'sex'=>intval($userinfo['sex']),\n\t\t\t\t\t\t\t\t'location'=>$userinfo['province'].'-'.$userinfo['city'],\n\t\t\t\t\t\t\t\t'avatar'=>$userinfo['headimgurl']\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn $this->open_id;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ($this->wxuser) {\n\t\t\t\t\t$_SESSION['wxuser'] = $this->wxuser;\n\t\t\t\t\t$_SESSION['open_id'] =  $json[\"openid\"];\n\t\t\t\t\tunset($_SESSION['wx_redirect']);\n\t\t\t\t\treturn $this->open_id;\n\t\t\t\t}\n\t\t\t\t$scope = 'snsapi_userinfo';\n\t\t\t}\n\t\t\tif ($scope=='snsapi_base') {\n\t\t\t\t$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];\n\t\t\t\t$_SESSION['wx_redirect'] = $url;\n\t\t\t} else {\n\t\t\t\t$url = $_SESSION['wx_redirect'];\n\t\t\t}\n\t\t\tif (!$url) {\n\t\t\t\tunset($_SESSION['wx_redirect']);\n\t\t\t\tdie('获取用户授权失败');\n\t\t\t}\n\t\t\t$oauth_url = $we_obj->getOauthRedirect($url,\"wxbase\",$scope);\n\t\t\tredirect ( $oauth_url );\n\t\t}\n\t}\n}\n//$options = array(\n//\t\t'token'=>'uctoo', //填写你设定的key\n//\t\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询\n//\t\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥\n//);\n//$auth = new Wxauth($options);\n//var_dump($auth->wxuser);\n"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"name\" : \"dodgepudding/wechat-php-sdk\",\n  \"description\" : \"the wechat api library without framework dependency\",\n  \"version\" : \"1.1\",\n  \"require\" : {\n    \"php\" : \">=5.1.0\"\n  },\n  \"authors\" : [ {\n    \"name\" : \"dodgepudding\",\n    \"email\" : \"dodgepudding@gmail.com\"\n  }, {\n    \"name\" : \"binsee\",\n    \"email\" : \"binsee@163.com\"\n  } ],\n  \"keywords\" : [ \"wechat\", \"weixin\", \"thinkphp\", \"sample\" ],\n  \"support\" : {\n    \"issues\" : \"https://github.com/dodgepudding/wechat-php-sdk/issues\",\n    \"wiki\" : \"https://github.com/dodgepudding/wechat-php-sdk/wiki\",\n    \"source\" : \"https://github.com/dodgepudding/wechat-php-sdk\"\n  },\n  \"autoload\" : {\n    \"psr-4\" : {\n      \"dodgepudding\\\\wechat\\\\sdk\\\\\" : \"\"\n    }\n  },\n  \"type\" : \"library\",\n  \"license\" : \"LGPL\"\n}"
  },
  {
    "path": "demo.php",
    "content": "<?php\ninclude \"wechat.class.php\";\n$options = array(\n\t\t'token'=>'tokenaccesskey', //填写你设定的key\n        'encodingaeskey'=>'encodingaeskey' //填写加密用的EncodingAESKey，如接口为明文模式可忽略\n\t);\n$weObj = new Wechat($options);\n$weObj->valid();//明文或兼容模式可以在接口验证通过后注释此句，但加密模式一定不能注释，否则会验证失败\n$type = $weObj->getRev()->getRevType();\nswitch($type) {\n\tcase Wechat::MSGTYPE_TEXT:\n\t\t\t$weObj->text(\"hello, I'm wechat\")->reply();\n\t\t\texit;\n\t\t\tbreak;\n\tcase Wechat::MSGTYPE_EVENT:\n\t\t\tbreak;\n\tcase Wechat::MSGTYPE_IMAGE:\n\t\t\tbreak;\n\tdefault:\n\t\t\t$weObj->text(\"help info\")->reply();\n}"
  },
  {
    "path": "errCode.php",
    "content": "<?php\n/**\n *    微信公众平台PHP-SDK, 全局返回码类\n *  @author  binsee <binsee@163.com>\n *  @link https://github.com/binsee/wechat-php-sdk\n *  @version 1.0\n *  usage:\n *      $ret=ErrCode::getErrText(40001); //错误码可以通过公众号类库的公开变量errCode得到\n *      if ($ret)\n *          echo $ret;\n *      else\n *          echo \"未找到对应的内容\";\n */\nclass ErrCode\n{\n    public static $errCode=array(\n        '-1'=>'系统繁忙',\n        '0'=>'请求成功',\n        '40001'=>'获取access_token时AppSecret错误，或者access_token无效',\n        '40002'=>'不合法的凭证类型',\n        '40003'=>'不合法的OpenID',\n        '40004'=>'不合法的媒体文件类型',\n        '40005'=>'不合法的文件类型',\n        '40006'=>'不合法的文件大小',\n        '40007'=>'不合法的媒体文件id',\n        '40008'=>'不合法的消息类型',\n        '40009'=>'不合法的图片文件大小',\n        '40010'=>'不合法的语音文件大小',\n        '40011'=>'不合法的视频文件大小',\n        '40012'=>'不合法的缩略图文件大小',\n        '40013'=>'不合法的APPID',\n        '40014'=>'不合法的access_token',\n        '40015'=>'不合法的菜单类型',\n        '40016'=>'不合法的按钮个数',\n        '40017'=>'不合法的按钮类型',\n        '40018'=>'不合法的按钮名字长度',\n        '40019'=>'不合法的按钮KEY长度',\n        '40020'=>'不合法的按钮URL长度',\n        '40021'=>'不合法的菜单版本号',\n        '40022'=>'不合法的子菜单级数',\n        '40023'=>'不合法的子菜单按钮个数',\n        '40024'=>'不合法的子菜单按钮类型',\n        '40025'=>'不合法的子菜单按钮名字长度',\n        '40026'=>'不合法的子菜单按钮KEY长度',\n        '40027'=>'不合法的子菜单按钮URL长度',\n        '40028'=>'不合法的自定义菜单使用用户',\n        '40029'=>'不合法的oauth_code',\n        '40030'=>'不合法的refresh_token',\n        '40031'=>'不合法的openid列表',\n        '40032'=>'不合法的openid列表长度',\n        '40033'=>'不合法的请求字符，不能包含\\uxxxx格式的字符',\n        '40035'=>'不合法的参数',\n        '40038'=>'不合法的请求格式',\n        '40039'=>'不合法的URL长度',\n        '40050'=>'不合法的分组id',\n        '40051'=>'分组名字不合法',\n        '40099'=>'该 code 已被核销',\n        '41001'=>'缺少access_token参数',\n        '41002'=>'缺少appid参数',\n        '41003'=>'缺少refresh_token参数',\n        '41004'=>'缺少secret参数',\n        '41005'=>'缺少多媒体文件数据',\n        '41006'=>'缺少media_id参数',\n        '41007'=>'缺少子菜单数据',\n        '41008'=>'缺少oauth code',\n        '41009'=>'缺少openid',\n        '42001'=>'access_token超时',\n        '42002'=>'refresh_token超时',\n        '42003'=>'oauth_code超时',\n        '42005'=>'调用接口频率超过上限',\n        '43001'=>'需要GET请求',\n        '43002'=>'需要POST请求',\n        '43003'=>'需要HTTPS请求',\n        '43004'=>'需要接收者关注',\n        '43005'=>'需要好友关系',\n        '44001'=>'多媒体文件为空',\n        '44002'=>'POST的数据包为空',\n        '44003'=>'图文消息内容为空',\n        '44004'=>'文本消息内容为空',\n        '45001'=>'多媒体文件大小超过限制',\n        '45002'=>'消息内容超过限制',\n        '45003'=>'标题字段超过限制',\n        '45004'=>'描述字段超过限制',\n        '45005'=>'链接字段超过限制',\n        '45006'=>'图片链接字段超过限制',\n        '45007'=>'语音播放时间超过限制',\n        '45008'=>'图文消息超过限制',\n        '45009'=>'接口调用超过限制',\n        '45010'=>'创建菜单个数超过限制',\n        '45015'=>'回复时间超过限制',\n        '45016'=>'系统分组，不允许修改',\n        '45017'=>'分组名字过长',\n        '45018'=>'分组数量超过上限',\n        '45024'=>'账号数量超过上限',\n        '46001'=>'不存在媒体数据',\n        '46002'=>'不存在的菜单版本',\n        '46003'=>'不存在的菜单数据',\n        '46004'=>'不存在的用户',\n        '47001'=>'解析JSON/XML内容错误',\n        '48001'=>'api功能未授权',\n        '50001'=>'用户未授权该api',\n        '61450'=>'系统错误',\n        '61451'=>'参数错误',\n        '61452'=>'无效客服账号',\n        '61453'=>'账号已存在',\n        '61454'=>'客服帐号名长度超过限制(仅允许10个英文字符，不包括@及@后的公众号的微信号)',\n        '61455'=>'客服账号名包含非法字符(英文+数字)',\n        '61456'=>'客服账号个数超过限制(10个客服账号)',\n        '61457'=>'无效头像文件类型',\n        '61458'=>'客户正在被其他客服接待',\n        '61459'=>'客服不在线',\n        '61500'=>'日期格式错误',\n        '61501'=>'日期范围错误',\n        '7000000'=>'请求正常，无语义结果',\n        '7000001'=>'缺失请求参数',\n        '7000002'=>'signature 参数无效',\n        '7000003'=>'地理位置相关配置 1 无效',\n        '7000004'=>'地理位置相关配置 2 无效',\n        '7000005'=>'请求地理位置信息失败',\n        '7000006'=>'地理位置结果解析失败',\n        '7000007'=>'内部初始化失败',\n        '7000008'=>'非法 appid（获取密钥失败）',\n        '7000009'=>'请求语义服务失败',\n        '7000010'=>'非法 post 请求',\n        '7000011'=>'post 请求 json 字段无效',\n        '7000030'=>'查询 query 太短',\n        '7000031'=>'查询 query 太长',\n        '7000032'=>'城市、经纬度信息缺失',\n        '7000033'=>'query 请求语义处理失败',\n        '7000034'=>'获取天气信息失败',\n        '7000035'=>'获取股票信息失败',\n        '7000036'=>'utf8 编码转换失败',\n    );\n\n    public static function getErrText($err) {\n        if (isset(self::$errCode[$err])) {\n            return self::$errCode[$err];\n        }else {\n            return false;\n        };\n    }\n}\n\n?>"
  },
  {
    "path": "old_version/Thinkphp/Snoopy.class.php",
    "content": "<?php\n/*************************************************\n\nSnoopy - the PHP net client\nAuthor: Monte Ohrt <monte@ispi.net>\nCopyright (c): 1999-2008 New Digital Group, all rights reserved\nVersion: 1.2.4\n\n* This library is free software; you can redistribute it and/or\n* modify it under the terms of the GNU Lesser General Public\n* License as published by the Free Software Foundation; either\n* version 2.1 of the License, or (at your option) any later version.\n*\n* This library is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n* Lesser General Public License for more details.\n*\n* You should have received a copy of the GNU Lesser General Public\n* License along with this library; if not, write to the Free Software\n* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nYou may contact the author of Snoopy by e-mail at:\nmonte@ohrt.com\n\nThe latest version of Snoopy can be obtained from:\nhttp://snoopy.sourceforge.net/\n\n*************************************************/\n\nclass Snoopy\n{\n\t/**** Public variables ****/\n\n\t/* user definable vars */\n\n\tvar $host\t\t\t=\t\"www.php.net\";\t\t// host name we are connecting to\n\tvar $port\t\t\t=\t80;\t\t\t\t\t// port we are connecting to\n\tvar $proxy_host\t\t=\t\"\";\t\t\t\t\t// proxy host to use\n\tvar $proxy_port\t\t=\t\"\";\t\t\t\t\t// proxy port to use\n\tvar $proxy_user\t\t=\t\"\";\t\t\t\t\t// proxy user to use\n\tvar $proxy_pass\t\t=\t\"\";\t\t\t\t\t// proxy password to use\n\n\tvar $agent\t\t\t=\t\"Mozilla/5.0\";\t// agent we masquerade as\n\tvar\t$referer\t\t=\t\"\";\t\t\t\t\t// referer info to pass\n\tvar $cookies\t\t=\tarray();\t\t\t// array of cookies to pass\n\t// $cookies[\"username\"]=\"joe\";\n\tvar\t$rawheaders\t\t=\tarray();\t\t\t// array of raw headers to send\n\t// $rawheaders[\"Content-type\"]=\"text/html\";\n\n\tvar $maxredirs\t\t=\t5;\t\t\t\t\t// http redirection depth maximum. 0 = disallow\n\tvar $lastredirectaddr\t=\t\"\";\t\t\t\t// contains address of last redirected address\n\tvar\t$offsiteok\t\t=\ttrue;\t\t\t\t// allows redirection off-site\n\tvar $maxframes\t\t=\t0;\t\t\t\t\t// frame content depth maximum. 0 = disallow\n\tvar $expandlinks\t=\ttrue;\t\t\t\t// expand links to fully qualified URLs.\n\t// this only applies to fetchlinks()\n\t// submitlinks(), and submittext()\n\tvar $passcookies\t=\ttrue;\t\t\t\t// pass set cookies back through redirects\n\t// NOTE: this currently does not respect\n\t// dates, domains or paths.\n\n\tvar\t$user\t\t\t=\t\"\";\t\t\t\t\t// user for http authentication\n\tvar\t$pass\t\t\t=\t\"\";\t\t\t\t\t// password for http authentication\n\n\t// http accept types\n\tvar $accept\t\t\t=\t\"application/json, text/javascript, */*; q=0.01\";\n\n\tvar $results\t\t=\t\"\";\t\t\t\t\t// where the content is put\n\n\tvar $error\t\t\t=\t\"\";\t\t\t\t\t// error messages sent here\n\tvar\t$response_code\t=\t\"\";\t\t\t\t\t// response code returned from server\n\tvar\t$headers\t\t=\tarray();\t\t\t// headers returned from server sent here\n\tvar\t$maxlength\t\t=\t500000;\t\t\t\t// max return data length (body)\n\tvar $read_timeout\t=\t0;\t\t\t\t\t// timeout on read operations, in seconds\n\t// supported only since PHP 4 Beta 4\n\t// set to 0 to disallow timeouts\n\tvar $timed_out\t\t=\tfalse;\t\t\t\t// if a read operation timed out\n\tvar\t$status\t\t\t=\t0;\t\t\t\t\t// http request status\n\n\tvar $temp_dir\t\t=\t\"/tmp\";\t\t\t\t// temporary directory that the webserver\n\t// has permission to write to.\n\t// under Windows, this should be C:\\temp\n\n\tvar\t$curl_path\t\t=\t\"/usr/local/bin/curl\";\n\t// Snoopy will use cURL for fetching\n\t// SSL content if a full system path to\n\t// the cURL binary is supplied here.\n\t// set to false if you do not have\n\t// cURL installed. See http://curl.haxx.se\n\t// for details on installing cURL.\n\t// Snoopy does *not* use the cURL\n\t// library functions built into php,\n\t// as these functions are not stable\n\t// as of this Snoopy release.\n\n\t/**** Private variables ****/\n\n\tvar\t$_maxlinelen\t=\t4096;\t\t\t\t// max line length (headers)\n\n\tvar $_httpmethod\t=\t\"GET\";\t\t\t\t// default http request method\n\tvar $_httpversion\t=\t\"HTTP/1.0\";\t\t\t// default http request version\n\tvar $_submit_method\t=\t\"POST\";\t\t\t\t// default submit method\n\tvar $_submit_type\t=\t\"application/x-www-form-urlencoded\";\t// default submit type\n\tvar $_mime_boundary\t=   \"\";\t\t\t\t\t// MIME boundary for multipart/form-data submit type\n\tvar $_redirectaddr\t=\tfalse;\t\t\t\t// will be set if page fetched is a redirect\n\tvar $_redirectdepth\t=\t0;\t\t\t\t\t// increments on an http redirect\n\tvar $_frameurls\t\t= \tarray();\t\t\t// frame src urls\n\tvar $_framedepth\t=\t0;\t\t\t\t\t// increments on frame depth\n\n\tvar $_isproxy\t\t=\tfalse;\t\t\t\t// set if using a proxy server\n\tvar $_fp_timeout\t=\t30;\t\t\t\t\t// timeout for socket connection\n\n\t/*======================================================================*\\\n\t Function:\tfetch\n\tPurpose:\tfetch the contents of a web page\n\t(and possibly other protocols in the\n\t\t\tfuture like ftp, nntp, gopher, etc.)\n\tInput:\t\t$URI\tthe location of the page to fetch\n\tOutput:\t\t$this->results\tthe output text from the fetch\n\t\\*======================================================================*/\n\n\tfunction fetch($URI)\n\t{\n\n\t\t//preg_match(\"|^([^:]+)://([^:/]+)(:[\\d]+)*(.*)|\",$URI,$URI_PARTS);\n\t\t$URI_PARTS = parse_url($URI);\n\t\tif (!empty($URI_PARTS[\"user\"]))\n\t\t\t$this->user = $URI_PARTS[\"user\"];\n\t\tif (!empty($URI_PARTS[\"pass\"]))\n\t\t\t$this->pass = $URI_PARTS[\"pass\"];\n\t\tif (empty($URI_PARTS[\"query\"]))\n\t\t\t$URI_PARTS[\"query\"] = '';\n\t\tif (empty($URI_PARTS[\"path\"]))\n\t\t\t$URI_PARTS[\"path\"] = '';\n\n\t\tswitch(strtolower($URI_PARTS[\"scheme\"]))\n\t\t{\n\t\t\tcase \"http\":\n\t\t\t\t$this->host = $URI_PARTS[\"host\"];\n\t\t\t\tif(!empty($URI_PARTS[\"port\"]))\n\t\t\t\t\t$this->port = $URI_PARTS[\"port\"];\n\t\t\t\tif($this->_connect($fp))\n\t\t\t\t{\n\t\t\t\t\tif($this->_isproxy)\n\t\t\t\t\t{\n\t\t\t\t\t\t// using proxy, send entire URI\n\t\t\t\t\t\t$this->_httprequest($URI,$fp,$URI,$this->_httpmethod);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$path = $URI_PARTS[\"path\"].($URI_PARTS[\"query\"] ? \"?\".$URI_PARTS[\"query\"] : \"\");\n\t\t\t\t\t\t// no proxy, send only the path\n\t\t\t\t\t\t$this->_httprequest($path, $fp, $URI, $this->_httpmethod);\n\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t$this->_disconnect($fp);\n\n\t\t\t\t\tif($this->_redirectaddr)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* url was redirected, check if we've hit the max depth */\n\t\t\t\t\t\tif($this->maxredirs > $this->_redirectdepth)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// only follow redirect if it's on this site, or offsiteok is true\n\t\t\t\t\t\t\tif(preg_match(\"|^http://\".preg_quote($this->host).\"|i\",$this->_redirectaddr) || $this->offsiteok)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t/* follow the redirect */\n\t\t\t\t\t\t\t\t$this->_redirectdepth++;\n\t\t\t\t\t\t\t\t$this->lastredirectaddr=$this->_redirectaddr;\n\t\t\t\t\t\t\t\t$this->fetch($this->_redirectaddr);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t$frameurls = $this->_frameurls;\n\t\t\t\t\t\t$this->_frameurls = array();\n\n\t\t\t\t\t\twhile(list(,$frameurl) = each($frameurls))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif($this->_framedepth < $this->maxframes)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t$this->fetch($frameurl);\n\t\t\t\t\t\t\t\t$this->_framedepth++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t\tbreak;\n\t\t\tcase \"https\":\n\t\t\t\tif (!function_exists('curl_init')) {\n\t\t\t\t\tif(!$this->curl_path)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tif(function_exists(\"is_executable\"))\n\t\t\t\t\t\tif (!is_executable($this->curl_path))\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t$this->host = $URI_PARTS[\"host\"];\n\t\t\t\tif(!empty($URI_PARTS[\"port\"]))\n\t\t\t\t\t$this->port = $URI_PARTS[\"port\"];\n\t\t\t\tif($this->_isproxy)\n\t\t\t\t{\n\t\t\t\t\t// using proxy, send entire URI\n\t\t\t\t\t$this->_httpsrequest($URI,$URI,$this->_httpmethod);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$path = $URI_PARTS[\"path\"].($URI_PARTS[\"query\"] ? \"?\".$URI_PARTS[\"query\"] : \"\");\n\t\t\t\t\t// no proxy, send only the path\n\t\t\t\t\t$this->_httpsrequest($path, $URI, $this->_httpmethod);\n\t\t\t\t}\n\n\t\t\t\tif($this->_redirectaddr)\n\t\t\t\t{\n\t\t\t\t\t/* url was redirected, check if we've hit the max depth */\n\t\t\t\t\tif($this->maxredirs > $this->_redirectdepth)\n\t\t\t\t\t{\n\t\t\t\t\t\t// only follow redirect if it's on this site, or offsiteok is true\n\t\t\t\t\t\tif(preg_match(\"|^https://\".preg_quote($this->host).\"|i\",$this->_redirectaddr) || $this->offsiteok)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/* follow the redirect */\n\t\t\t\t\t\t\t$this->_redirectdepth++;\n\t\t\t\t\t\t\t$this->lastredirectaddr=$this->_redirectaddr;\n\t\t\t\t\t\t\t$this->fetch($this->_redirectaddr);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)\n\t\t\t\t{\n\t\t\t\t\t$frameurls = $this->_frameurls;\n\t\t\t\t\t$this->_frameurls = array();\n\n\t\t\t\t\twhile(list(,$frameurl) = each($frameurls))\n\t\t\t\t\t{\n\t\t\t\t\t\tif($this->_framedepth < $this->maxframes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$this->fetch($frameurl);\n\t\t\t\t\t\t\t$this->_framedepth++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// not a valid protocol\n\t\t\t\t$this->error\t=\t'Invalid protocol \"'.$URI_PARTS[\"scheme\"].'\"\\n';\n\t\t\t\treturn false;\n\t\t\t\tbreak;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tsubmit\n\tPurpose:\tsubmit an http form\n\tInput:\t\t$URI\tthe location to post the data\n\t$formvars\tthe formvars to use.\n\tformat: $formvars[\"var\"] = \"val\";\n\t$formfiles  an array of files to submit\n\tformat: $formfiles[\"var\"] = \"/dir/filename.ext\";\n\tOutput:\t\t$this->results\tthe text output from the post\n\t\\*======================================================================*/\n\n\tfunction submit($URI, $formvars=\"\", $formfiles=\"\")\n\t{\n\t\tunset($postdata);\n\n\t\t$postdata = $this->_prepare_post_body($formvars, $formfiles);\n\t\t$URI_PARTS = parse_url($URI);\n\t\tif (!empty($URI_PARTS[\"user\"]))\n\t\t\t$this->user = $URI_PARTS[\"user\"];\n\t\tif (!empty($URI_PARTS[\"pass\"]))\n\t\t\t$this->pass = $URI_PARTS[\"pass\"];\n\t\tif (empty($URI_PARTS[\"query\"]))\n\t\t\t$URI_PARTS[\"query\"] = '';\n\t\tif (empty($URI_PARTS[\"path\"]))\n\t\t\t$URI_PARTS[\"path\"] = '';\n\n\t\tswitch(strtolower($URI_PARTS[\"scheme\"]))\n\t\t{\n\t\t\tcase \"http\":\n\t\t\t\t$this->host = $URI_PARTS[\"host\"];\n\t\t\t\tif(!empty($URI_PARTS[\"port\"]))\n\t\t\t\t\t$this->port = $URI_PARTS[\"port\"];\n\t\t\t\tif($this->_connect($fp))\n\t\t\t\t{\n\t\t\t\t\tif($this->_isproxy)\n\t\t\t\t\t{\n\t\t\t\t\t\t// using proxy, send entire URI\n\t\t\t\t\t\t$this->_httprequest($URI,$fp,$URI,$this->_submit_method,$this->_submit_type,$postdata);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$path = $URI_PARTS[\"path\"].($URI_PARTS[\"query\"] ? \"?\".$URI_PARTS[\"query\"] : \"\");\n\t\t\t\t\t\t// no proxy, send only the path\n\t\t\t\t\t\t$this->_httprequest($path, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata);\n\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t$this->_disconnect($fp);\n\n\t\t\t\t\tif($this->_redirectaddr)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* url was redirected, check if we've hit the max depth */\n\t\t\t\t\t\tif($this->maxredirs > $this->_redirectdepth)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(!preg_match(\"|^\".$URI_PARTS[\"scheme\"].\"://|\", $this->_redirectaddr))\n\t\t\t\t\t\t\t\t$this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS[\"scheme\"].\"://\".$URI_PARTS[\"host\"]);\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// only follow redirect if it's on this site, or offsiteok is true\n\t\t\t\t\t\t\tif(preg_match(\"|^http://\".preg_quote($this->host).\"|i\",$this->_redirectaddr) || $this->offsiteok)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t/* follow the redirect */\n\t\t\t\t\t\t\t\t$this->_redirectdepth++;\n\t\t\t\t\t\t\t\t$this->lastredirectaddr=$this->_redirectaddr;\n\t\t\t\t\t\t\t\tif( strpos( $this->_redirectaddr, \"?\" ) > 0 )\n\t\t\t\t\t\t\t\t\t$this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t$this->submit($this->_redirectaddr,$formvars, $formfiles);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t$frameurls = $this->_frameurls;\n\t\t\t\t\t\t$this->_frameurls = array();\n\n\t\t\t\t\t\twhile(list(,$frameurl) = each($frameurls))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif($this->_framedepth < $this->maxframes)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t$this->fetch($frameurl);\n\t\t\t\t\t\t\t\t$this->_framedepth++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t\tbreak;\n\t\t\tcase \"https\":\n\t\t\t\tif (!function_exists('curl_init')) {\n\t\t\t\tif(!$this->curl_path)\n\t\t\t\t\treturn false;\n\t\t\t\tif(function_exists(\"is_executable\"))\n\t\t\t\t\tif (!is_executable($this->curl_path))\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t$this->host = $URI_PARTS[\"host\"];\n\t\t\t\tif(!empty($URI_PARTS[\"port\"]))\n\t\t\t\t\t$this->port = $URI_PARTS[\"port\"];\n\t\t\t\tif($this->_isproxy)\n\t\t\t\t{\n\t\t\t\t\t// using proxy, send entire URI\n\t\t\t\t\t$this->_httpsrequest($URI, $URI, $this->_submit_method, $this->_submit_type, $postdata);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$path = $URI_PARTS[\"path\"].($URI_PARTS[\"query\"] ? \"?\".$URI_PARTS[\"query\"] : \"\");\n\t\t\t\t\t// no proxy, send only the path\n\t\t\t\t\t$this->_httpsrequest($path, $URI, $this->_submit_method, $this->_submit_type, $postdata);\n\t\t\t\t}\n\n\t\t\t\tif($this->_redirectaddr)\n\t\t\t\t{\n\t\t\t\t\t/* url was redirected, check if we've hit the max depth */\n\t\t\t\t\tif($this->maxredirs > $this->_redirectdepth)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(!preg_match(\"|^\".$URI_PARTS[\"scheme\"].\"://|\", $this->_redirectaddr))\n\t\t\t\t\t\t\t$this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS[\"scheme\"].\"://\".$URI_PARTS[\"host\"]);\n\n\t\t\t\t\t\t// only follow redirect if it's on this site, or offsiteok is true\n\t\t\t\t\t\tif(preg_match(\"|^https://\".preg_quote($this->host).\"|i\",$this->_redirectaddr) || $this->offsiteok)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/* follow the redirect */\n\t\t\t\t\t\t\t$this->_redirectdepth++;\n\t\t\t\t\t\t\t$this->lastredirectaddr=$this->_redirectaddr;\n\t\t\t\t\t\t\tif( strpos( $this->_redirectaddr, \"?\" ) > 0 )\n\t\t\t\t\t\t\t\t$this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t$this->submit($this->_redirectaddr,$formvars, $formfiles);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)\n\t\t\t\t{\n\t\t\t\t\t$frameurls = $this->_frameurls;\n\t\t\t\t\t$this->_frameurls = array();\n\n\t\t\t\t\twhile(list(,$frameurl) = each($frameurls))\n\t\t\t\t\t{\n\t\t\t\t\t\tif($this->_framedepth < $this->maxframes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$this->fetch($frameurl);\n\t\t\t\t\t\t\t$this->_framedepth++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\t// not a valid protocol\n\t\t\t\t$this->error\t=\t'Invalid protocol \"'.$URI_PARTS[\"scheme\"].'\"\\n';\n\t\t\t\treturn false;\n\t\t\t\tbreak;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tfetchlinks\n\tPurpose:\tfetch the links from a web page\n\tInput:\t\t$URI\twhere you are fetching from\n\tOutput:\t\t$this->results\tan array of the URLs\n\t\\*======================================================================*/\n\n\tfunction fetchlinks($URI)\n\t{\n\t\tif ($this->fetch($URI))\n\t\t{\n\t\t\tif($this->lastredirectaddr)\n\t\t\t\t$URI = $this->lastredirectaddr;\n\t\t\tif(is_array($this->results))\n\t\t\t{\n\t\t\t\tfor($x=0;$x<count($this->results);$x++)\n\t\t\t\t\t$this->results[$x] = $this->_striplinks($this->results[$x]);\n\t\t\t}\n\t\t\telse\n\t\t\t\t$this->results = $this->_striplinks($this->results);\n\n\t\t\tif($this->expandlinks)\n\t\t\t\t$this->results = $this->_expandlinks($this->results, $URI);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tfetchform\n\tPurpose:\tfetch the form elements from a web page\n\tInput:\t\t$URI\twhere you are fetching from\n\tOutput:\t\t$this->results\tthe resulting html form\n\t\\*======================================================================*/\n\n\tfunction fetchform($URI)\n\t{\n\n\t\tif ($this->fetch($URI))\n\t\t{\n\n\t\t\tif(is_array($this->results))\n\t\t\t{\n\t\t\t\tfor($x=0;$x<count($this->results);$x++)\n\t\t\t\t\t$this->results[$x] = $this->_stripform($this->results[$x]);\n\t\t\t}\n\t\t\telse\n\t\t\t\t$this->results = $this->_stripform($this->results);\n\t\t\t\t\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\n\t/*======================================================================*\\\n\t Function:\tfetchtext\n\tPurpose:\tfetch the text from a web page, stripping the links\n\tInput:\t\t$URI\twhere you are fetching from\n\tOutput:\t\t$this->results\tthe text from the web page\n\t\\*======================================================================*/\n\n\tfunction fetchtext($URI)\n\t{\n\t\tif($this->fetch($URI))\n\t\t{\n\t\t\tif(is_array($this->results))\n\t\t\t{\n\t\t\t\tfor($x=0;$x<count($this->results);$x++)\n\t\t\t\t\t$this->results[$x] = $this->_striptext($this->results[$x]);\n\t\t\t}\n\t\t\telse\n\t\t\t\t$this->results = $this->_striptext($this->results);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tsubmitlinks\n\tPurpose:\tgrab links from a form submission\n\tInput:\t\t$URI\twhere you are submitting from\n\tOutput:\t\t$this->results\tan array of the links from the post\n\t\\*======================================================================*/\n\n\tfunction submitlinks($URI, $formvars=\"\", $formfiles=\"\")\n\t{\n\t\tif($this->submit($URI,$formvars, $formfiles))\n\t\t{\n\t\t\tif($this->lastredirectaddr)\n\t\t\t\t$URI = $this->lastredirectaddr;\n\t\t\tif(is_array($this->results))\n\t\t\t{\n\t\t\t\tfor($x=0;$x<count($this->results);$x++)\n\t\t\t\t{\n\t\t\t\t\t$this->results[$x] = $this->_striplinks($this->results[$x]);\n\t\t\t\t\tif($this->expandlinks)\n\t\t\t\t\t\t$this->results[$x] = $this->_expandlinks($this->results[$x],$URI);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->results = $this->_striplinks($this->results);\n\t\t\t\tif($this->expandlinks)\n\t\t\t\t\t$this->results = $this->_expandlinks($this->results,$URI);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tsubmittext\n\tPurpose:\tgrab text from a form submission\n\tInput:\t\t$URI\twhere you are submitting from\n\tOutput:\t\t$this->results\tthe text from the web page\n\t\\*======================================================================*/\n\n\tfunction submittext($URI, $formvars = \"\", $formfiles = \"\")\n\t{\n\t\tif($this->submit($URI,$formvars, $formfiles))\n\t\t{\n\t\t\tif($this->lastredirectaddr)\n\t\t\t\t$URI = $this->lastredirectaddr;\n\t\t\tif(is_array($this->results))\n\t\t\t{\n\t\t\t\tfor($x=0;$x<count($this->results);$x++)\n\t\t\t\t{\n\t\t\t\t\t$this->results[$x] = $this->_striptext($this->results[$x]);\n\t\t\t\t\tif($this->expandlinks)\n\t\t\t\t\t\t$this->results[$x] = $this->_expandlinks($this->results[$x],$URI);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->results = $this->_striptext($this->results);\n\t\t\t\tif($this->expandlinks)\n\t\t\t\t\t$this->results = $this->_expandlinks($this->results,$URI);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\n\n\t/*======================================================================*\\\n\t Function:\tset_submit_multipart\n\tPurpose:\tSet the form submission content type to\n\tmultipart/form-data\n\t\\*======================================================================*/\n\tfunction set_submit_multipart()\n\t{\n\t\t$this->_submit_type = \"multipart/form-data\";\n\t}\n\n\n\t/*======================================================================*\\\n\t Function:\tset_submit_normal\n\tPurpose:\tSet the form submission content type to\n\tapplication/x-www-form-urlencoded\n\t\\*======================================================================*/\n\tfunction set_submit_normal()\n\t{\n\t\t$this->_submit_type = \"application/x-www-form-urlencoded\";\n\t}\n\n\n\n\n\t/*======================================================================*\\\n\t Private functions\n\t\\*======================================================================*/\n\n\n\t/*======================================================================*\\\n\t Function:\t_striplinks\n\tPurpose:\tstrip the hyperlinks from an html document\n\tInput:\t\t$document\tdocument to strip.\n\tOutput:\t\t$match\t\tan array of the links\n\t\\*======================================================================*/\n\n\tfunction _striplinks($document)\n\t{\n\t\tpreg_match_all(\"'<\\s*a\\s.*?href\\s*=\\s*\t\t\t# find <a href=\n\t\t\t\t\t\t([\\\"\\'])?\t\t\t\t\t# find single or double quote\n\t\t\t\t\t\t(?(1) (.*?)\\\\1 | ([^\\s\\>]+))\t\t# if quote found, match up to next matching\n\t\t\t\t\t\t\t\t\t\t\t\t\t# quote, otherwise match up to next space\n\t\t\t\t\t\t'isx\",$document,$links);\n\n\n\t\t// catenate the non-empty matches from the conditional subpattern\n\n\t\twhile(list($key,$val) = each($links[2]))\n\t\t{\n\t\t\tif(!empty($val))\n\t\t\t\t$match[] = $val;\n\t\t}\n\n\t\twhile(list($key,$val) = each($links[3]))\n\t\t{\n\t\t\tif(!empty($val))\n\t\t\t\t$match[] = $val;\n\t\t}\n\n\t\t// return the links\n\t\treturn $match;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\t_stripform\n\tPurpose:\tstrip the form elements from an html document\n\tInput:\t\t$document\tdocument to strip.\n\tOutput:\t\t$match\t\tan array of the links\n\t\\*======================================================================*/\n\n\tfunction _stripform($document)\n\t{\n\t\tpreg_match_all(\"'<\\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\\/?(option|select)[^<>]*>[\\r\\n]*)|(?=[\\r\\n]*))|(?=[\\r\\n]*))'Usi\",$document,$elements);\n\n\t\t// catenate the matches\n\t\t$match = implode(\"\\r\\n\",$elements[0]);\n\n\t\t// return the links\n\t\treturn $match;\n\t}\n\n\n\n\t/*======================================================================*\\\n\t Function:\t_striptext\n\tPurpose:\tstrip the text from an html document\n\tInput:\t\t$document\tdocument to strip.\n\tOutput:\t\t$text\t\tthe resulting text\n\t\\*======================================================================*/\n\n\tfunction _striptext($document)\n\t{\n\n\t\t// I didn't use preg eval (//e) since that is only available in PHP 4.0.\n\t\t// so, list your entities one by one here. I included some of the\n\t\t// more common ones.\n\n\t\t$search = array(\"'<script[^>]*?>.*?</script>'si\",\t// strip out javascript\n\t\t\t\t\"'<[\\/\\!]*?[^<>]*?>'si\",\t\t\t// strip out html tags\n\t\t\t\t\"'([\\r\\n])[\\s]+'\",\t\t\t\t\t// strip out white space\n\t\t\t\t\"'&(quot|#34|#034|#x22);'i\",\t\t// replace html entities\n\t\t\t\t\"'&(amp|#38|#038|#x26);'i\",\t\t\t// added hexadecimal values\n\t\t\t\t\"'&(lt|#60|#060|#x3c);'i\",\n\t\t\t\t\"'&(gt|#62|#062|#x3e);'i\",\n\t\t\t\t\"'&(nbsp|#160|#xa0);'i\",\n\t\t\t\t\"'&(iexcl|#161);'i\",\n\t\t\t\t\"'&(cent|#162);'i\",\n\t\t\t\t\"'&(pound|#163);'i\",\n\t\t\t\t\"'&(copy|#169);'i\",\n\t\t\t\t\"'&(reg|#174);'i\",\n\t\t\t\t\"'&(deg|#176);'i\",\n\t\t\t\t\"'&(#39|#039|#x27);'\",\n\t\t\t\t\"'&(euro|#8364);'i\",\t\t\t\t// europe\n\t\t\t\t\"'&a(uml|UML);'\",\t\t\t\t\t// german\n\t\t\t\t\"'&o(uml|UML);'\",\n\t\t\t\t\"'&u(uml|UML);'\",\n\t\t\t\t\"'&A(uml|UML);'\",\n\t\t\t\t\"'&O(uml|UML);'\",\n\t\t\t\t\"'&U(uml|UML);'\",\n\t\t\t\t\"'&szlig;'i\",\n\t\t);\n\t\t$replace = array(\t\"\",\n\t\t\t\t\"\",\n\t\t\t\t\"\\\\1\",\n\t\t\t\t\"\\\"\",\n\t\t\t\t\"&\",\n\t\t\t\t\"<\",\n\t\t\t\t\">\",\n\t\t\t\t\" \",\n\t\t\t\tchr(161),\n\t\t\t\tchr(162),\n\t\t\t\tchr(163),\n\t\t\t\tchr(169),\n\t\t\t\tchr(174),\n\t\t\t\tchr(176),\n\t\t\t\tchr(39),\n\t\t\t\tchr(128),\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t);\n\t\t\t\n\t\t$text = preg_replace($search,$replace,$document);\n\n\t\treturn $text;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\t_expandlinks\n\tPurpose:\texpand each link into a fully qualified URL\n\tInput:\t\t$links\t\t\tthe links to qualify\n\t$URI\t\t\tthe full URI to get the base from\n\tOutput:\t\t$expandedLinks\tthe expanded links\n\t\\*======================================================================*/\n\n\tfunction _expandlinks($links,$URI)\n\t{\n\n\t\tpreg_match(\"/^[^\\?]+/\",$URI,$match);\n\n\t\t$match = preg_replace(\"|/[^\\/\\.]+\\.[^\\/\\.]+$|\",\"\",$match[0]);\n\t\t$match = preg_replace(\"|/$|\",\"\",$match);\n\t\t$match_part = parse_url($match);\n\t\t$match_root =\n\t\t$match_part[\"scheme\"].\"://\".$match_part[\"host\"];\n\n\t\t$search = array( \t\"|^http://\".preg_quote($this->host).\"|i\",\n\t\t\t\t\"|^(\\/)|i\",\n\t\t\t\t\"|^(?!http://)(?!mailto:)|i\",\n\t\t\t\t\"|/\\./|\",\n\t\t\t\t\"|/[^\\/]+/\\.\\./|\"\n\t\t);\n\n\t\t$replace = array(\t\"\",\n\t\t\t\t$match_root.\"/\",\n\t\t\t\t$match.\"/\",\n\t\t\t\t\"/\",\n\t\t\t\t\"/\"\n\t\t);\n\n\t\t$expandedLinks = preg_replace($search,$replace,$links);\n\n\t\treturn $expandedLinks;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\t_httprequest\n\tPurpose:\tgo get the http data from the server\n\tInput:\t\t$url\t\tthe url to fetch\n\t$fp\t\t\tthe current open file pointer\n\t$URI\t\tthe full URI\n\t$body\t\tbody contents to send if any (POST)\n\tOutput:\n\t\\*======================================================================*/\n\n\tfunction _httprequest($url,$fp,$URI,$http_method,$content_type=\"\",$body=\"\")\n\t{\n\t\t$cookie_headers = '';\n\t\tif($this->passcookies && $this->_redirectaddr)\n\t\t\t$this->setcookies();\n\t\t\t\n\t\t$URI_PARTS = parse_url($URI);\n\t\tif(empty($url))\n\t\t\t$url = \"/\";\n\t\t$headers = $http_method.\" \".$url.\" \".$this->_httpversion.\"\\r\\n\";\n\t\tif(!empty($this->agent))\n\t\t\t$headers .= \"User-Agent: \".$this->agent.\"\\r\\n\";\n\t\tif(!empty($this->host) && !isset($this->rawheaders['Host'])) {\n\t\t\t$headers .= \"Host: \".$this->host;\n\t\t\tif(!empty($this->port) && $this->port!=80)\n\t\t\t\t$headers .= \":\".$this->port;\n\t\t\t$headers .= \"\\r\\n\";\n\t\t}\n\t\tif(!empty($this->accept))\n\t\t\t$headers .= \"Accept: \".$this->accept.\"\\r\\n\";\n\t\tif(!empty($this->referer))\n\t\t\t$headers .= \"Referer: \".$this->referer.\"\\r\\n\";\n\t\tif(!empty($this->cookies))\n\t\t{\n\t\t\tif(!is_array($this->cookies))\n\t\t\t\t$this->cookies = (array)$this->cookies;\n\n\t\t\treset($this->cookies);\n\t\t\tif ( count($this->cookies) > 0 ) {\n\t\t\t\t$cookie_headers .= 'Cookie: ';\n\t\t\t\tforeach ( $this->cookies as $cookieKey => $cookieVal ) {\n\t\t\t\t\t$cookie_headers .= $cookieKey.\"=\".urlencode($cookieVal).\"; \";\n\t\t\t\t}\n\t\t\t\t$headers .= substr($cookie_headers,0,-2) . \"\\r\\n\";\n\t\t\t}\n\t\t}\n\t\tif(!empty($this->rawheaders))\n\t\t{\n\t\t\tif(!is_array($this->rawheaders))\n\t\t\t\t$this->rawheaders = (array)$this->rawheaders;\n\t\t\twhile(list($headerKey,$headerVal) = each($this->rawheaders))\n\t\t\t\t$headers .= $headerKey.\": \".$headerVal.\"\\r\\n\";\n\t\t}\n\t\tif(!empty($content_type)) {\n\t\t\t$headers .= \"Content-type: $content_type\";\n\t\t\tif ($content_type == \"multipart/form-data\")\n\t\t\t\t$headers .= \"; boundary=\".$this->_mime_boundary;\n\t\t\t$headers .= \"\\r\\n\";\n\t\t}\n\t\tif(!empty($body))\n\t\t\t$headers .= \"Content-length: \".strlen($body).\"\\r\\n\";\n\t\tif(!empty($this->user) || !empty($this->pass))\n\t\t\t$headers .= \"Authorization: Basic \".base64_encode($this->user.\":\".$this->pass).\"\\r\\n\";\n\n\t\t//add proxy auth headers\n\t\tif(!empty($this->proxy_user))\n\t\t\t$headers .= 'Proxy-Authorization: ' . 'Basic ' . base64_encode($this->proxy_user . ':' . $this->proxy_pass).\"\\r\\n\";\n\n\n\t\t$headers .= \"\\r\\n\";\n\n\t\t// set the read timeout if needed\n\t\tif ($this->read_timeout > 0)\n\t\t\tsocket_set_timeout($fp, $this->read_timeout);\n\t\t$this->timed_out = false;\n\n\t\tfwrite($fp,$headers.$body,strlen($headers.$body));\n\n\t\t$this->_redirectaddr = false;\n\t\tunset($this->headers);\n\n\t\twhile($currentHeader = fgets($fp,$this->_maxlinelen))\n\t\t{\n\t\t\tif ($this->read_timeout > 0 && $this->_check_timeout($fp))\n\t\t\t{\n\t\t\t\t$this->status=-100;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif($currentHeader == \"\\r\\n\")\n\t\t\t\tbreak;\n\n\t\t\t// if a header begins with Location: or URI:, set the redirect\n\t\t\tif(preg_match(\"/^(Location:|URI:)/i\",$currentHeader))\n\t\t\t{\n\t\t\t\t// get URL portion of the redirect\n\t\t\t\tpreg_match(\"/^(Location:|URI:)[ ]+(.*)/i\",chop($currentHeader),$matches);\n\t\t\t\t// look for :// in the Location header to see if hostname is included\n\t\t\t\tif (!empty($matches)) {\n\t\t\t\t\tif(!preg_match(\"|\\:\\/\\/|\",$matches[2]))\n\t\t\t\t\t{\n\t\t\t\t\t\t// no host in the path, so prepend\n\t\t\t\t\t\t$this->_redirectaddr = $URI_PARTS[\"scheme\"].\"://\".$this->host.\":\".$this->port;\n\t\t\t\t\t\t// eliminate double slash\n\t\t\t\t\t\tif(!preg_match(\"|^/|\",$matches[2]))\n\t\t\t\t\t\t\t$this->_redirectaddr .= \"/\".$matches[2];\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t$this->_redirectaddr .= $matches[2];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\t$this->_redirectaddr = $matches[2];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(preg_match(\"|^HTTP/|\",$currentHeader))\n\t\t\t{\n\t\t\t\tif(preg_match(\"|^HTTP/[^\\s]*\\s(.*?)\\s|\",$currentHeader, $status))\n\t\t\t\t{\n\t\t\t\t\t$this->status= $status[1];\n\t\t\t\t}\n\t\t\t\t$this->response_code = $currentHeader;\n\t\t\t}\n\n\t\t\t$this->headers[] = $currentHeader;\n\t\t}\n\n\t\t$results = '';\n\t\tdo {\n\t\t\t$_data = fread($fp, $this->maxlength);\n\t\t\tif (strlen($_data) == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t$results .= $_data;\n\t\t} while(true);\n\n\t\tif ($this->read_timeout > 0 && $this->_check_timeout($fp))\n\t\t{\n\t\t\t$this->status=-100;\n\t\t\treturn false;\n\t\t}\n\n\t\t// check if there is a a redirect meta tag\n\n\t\tif(preg_match(\"'<meta[\\s]*http-equiv[^>]*?content[\\s]*=[\\s]*[\\\"\\']?\\d+;[\\s]*URL[\\s]*=[\\s]*([^\\\"\\']*?)[\\\"\\']?>'i\",$results,$match))\n\n\t\t{\n\t\t\t$this->_redirectaddr = $this->_expandlinks($match[1],$URI);\n\t\t}\n\n\t\t// have we hit our frame depth and is there frame src to fetch?\n\t\tif(($this->_framedepth < $this->maxframes) && preg_match_all(\"'<frame\\s+.*src[\\s]*=[\\'\\\"]?([^\\'\\\"\\>]+)'i\",$results,$match))\n\t\t{\n\t\t\t$this->results[] = $results;\n\t\t\tfor($x=0; $x<count($match[1]); $x++)\n\t\t\t\t$this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS[\"scheme\"].\"://\".$this->host);\n\t\t}\n\t\t// have we already fetched framed content?\n\t\telseif(is_array($this->results))\n\t\t$this->results[] = $results;\n\t\t// no framed content\n\t\telse\n\t\t\t$this->results = $results;\n\n\t\treturn true;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\t_httpsrequest\n\tPurpose:\tgo get the https data from the server using curl\n\tInput:\t\t$url\t\tthe url to fetch\n\t$URI\t\tthe full URI\n\t$body\t\tbody contents to send if any (POST)\n\tOutput:\n\t\\*======================================================================*/\n\n\tfunction _httpsrequest($url,$URI,$http_method,$content_type=\"\",$body=\"\")\n\t{\n\t\tif($this->passcookies && $this->_redirectaddr)\n\t\t\t$this->setcookies();\n\n\t\t$headers = array();\n\t\t\t\n\t\t$URI_PARTS = parse_url($URI);\n\t\tif(empty($url))\n\t\t\t$url = \"/\";\n\t\t// GET ... header not needed for curl\n\t\t//$headers[] = $http_method.\" \".$url.\" \".$this->_httpversion;\n\t\tif(!empty($this->agent))\n\t\t\t$headers[] = \"User-Agent: \".$this->agent;\n\t\tif(!empty($this->host))\n\t\t\tif(!empty($this->port) && $this->port!=80)\n\t\t\t$headers[] = \"Host: \".$this->host.\":\".$this->port;\n\t\telse\n\t\t\t$headers[] = \"Host: \".$this->host;\n\t\tif(!empty($this->accept))\n\t\t\t$headers[] = \"Accept: \".$this->accept;\n\t\tif(!empty($this->referer))\n\t\t\t$headers[] = \"Referer: \".$this->referer;\n\t\tif(!empty($this->cookies))\n\t\t{\n\t\t\tif(!is_array($this->cookies))\n\t\t\t\t$this->cookies = (array)$this->cookies;\n\n\t\t\treset($this->cookies);\n\t\t\tif ( count($this->cookies) > 0 ) {\n\t\t\t\t$cookie_str = 'Cookie: ';\n\t\t\t\tforeach ( $this->cookies as $cookieKey => $cookieVal ) {\n\t\t\t\t\t$cookie_str .= $cookieKey.\"=\".urlencode($cookieVal).\"; \";\n\t\t\t\t}\n\t\t\t\t$headers[] = substr($cookie_str,0,-2);\n\t\t\t}\n\t\t}\n\t\tif(!empty($this->rawheaders))\n\t\t{\n\t\t\tif(!is_array($this->rawheaders))\n\t\t\t\t$this->rawheaders = (array)$this->rawheaders;\n\t\t\twhile(list($headerKey,$headerVal) = each($this->rawheaders))\n\t\t\t\t$headers[] = $headerKey.\": \".$headerVal;\n\t\t}\n\t\tif(!empty($content_type)) {\n\t\t\tif ($content_type == \"multipart/form-data\")\n\t\t\t\t$headers[] = \"Content-type: $content_type; boundary=\".$this->_mime_boundary;\n\t\t\telse\n\t\t\t\t$headers[] = \"Content-type: $content_type\";\n\t\t}\n\t\tif(!empty($body))\n\t\t\t$headers[] = \"Content-length: \".strlen($body);\n\t\tif(!empty($this->user) || !empty($this->pass))\n\t\t\t$headers[] = \"Authorization: BASIC \".base64_encode($this->user.\":\".$this->pass);\n\t\tif (function_exists('curl_init')) {\n\t\t\t$ch = curl_init();\n\t\t\tcurl_setopt($ch, CURLOPT_URL, $URI);\n\t\t\tcurl_setopt($ch, CURLOPT_HEADER, true); \n\t\t\tcurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);\n\t\t\tcurl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);\n\t\t\tcurl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); \n\t\t\tcurl_setopt($ch, CURLOPT_HTTPHEADER, $headers); \n\t\t\tcurl_setopt($ch, CURLOPT_TIMEOUT, $this->read_timeout);\n\t\t\tif(!empty($body)) {\n\t\t\t\tcurl_setopt($ch, CURLOPT_POST, true);\n\t\t\t\tcurl_setopt($ch, CURLOPT_POSTFIELDS, $body);\n\t\t\t}\n\t\t\t$data = curl_exec($ch);\n\t\t\tif ($data === false) {\n\t\t\t\t$this->error = \"Error: Curl error  \".curl_error($ch);\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$parts = explode(\"\\r\\n\\r\\n\",$data,2);\n\t\t\t$result_headers = explode(\"\\r\\n\",$parts[0]);\n\t\t\t$results = $parts[1];\n\t\t\tunset($parts);\n\t\t} else {\n\t\t\t\tfor($curr_header = 0; $curr_header < count($headers); $curr_header++) {\n\t\t\t\t\t$safer_header = strtr( $headers[$curr_header], \"\\\"\", \" \" );\n\t\t\t\t\t$cmdline_params .= \" -H \\\"\".$safer_header.\"\\\"\";\n\t\t\t\t}\n\t\t\n\t\t\t\tif(!empty($body))\n\t\t\t\t\t$cmdline_params .= \" -d \\\"$body\\\"\";\n\t\t\n\t\t\t\tif($this->read_timeout > 0)\n\t\t\t\t\t$cmdline_params .= \" -m \".$this->read_timeout;\n\t\t\n\t\t\t\t$headerfile = tempnam($temp_dir, \"sno\");\n\t\t\n\t\t\t\texec($this->curl_path.\" -k -D \\\"$headerfile\\\"\".$cmdline_params.\" \\\"\".escapeshellcmd($URI).\"\\\"\",$results,$return);\n\t\t\n\t\t\t\tif($return)\n\t\t\t\t{\n\t\t\t\t\t$this->error = \"Error: cURL could not retrieve the document, error $return.\";\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t\n\t\t\t$results = implode(\"\\r\\n\",$results);\n\t\n\t\t\t$result_headers = file(\"$headerfile\");\n\t\t}\n\t\t$this->_redirectaddr = false;\n\t\tunset($this->headers);\n\n\t\tfor($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++)\n\t\t{\n\t\t\t\t\n\t\t\t// if a header begins with Location: or URI:, set the redirect\n\t\t\tif(preg_match(\"/^(Location: |URI: )/i\",$result_headers[$currentHeader]))\n\t\t\t{\n\t\t\t\t// get URL portion of the redirect\n\t\t\t\tpreg_match(\"/^(Location: |URI:)\\s+(.*)/\",chop($result_headers[$currentHeader]),$matches);\n\t\t\t\t// look for :// in the Location header to see if hostname is included\n\t\t\t\tif (!empty($matches)) {\n\t\t\t\t\tif(!preg_match(\"|\\:\\/\\/|\",$matches[2]))\n\t\t\t\t\t{\n\t\t\t\t\t\t// no host in the path, so prepend\n\t\t\t\t\t\t$this->_redirectaddr = $URI_PARTS[\"scheme\"].\"://\".$this->host;\n\t\t\t\t\t\t// eliminate double slash\n\t\t\t\t\t\tif(!preg_match(\"|^/|\",$matches[2]))\n\t\t\t\t\t\t\t$this->_redirectaddr .= \"/\".$matches[2];\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t$this->_redirectaddr .= $matches[2];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\t$this->_redirectaddr = $matches[2];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(preg_match(\"|^HTTP/|\",$result_headers[$currentHeader]))\n\t\t\t\t$this->response_code = $result_headers[$currentHeader];\n\n\t\t\t$this->headers[] = $result_headers[$currentHeader];\n\t\t}\n\n\t\t// check if there is a a redirect meta tag\n\n\t\tif(preg_match(\"'<meta[\\s]*http-equiv[^>]*?content[\\s]*=[\\s]*[\\\"\\']?\\d+;[\\s]*URL[\\s]*=[\\s]*([^\\\"\\']*?)[\\\"\\']?>'i\",$results,$match))\n\t\t{\n\t\t\t$this->_redirectaddr = $this->_expandlinks($match[1],$URI);\n\t\t}\n\n\t\t// have we hit our frame depth and is there frame src to fetch?\n\t\tif(($this->_framedepth < $this->maxframes) && preg_match_all(\"'<frame\\s+.*src[\\s]*=[\\'\\\"]?([^\\'\\\"\\>]+)'i\",$results,$match))\n\t\t{\n\t\t\t$this->results[] = $results;\n\t\t\tfor($x=0; $x<count($match[1]); $x++)\n\t\t\t\t$this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS[\"scheme\"].\"://\".$this->host);\n\t\t}\n\t\t// have we already fetched framed content?\n\t\telseif(is_array($this->results))\n\t\t\t$this->results[] = $results;\n\t\t// no framed content\n\t\telse\n\t\t\t$this->results = $results;\n\t\tif (isset($headerfile) && file_exists($headerfile))\n\t\t\tunlink($headerfile);\n\n\t\treturn true;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tsetcookies()\n\tPurpose:\tset cookies for a redirection\n\t\\*======================================================================*/\n\n\tfunction setcookies()\n\t{\n\t\tfor($x=0; $x<count($this->headers); $x++)\n\t\t{\n\t\t\tif(preg_match('/^set-cookie:[\\s]+([^=]+)=([^;]+)/i', $this->headers[$x],$match))\n\t\t\t\t$this->cookies[$match[1]] = urldecode($match[2]);\n\t\t}\n\t}\n\n\n\t/*======================================================================*\\\n\t Function:\t_check_timeout\n\tPurpose:\tchecks whether timeout has occurred\n\tInput:\t\t$fp\tfile pointer\n\t\\*======================================================================*/\n\n\tfunction _check_timeout($fp)\n\t{\n\t\tif ($this->read_timeout > 0) {\n\t\t\t$fp_status = socket_get_status($fp);\n\t\t\tif ($fp_status[\"timed_out\"]) {\n\t\t\t\t$this->timed_out = true;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\t_connect\n\tPurpose:\tmake a socket connection\n\tInput:\t\t$fp\tfile pointer\n\t\\*======================================================================*/\n\n\tfunction _connect(&$fp)\n\t{\n\t\tif(!empty($this->proxy_host) && !empty($this->proxy_port))\n\t\t{\n\t\t\t$this->_isproxy = true;\n\n\t\t\t$host = $this->proxy_host;\n\t\t\t$port = $this->proxy_port;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$host = $this->host;\n\t\t\t$port = $this->port;\n\t\t}\n\n\t\t$this->status = 0;\n\n\t\tif($fp = fsockopen(\n\t\t\t\t$host,\n\t\t\t\t$port,\n\t\t\t\t$errno,\n\t\t\t\t$errstr,\n\t\t\t\t$this->_fp_timeout\n\t\t))\n\t\t{\n\t\t\t// socket connection succeeded\n\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// socket connection failed\n\t\t\t$this->status = $errno;\n\t\t\tswitch($errno)\n\t\t\t{\n\t\t\t\tcase -3:\n\t\t\t\t\t$this->error=\"socket creation failed (-3)\";\n\t\t\t\tcase -4:\n\t\t\t\t\t$this->error=\"dns lookup failure (-4)\";\n\t\t\t\tcase -5:\n\t\t\t\t\t$this->error=\"connection refused or timed out (-5)\";\n\t\t\t\tdefault:\n\t\t\t\t\t$this->error=\"connection failed (\".$errno.\")\";\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\t/*======================================================================*\\\n\t Function:\t_disconnect\n\tPurpose:\tdisconnect a socket connection\n\tInput:\t\t$fp\tfile pointer\n\t\\*======================================================================*/\n\n\tfunction _disconnect($fp)\n\t{\n\t\treturn(fclose($fp));\n\t}\n\n\n\t/*======================================================================*\\\n\t Function:\t_prepare_post_body\n\tPurpose:\tPrepare post body according to encoding type\n\tInput:\t\t$formvars  - form variables\n\t$formfiles - form upload files\n\tOutput:\t\tpost body\n\t\\*======================================================================*/\n\n\tfunction _prepare_post_body($formvars, $formfiles)\n\t{\n\t\tsettype($formvars, \"array\");\n\t\tsettype($formfiles, \"array\");\n\t\t$postdata = '';\n\n\t\tif (count($formvars) == 0 && count($formfiles) == 0)\n\t\t\treturn;\n\t\tif (is_string($formvars)) return $formvars;\n\t\tif((count($formvars) == 1) && isset($formvars[0])) return $formvars[0];\n\t\tswitch ($this->_submit_type) {\n\t\t\tcase \"application/x-www-form-urlencoded\":\n\t\t\t\treset($formvars);\n\t\t\t\twhile(list($key,$val) = each($formvars)) {\n\t\t\t\t\tif (is_array($val) || is_object($val)) {\n\t\t\t\t\t\twhile (list($cur_key, $cur_val) = each($val)) {\n\t\t\t\t\t\t\t$postdata .= urlencode($key).\"[]=\".urlencode($cur_val).\"&\";\n\t\t\t\t\t\t}\n\t\t\t\t\t} else\n\t\t\t\t\t\t$postdata .= urlencode($key).\"=\".urlencode($val).\"&\";\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"multipart/form-data\":\n\t\t\t\t$this->_mime_boundary = \"--------\".md5(uniqid(microtime()));\n\n\t\t\t\treset($formvars);\n\t\t\t\twhile(list($key,$val) = each($formvars)) {\n\t\t\t\t\tif (is_array($val) || is_object($val)) {\n\t\t\t\t\t\twhile (list($cur_key, $cur_val) = each($val)) {\n\t\t\t\t\t\t\t$postdata .= \"--\".$this->_mime_boundary.\"\\r\\n\";\n\t\t\t\t\t\t\t$postdata .= \"Content-Disposition: form-data; name=\\\"$key\\[\\]\\\"\\r\\n\\r\\n\";\n\t\t\t\t\t\t\t$postdata .= \"$cur_val\\r\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$postdata .= \"--\".$this->_mime_boundary.\"\\r\\n\";\n\t\t\t\t\t\t$postdata .= \"Content-Disposition: form-data; name=\\\"$key\\\"\\r\\n\\r\\n\";\n\t\t\t\t\t\t$postdata .= \"$val\\r\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treset($formfiles);\n\t\t\t\twhile (list($field_name, $file_names) = each($formfiles)) {\n\t\t\t\t\tsettype($file_names, \"array\");\n\t\t\t\t\twhile (list(, $file_name) = each($file_names)) {\n\t\t\t\t\t\t$file_content = file_get_contents($file_name);\n\t\t\t\t\t\tif (!$file_content) continue;\n\n\t\t\t\t\t\t$base_name = basename($file_name);\n\n\t\t\t\t\t\t$postdata .= \"--\".$this->_mime_boundary.\"\\r\\n\";\n\t\t\t\t\t\t$postdata .= \"Content-Disposition: form-data; name=\\\"$field_name\\\"; filename=\\\"$base_name\\\"\\r\\nContent-Type: image/jpeg\\r\\n\\r\\n\";\n\t\t\t\t\t\t$postdata .= \"$file_content\\r\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$postdata .= \"--\".$this->_mime_boundary.\"--\\r\\n\";\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn $postdata;\n\t}\n}"
  },
  {
    "path": "old_version/Thinkphp/Wechatauth.class.php",
    "content": "<?php\n/**\n *\t微信公众平台PHP-SDK\n *  Wechatauth为非官方微信登陆API\n *  用户通过扫描网页提供的二维码实现登陆信息获取\n *  主要实现如下功能:\n *  get_login_code() 获取登陆授权码, 通过授权码才能获取二维码\n *  get_code_image($code='') 将上面获取的授权码转换为图片二维码\n *  verify_code() 鉴定是否登陆成功,返回200为最终授权成功.\n *  get_login_info() 鉴定成功后调用此方法即可获取用户基本信息\n *  get_avatar($url) 获取用户头像图片数据\n *  @author dodge <dodgepudding@gmail.com>\n *  @link https://github.com/dodgepudding/wechat-php-sdk\n *  @version 1.1\n *  \n */\ninclude \"Snoopy.class.php\";\nclass Wechatauth\n{\n\tprivate $cookie;\n\tprivate $skey;\n\tprivate $_cookiename;\n\tprivate $_cookieexpired = 3600;\n\tprivate $_account = 'test';\n\tprivate $_datapath = './data/cookie_';\n\tprivate $debug;\n\tprivate $_logcallback;\n\tpublic $login_user; //当前登陆用户, 调用get_login_info后获取\n\t\n\tpublic function __construct($options)\n\t{\n\t\t$this->_account = isset($options['account'])?$options['account']:'';\n\t\t$this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath;\n\t\t$this->debug = isset($options['debug'])?$options['debug']:false;\n\t\t$this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;\n\t\t$this->_cookiename = $this->_datapath.$this->_account;\n\t\t$this->getCookie($this->_cookiename);\n\t}\n\t/**\n\t * 把cookie写入缓存\n\t * @param  string $filename 缓存文件名\n\t * @param  string $content  文件内容\n\t * @return bool\n\t */\n\tpublic function saveCookie($filename,$content){\n\t\treturn S($filename,$content,$this->_cookieexpired);\n\t}\n\n\t/**\n\t * 读取cookie缓存内容\n\t * @param  string $filename 缓存文件名\n\t * @return string cookie\n\t */\n\tpublic function getCookie($filename){\n\t\t$data = S($filename);\n\t\tif ($data) $this->cookie = $data;\n\t\treturn $this->cookie;\n\t}\n\t\n\t/*\n\t * 删除cookie\n\t */\n\tpublic function deleteCookie($filename) {\n\t\t$this->cookie = '';\n\t\tS($filename,null);\n\t\treturn true;\n\t}\n\t\n\tprivate function log($log){\n\t\tif ($this->debug && function_exists($this->_logcallback)) {\n\t\t\tif (is_array($log)) $log = print_r($log,true);\n\t\t\treturn call_user_func($this->_logcallback,$log);\n\t\t}\n\t}\n\t\n\t/**\n\t * 获取登陆二维码对应的授权码\n\t */\t\n\tpublic function get_login_code(){\n\t\tif ($this->_logincode) return $this->_logincode;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$codeurl = 'https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_='.$t;\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->fetch($codeurl);\n\t\t$result = $send_snoopy->results;\n\t\tif ($result) {\n\t\t\tpreg_match(\"/window.QRLogin.uuid\\s+=\\s+\\\"([^\\\"]+)\\\"/\",$result,$matches);\n\t\t\tif(count($matches)>1) {\n\t\t\t\t$this->_logincode = $matches[1];\n\t\t\t\t$_SESSION['login_step'] = 0;\n\t\t\t\treturn $this->_logincode;\n\t\t\t}\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * 通过授权码获取对应的二维码图片地址\n\t * @param string $code\n\t * @return string image url\n\t */\n\tpublic function get_code_image($code=''){\n\t\tif ($code=='') $code = $this->_logincode;\n\t\tif (!$code) return false;\n\t\treturn 'http://login.weixin.qq.com/qrcode/'.$this->_logincode.'?t=webwx';\n\t}\n\t\n\t/**\n\t * 设置二维码对应的授权码\n\t * @param string $code\n\t * @return class $this\n\t */\n\tpublic  function set_login_code($code) {\n\t\t$this->_logincode = $code;\n\t\treturn $this;\n\t}\n\t\n\t/**\n\t * 二维码登陆验证\n\t *\n\t * @return status:\n\t * >=400: invaild code; 408: not auth and wait, 400,401: not valid or expired\n\t * 201: just scaned but not confirm\n\t * 200: confirm then you can get user info\n\t */\n\tpublic function verify_code() {\n\t\tif (!$this->_logincode) return false;\n\t\t$t = time().strval(mt_rand(100,999));\n\n\t\t\t$url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid='.$this->_logincode.'&tip=0&_='.$t;\n\t\t\t$send_snoopy = new Snoopy; \n\t\t\t$send_snoopy->referer = \"https://wx.qq.com/\";\n\t\t\t$send_snoopy->fetch($url);\n\t\t\t$result = $send_snoopy->results;\n\t\t\t$this->log('step1:'.$result);\n\t\t\tif ($result) {\n\t\t\t\tpreg_match(\"/window\\.code=(\\d+)/\",$result,$matches);\n\t\t\t\tif(count($matches)>1) {\n\t\t\t\t\t$status = intval($matches[1]);\n\t\t\t\t\tif ($status==201) $_SESSION['login_step'] = 1;\n\t\t\t\t\tif ($status==200) {\n\t\t\t\t\t\tpreg_match(\"/ticket=([0-9a-z-_]+)&lang=zh_CN&scan=(\\d+)/\",$result,$matches);\n\t\t\t\t\t\tpreg_match(\"/window.redirect_uri=\\\"([^\\\"]+)\\\"/\",$result,$matcheurl);\n\t\t\t\t\t\t$this->log('step2:'.print_r($matches,true));\n\t\t\t\t\t\tif (count($matcheurl)>1) {\n\t\t\t\t\t\t\t$ticket = $matches[1];\n\t\t\t\t\t\t\t$scan = $matches[2];\n\t\t\t\t\t\t\t//$loginurl = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket='.$ticket.'&lang=zh_CN&scan='.$scan.'&fun=new';\n\t\t\t\t\t\t\t$loginurl = str_replace(\"wx.qq.com\", \"wx2.qq.com\", $matcheurl[1]).'&fun=old';\n\t\t\t\t\t\t\t$urlpart = parse_url($loginurl);\n\t\t\t\t\t\t\t$send_snoopy = new Snoopy; \n\t\t\t\t\t\t\t$send_snoopy->referer = \"https://{$urlpart['host']}/cgi-bin/mmwebwx-bin/webwxindex?t=chat\";\n\t\t\t\t\t\t\t$send_snoopy->fetch($loginurl);\n\t\t\t\t\t\t\t$result = $send_snoopy->results;\n\t\t\t\t\t\t\t$xml = simplexml_load_string($result);\n\t\t\t\t\t\t\tif ($xml->ret==\"0\") $this->skey = $xml->skey;\n\t\t\t\t\t\t\tforeach ($send_snoopy->headers as $key => $value) {\n\t\t\t\t\t\t\t\t$value = trim($value);\n\t\t\t\t\t\t\t\tif(strpos($value,'Set-Cookie: ') !== false){\n\t\t\t\t\t\t\t\t\t$tmp = str_replace(\"Set-Cookie: \",\"\",$value);\n\t\t\t\t\t\t\t\t\t$tmparray = explode(';', $tmp);\n\t\t\t\t\t\t\t\t\t$item = trim($tmparray[0]);\n\t\t\t\t\t\t\t\t\t$cookie.=$item.';';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$cookie .=\"Domain=.qq.com;\";\n\t\t\t\t\t\t\t$this->cookie = $cookie;\n\t\t\t\t\t\t\t$this->log('step3:'.$loginurl.';cookie:'.$cookie.';respond:'.$result);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t$this->saveCookie($this->_cookiename,$this->cookie);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn $status;\n\t\t\t\t}\n\t\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取登陆的cookie\n\t *\n\t * @param bool $is_array 是否以数值方式返回，默认否，返回字符串\n\t * @return string|array\n\t */\n\tpublic function get_login_cookie($is_array = false){\n\t\tif (!$is_array)\treturn $this->cookie;\n\t\t$c_arr = explode(';',$this->cookie);\n\t\t$cookie = array();\n\t\tforeach($c_arr as $item) {\n\t\t\t$kitem = explode('=',trim($item));\n\t\t\tif (count($kitem)>1) {\n\t\t\t\t$key = trim($kitem[0]);\n\t\t\t\t$val = trim($kitem[1]);\n\t\t\t\tif (!empty($val)) $cookie[$key] = $val;\n\t\t\t}\n\t\t}\n\t\treturn $cookie;\n\t}\n\t\n\t/**\n\t * \t 授权登陆后获取用户登陆信息\n\t */\n\tpublic function get_login_info(){\n\t\tif (!$this->cookie) return false;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$send_snoopy = new Snoopy; \n\t\t$submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r='.$t.'&skey='.urlencode($this->skey);\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://wx2.qq.com/\";\n\t\t$citems = $this->get_login_cookie(true);\n\t\t$post = array(\n\t\t\t\"BaseRequest\"=>array(\n\t\t\t\tarray(\n\t\t\t\t\t\"Uin\"=>$citems['wxuin'],\n\t\t\t\t\t\"Sid\"=>$citems['wxsid'],\n\t\t\t\t\t\"Skey\"=>$this->skey,\n\t\t\t\t\t\"DeviceID\"=>''\n\t\t\t\t)\n\t\t\t)\n\t\t);\n\t\t$send_snoopy->submit($submit,json_encode($post));\n\t\t$this->log('login_info:'.$send_snoopy->results);\n\t\t$result = json_decode($send_snoopy->results,true);\n\t\tif ($result['BaseResponse']['Ret']<0) return false;\n\t\t$this->_login_user = $result['User'];\n\t\treturn $result;\n\t}\n\t\n\t/**\n\t *  获取头像\n\t *  @param string $url 传入从用户信息接口获取到的头像地址\n\t */\n\tpublic function get_avatar($url) {\n\t\tif (!$this->cookie) return false;\n\t\tif (strpos($url, 'http')===false) {\n\t\t\t$url = 'http://wx2.qq.com'.$url;\n\t\t}\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://wx2.qq.com/\";\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\t\tif ($result) \n\t\t\treturn $result;\n\t\telse\n\t\t\treturn false;\n\t}\n\t\n\t/**\n\t * 登出当前登陆用户\n\t */\n\tpublic function logout(){\n\t\tif (!$this->cookie) return false;\n\t\tpreg_match(\"/wxuin=(\\w+);/\",$this->cookie,$matches);\n\t\tif (count($matches)>1) $uid = $matches[1];\n\t\tpreg_match(\"/wxsid=(\\w+);/\",$this->cookie,$matches);\n\t\tif (count($matches)>1) $sid = $matches[1];\n\t\t$this->log('logout: uid='.$uid.';sid='.$sid);\n\t\t$send_snoopy = new Snoopy; \n\t\t$submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1';\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://wx2.qq.com/\";\n\t\t$send_snoopy->submit($submit,array('uin'=>$uid,'sid'=>$sid));\n\t\t$this->deleteCookie($this->_cookiename);\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "old_version/Thinkphp/Wechatext.class.php",
    "content": "<?php\n/**\n *\t微信公众平台PHP-SDK\n *  Wechatext为非官方微信发送API\n *  注: 用户id为通过getMsg()方法获取的FakeId值\n *  主要实现如下功能:\n *  send($id,$content) 向某用户id发送微信文字信息\n *  getUserList($page,$pagesize,$groupid) 获取用户信息\n *  getGroupList($page,$pagesize) 获取群组信息\n *  sendNews($id,$msgid) 发送图文消息\n *  getNewsList($page,$pagesize) 获取图文信息列表\n *  uploadFile($filepath,$type) 上传附件,包括图片/音频/视频\n *  addPreview($title,$author,$summary,$content,$photoid,$srcurl='')   创建新的图文信息 \n *  getFileList($type,$page,$pagesize) 获取素材库文件列表\n *  sendImage($id,$fid) 发送图片消息\n *  sendAudio($id,$fid) 发送音频消息\n *  sendVideo($id,$fid) 发送视频消息\n *  getInfo($id) 根据id获取用户资料\n *  getNewMsgNum($lastid) 获取从$lastid算起新消息的数目\n *  getTopMsg() 获取最新一条消息的数据, 此方法获取的消息id可以作为检测新消息的$lastid依据\n *  getMsg($lastid,$offset=0,$perpage=50,$day=0,$today=0,$star=0) 获取最新的消息列表, 列表将返回消息id, 用户id, 消息类型, 文字消息等参数\n *  消息返回结构:  {\"id\":\"消息id\",\"type\":\"类型号(1为文字,2为图片,3为语音)\",\"fileId\":\"0\",\"hasReply\":\"0\",\"fakeId\":\"用户uid\",\"nickName\":\"昵称\",\"dateTime\":\"时间戳\",\"content\":\"文字内容\"} \n *  getMsgImage($msgid,$mode='large') 若消息type类型为2, 调用此方法获取图片数据\n *  getMsgVoice($msgid) 若消息type类型为3, 调用此方法获取语音数据\n *  quickSetInterface($url, $token) 快速设置接口信息\n *  @author dodge <dodgepudding@gmail.com>\n *  @link https://github.com/dodgepudding/wechat-php-sdk\n *  @version 1.2\n *  \n */\n\ninclude \"Snoopy.class.php\";\nclass Wechatext\n{\n\tprivate $cookie;\n\tprivate $_cookiename;\n\tprivate $_cookieexpired = 3600;\n\tprivate $_account;\n\tprivate $_password;\n\tprivate $_datapath = './data/cookie_';\n\tprivate $debug;\n\tprivate $_logcallback;\n\tprivate $_token;\n\t\n\tpublic function __construct($options)\n\t{\n\t\t$this->_account = isset($options['account'])?$options['account']:'';\n\t\t$this->_password = isset($options['password'])?$options['password']:'';\n\t\t$this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath;\n\t\t$this->debug = isset($options['debug'])?$options['debug']:false;\n\t\t$this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;\n\t\t$this->_cookiename = $this->_datapath.$this->_account;\n\t\t$this->cookie = $this->getCookie($this->_cookiename);\n\t}\n\n\t/**\n\t * 主动发消息\n\t * @param  string $id      用户的uid(即FakeId)\n\t * @param  string $content 发送的内容\n\t */\n\tpublic function send($id,$content)\n\t{\n\t\t$send_snoopy = new Snoopy; \n\t\t$post = array();\n\t\t$post['tofakeid'] = $id;\n\t\t$post['type'] = 1;\n\t\t$post['token'] = $this->_token;\n\t\t$post['content'] = $content;\n\t\t$post['ajax'] = 1;\n        $send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=$id&token={$this->_token}&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response\";\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\treturn $send_snoopy->results;\n\t}\n\t\n\t/**\n\t * 群发功能 纯文本\n\t * @param string $content\n\t * @return string\n\t */\n\tpublic function mass($content) {\n\t\t$send_snoopy = new Snoopy;\n\t\t$post = array();\n\t\t$post['type'] = 1;\n\t\t$post['token'] = $this->_token;\n\t\t$post['content'] = $content;\n\t\t$post['ajax'] = 1;\n\t\t$post['city']='';\n\t\t$post['country']='';\n\t\t$post['f']='json';\n\t\t$post['groupid']='-1';\n\t\t$post['imgcode']='';\n\t\t$post['lang']='zh_CN';\n\t\t$post['province']='';\n\t\t$post['random']=  rand(0, 1);\n\t\t$post['sex']=0;\n\t\t$post['synctxnews']=0;\n\t\t$post['synctxweibo']=0;\n\t\t$post['t']='ajax-response';\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/masssend\";\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\treturn $send_snoopy->results;\n\t}\n\t\n\t/**\n\t * 群发功能 图文素材\n\t * @param int $appmsgid 图文素材ID\n\t * @return string\n\t */\n\tfunction massNews($appmsgid){\n\t\t$send_snoopy = new Snoopy;\n\t\t$post = array();\n\t\t$post['type'] = 10;\n\t\t$post['token'] = $this->_token;\n\t\t$post['appmsgid'] = $appmsgid;\n\t\t$post['ajax'] = 1;\n\t\t$post['city']='';\n\t\t$post['country']='';\n\t\t$post['f']='json';\n\t\t$post['groupid']='-1';\n\t\t$post['imgcode']='';\n\t\t$post['lang']='zh_CN';\n\t\t$post['province']='';\n\t\t$post['random']=  rand(0, 1);\n\t\t$post['sex']=0;\n\t\t$post['synctxnews']=0;\n\t\t$post['synctxweibo']=0;\n\t\t$post['t']='ajax-response';\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/masssend\";\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\treturn $send_snoopy->results;\n\t}\n\t\n\t/**\n\t * 获取用户列表列表\n\t * @param $page 页码(从0开始)\n\t * @param $pagesize 每页大小\n\t * @param $groupid 分组id\n\t * @return array ({contacts:[{id:12345667,nick_name:\"昵称\",remark_name:\"备注名\",group_id:0},{}....]})\n\t */\n\tfunction getUserList($page=0,$pagesize=10,$groupid=0){\n\t\t$send_snoopy = new Snoopy;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=\".$pagesize.\"&pageidx=\".$page.\"&type=0&groupid=0&lang=zh_CN&token=\".$this->_token;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=\".$pagesize.\"&pageidx=\".$page.\"&type=0&groupid=$groupid&lang=zh_CN&f=json&token=\".$this->_token;\n\t\t$send_snoopy->fetch($submit);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('userlist:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['contact_list'])) {\n\t\t\t$json = json_decode($json['contact_list'],true);\n\t\t\tif (isset($json['contacts']))\n\t\t\t\treturn $json['contacts'];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取分组列表\n\t * \n\t */\n\tfunction getGroupList(){\n\t\t$send_snoopy = new Snoopy;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&lang=zh_CN&token=\".$this->_token;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&lang=zh_CN&f=json&token=\".$this->_token;\n\t\t$send_snoopy->fetch($submit);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('userlist:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['group_list'])){\n\t\t\t$json = json_decode($json['group_list'],true);\n\t\t\tif (isset($json['groups']))\n\t\t\t\treturn $json['groups'];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取图文信息列表\n\t * @param $page 页码(从0开始)\n\t * @param $pagesize 每页大小\n\t * @return array\n\t */\n\tpublic function getNewsList($page,$pagesize=10) {\n\t\t$send_snoopy = new Snoopy;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$type=10;\n\t\t$begin = $page*$pagesize;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=\".$this->_token.\"&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/appmsg?token=\".$this->_token.\"&lang=zh_CN&type=$type&action=list&begin=$begin&count=$pagesize&f=json&random=0.\".$t;\n\t\t$send_snoopy->fetch($submit);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('newslist:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['app_msg_info'])) {\n\t\t\treturn $json['app_msg_info'];\n\t\t} \n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取与指定用户的对话内容\n\t * @param  $fakeid\n\t * @return  array\n\t */\n\tpublic function getDialogMsg($fakeid) {\n\t\t$send_snoopy = new Snoopy;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=\".$this->_token.\"&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=\".$fakeid.\"&token=\".$this->_token.\"&lang=zh_CN&f=json&random=\".$t;\n\t\t$send_snoopy->fetch($submit);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('DialogMsg:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['page_info'])) {\n\t\t\treturn $json['page_info'];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 发送图文信息,必须从图文库里选取消息ID发送\n\t * @param  string $id      用户的uid(即FakeId)\n\t * @param  string $msgid 图文消息id\n\t */\n\tpublic function sendNews($id,$msgid)\n\t{\n\t\t$send_snoopy = new Snoopy; \n\t\t$post = array();\n\t\t$post['tofakeid'] = $id;\n\t\t$post['type'] = 10;\n\t\t$post['token'] = $this->_token;\n\t\t$post['fid'] = $msgid;\n\t\t$post['appmsgid'] = $msgid;\n\t\t$post['error'] = 'false';\n\t\t$post['ajax'] = 1;\n        $send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response\";\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\treturn $send_snoopy->results;\n\t}\n\t\n\t/**\n\t * 上传附件(图片/音频/视频)\n\t * @param string $filepath 本地文件地址\n\t * @param int $type 文件类型: 2:图片 3:音频 4:视频\n\t */\n\tpublic function uploadFile($filepath,$type=2) {\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->referer = \"http://mp.weixin.qq.com/cgi-bin/indexpage?t=wxm-upload&lang=zh_CN&type=2&formId=1\";\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$post = array('formId'=>'');\n\t\t$postfile = array('uploadfile'=>$filepath);\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->set_submit_multipart();\n\t\t$submit = \"http://mp.weixin.qq.com/cgi-bin/uploadmaterial?cgi=uploadmaterial&type=$type&token=\".$this->_token.\"&t=iframe-uploadfile&lang=zh_CN&formId=\tfile_from_\".$t;\n\t\t$send_snoopy->submit($submit,$post,$postfile);\n\t\t$tmp = $send_snoopy->results;\n\t\t$this->log('upload:'.$tmp);\n\t\tpreg_match(\"/formId,.*?\\'(\\d+)\\'/\",$tmp,$matches);\n\t\tif (isset($matches[1])) {\n\t\t\treturn $matches[1];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 创建图文消息\n\t * @param array $title 标题\n\t * @param array $summary 摘要\n\t * @param array $content 内容\n\t * @param array $photoid 素材库里的图片id(可通过uploadFile上传后获取)\n\t * @param array $srcurl 原文链接\n\t * @return json\n\t */\n\tpublic function addPreview($title,$author,$summary,$content,$photoid,$srcurl='') {\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&token='.$this->_token;\n\t\t\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&t=ajax-response&sub=create&token=\".$this->_token;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t\n\t\t$send_snoopy->set_submit_normal();\n\t\t$post = array(\n\t\t\t\t'token'=>$this->_token,\n\t\t\t\t'type'=>10,\n\t\t\t\t'lang'=>'zh_CN',\n\t\t\t\t'sub'=>'create',\n\t\t\t\t'ajax'=>1,\n\t\t\t\t'AppMsgId'=>'',\t\t\t\t\n\t\t\t\t'error'=>'false',\n\t\t);\n\t\tif (count($title)==count($author)&&count($title)==count($summary)&&count($title)==count($content)&&count($title)==count($photoid))\n\t\t{\n\t\t\t$i = 0;\n\t\t\tforeach($title as $v) {\n\t\t\t\t$post['title'.$i] = $title[$i];\n\t\t\t\t$post['author'.$i] = $author[$i];\n\t\t\t\t$post['digest'.$i] = $summary[$i];\n\t\t\t\t$post['content'.$i] = $content[$i];\n\t\t\t\t$post['fileid'.$i] = $photoid[$i];\n\t\t\t\tif ($srcurl[$i]) $post['sourceurl'.$i] = $srcurl[$i];\n\t\t\t\t\n\t\t\t\t$i++;\n\t\t\t\t}\n\t\t}\n\t\t$post['count'] = $i;\n\t\t$post['token'] = $this->_token;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$tmp = $send_snoopy->results;\n\t\t$this->log('step2:'.$tmp);\n\t\t$json = json_decode($tmp,true);\n\t\treturn $json;\n\t}\n\t\n\t/**\n\t * 发送媒体文件\n\t * @param $id 用户的uid(即FakeId)\n\t * @param $fid 文件id\n\t * @param $type 文件类型\n\t */\n\tpublic function sendFile($id,$fid,$type) {\n\t\t$send_snoopy = new Snoopy; \n\t\t$post = array();\n\t\t$post['tofakeid'] = $id;\n\t\t$post['type'] = $type;\n\t\t$post['token'] = $this->_token;\n\t\t$post['fid'] = $fid;\n\t\t$post['fileid'] = $fid;\n\t\t$post['error'] = 'false';\n\t\t$post['ajax'] = 1;\n        $send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response\";\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('sendfile:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif ($json && $json['ret']==0) \n\t\t\treturn true;\n\t\telse\n\t\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取素材库文件列表\n\t * @param $type 文件类型: 2:图片 3:音频 4:视频\n\t * @param $page 页码(从0开始)\n\t * @param $pagesize 每页大小\n\t * @return array\n\t */\n\tpublic function getFileList($type,$page,$pagesize=10) {\n\t\t$send_snoopy = new Snoopy;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$begin = $page*$pagesize;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=\".$this->_token.\"&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/filepage?token=\".$this->_token.\"&lang=zh_CN&type=$type&random=0.\".$t.\"&begin=$begin&count=$pagesize&f=json\";\n\t\t$send_snoopy->fetch($submit);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('filelist:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['page_info']))\n\t\t\treturn $json['page_info'];\n\t\telse\n\t\t\treturn false;\n\t}\n\t\n\t/**\n\t * 发送图文信息,必须从库里选取文件ID发送\n\t * @param  string $id      用户的uid(即FakeId)\n\t * @param  string $fid 文件id\n\t */\n\tpublic function sendImage($id,$fid)\n\t{\n\t\treturn $this->sendFile($id,$fid,2);\n\t}\n\t\n\t/**\n\t * 发送语音信息,必须从库里选取文件ID发送\n\t * @param  string $id      用户的uid(即FakeId)\n\t * @param  string $fid 语音文件id\n\t */\n\tpublic function sendAudio($id,$fid)\n\t{\n\t\treturn $this->sendFile($id,$fid,3);\n\t}\n\t\n\t/**\n\t * 发送视频信息,必须从库里选取文件ID发送\n\t * @param  string $id      用户的uid(即FakeId)\n\t * @param  string $fid 视频文件id\n\t */\n\tpublic function sendVideo($id,$fid)\n\t{\n\t\treturn $this->sendFile($id,$fid,4);\n\t}\n\t\n\t/**\n\t * 发送预览图文消息\n\t * @param string $account 账户名称(user_name)\n\t * @param string $title 标题\n\t * @param string $summary 摘要\n\t * @param string $content 内容\n\t * @param string $photoid 素材库里的图片id(可通过uploadFile上传后获取)\n\t * @param string $srcurl 原文链接\n\t * @return json\n\t */\n\tpublic function sendPreview($account,$title,$summary,$content,$photoid,$srcurl='') {\n\t\t$send_snoopy = new Snoopy;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=preview&t=ajax-appmsg-preview\";\n\t\t$send_snoopy->set_submit_normal();\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&lang=zh_CN';\n\t\t$post = array(\n\t\t\t\t'AppMsgId'=>'',\n\t\t\t\t'ajax'=>1,\n\t\t\t\t'content0'=>$content,\n\t\t\t\t'count'=>1,\n\t\t\t\t'digest0'=>$summary,\n\t\t\t\t'error'=>'false',\n\t\t\t\t'fileid0'=>$photoid,\n\t\t\t\t'preusername'=>$account,\n\t\t\t\t'sourceurl0'=>$srcurl,\n\t\t\t\t'title0'=>$title,\n\t\t);\n\t\t$post['token'] = $this->_token;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$tmp = $send_snoopy->results;\n\t\t$this->log('sendpreview:'.$tmp);\n\t\t$json = json_decode($tmp,true);\n\t\treturn $json;\n\t}\n\t\n\t/**\n\t * 获取用户的信息\n\t * @param  string $id 用户的uid(即FakeId)\n\t * @return array  {fake_id:100001,nick_name:'昵称',user_name:'用户名',signature:'签名档',country:'中国',province:'广东',city:'广州',gender:'1',group_id:'0'},groups:{[id:0,name:'未分组',cnt:20]}\n\t */\n\tpublic function getInfo($id)\n\t{\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/getcontactinfo\";\n\t\t$post = array('ajax'=>1,'lang'=>'zh_CN','random'=>'0.'.$t,'token'=>$this->_token,'t'=>'ajax-getcontactinfo','fakeid'=>$id);\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\t$result = json_decode($send_snoopy->results,true);\n\t\tif(isset($result['contact_info'])){\n\t\t\treturn $result['contact_info'];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获得头像数据\n\t *\n\t * @param FakeId $fakeid\n\t * @return JPG二进制数据\n\t */\n\tpublic function getHeadImg($fakeid){\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com/misc/getheadimg?fakeid=$fakeid&token=\".$this->_token.\"&lang=zh_CN\";\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('Head image:'.$fakeid.'; length:'.strlen($result));\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * 获取消息更新数目\n\t * @param int $lastid 最近获取的消息ID,为0时获取总消息数目\n\t * @return int 数目\n\t */\n\tpublic function getNewMsgNum($lastid=0){\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/getnewmsgnum?t=ajax-getmsgnum&lastmsgid=\".$lastid;\n\t\t$post = array('ajax'=>1,'token'=>$this->_token);\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\t$result = json_decode($send_snoopy->results,1);\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn intval($result['newTotalMsgCount']);\n\t}\n\t\n\t/**\n\t * 获取最新一条消息\n\t * @return array {\"id\":\"最新一条id\",\"type\":\"类型号(1为文字,2为图片,3为语音)\",\"fileId\":\"0\",\"hasReply\":\"0\",\"fakeId\":\"用户uid\",\"nickName\":\"昵称\",\"dateTime\":\"时间戳\",\"content\":\"文字内容\",\"playLength\":\"0\",\"length\":\"0\",\"source\":\"\",\"starred\":\"0\",\"status\":\"4\"}        \n\t */\n\tpublic function getTopMsg(){\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=20&day=7&lang=zh_CN&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&count=20&day=7&lang=zh_CN&token=\".$this->_token;\n\t\t$send_snoopy->fetch($submit);\n\t\t$this->log($send_snoopy->results);\n\t\t$result = $send_snoopy->results;\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['msg_items'])) {\n\t\t\t$json = json_decode($json['msg_items'],true);\n\t\t\tif(isset($json['msg_item']))\n\t\t\t\treturn array_shift($json['msg_item']);\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取新消息\n\t * @param $lastid 传入最后的消息id编号,为0则从最新一条起倒序获取\n\t * @param $offset lastid起算第一条的偏移量\n\t * @param $perpage 每页获取多少条\n\t * @param $day 最近几天消息(0:今天,1:昨天,2:前天,3:更早,7:五天内)\n\t * @param $today 是否只显示今天的消息, 与$day参数不能同时大于0\n\t * @param $star 是否星标组信息\n\t * @return array[] 同getTopMsg()返回的字段结构相同\n\t */\n\tpublic function getMsg($lastid=0,$offset=0,$perpage=20,$day=7,$today=0,$star=0){\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/message?t=message/list&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$lastid = $lastid===0 ? '':$lastid;\n\t\t$addstar = $star?'&action=star':'';\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&lang=zh_CN{$addstar}&count=$perpage&timeline=$today&day=$day&frommsgid=$lastid&offset=$offset&token=\".$this->_token;\n\t\t$send_snoopy->fetch($submit);\n\t\t$this->log($send_snoopy->results);\n\t\t$result = $send_snoopy->results;\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['msg_items'])) {\n\t\t\t$json = json_decode($json['msg_items'],true);\n\t\t\tif(isset($json['msg_item']))\n\t\t\t\treturn $json['msg_item'];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取图片消息\n\t * @param int $msgid 消息id\n\t * @param string $mode 图片尺寸(large/small)\n\t * @return jpg二进制文件\n\t */\n\tpublic function getMsgImage($msgid,$mode='large'){\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com/cgi-bin/getimgdata?token=\".$this->_token.\"&msgid=$msgid&mode=$mode&source=&fileId=0\";\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('msg image:'.$msgid.';length:'.strlen($result));\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn $result;\n\t}\n\t\n\t/**\n\t * 获取语音消息\n\t * @param int $msgid 消息id\n\t * @return mp3二进制文件\n\t */\n\tpublic function getMsgVoice($msgid){\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com/cgi-bin/getvoicedata?token=\".$this->_token.\"&msgid=$msgid&fileId=0\";\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('msg voice:'.$msgid.';length:'.strlen($result));\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn $result;\n\t}\n\t\n\t/**\n\t * 开启开发者模式\n\t */\n\tpublic function openDevModel()\n\t{\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN\";\n\t\t$post['flag']=1;\n        $post['type']=2;   \n        $post['token']=$this->_token;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log($send_snoopy->results);\n\t\t$json = json_decode($result,true);\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * 关闭编辑模式\n\t */\n\tpublic function closeEditModel()\n\t{\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN\";\n\t\t$post['flag']=0;\n        $post['type']=1;   \n        $post['token']=$this->_token;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log($send_snoopy->results);\n\t\t$json = json_decode($result,true);\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * 配置接口信息\n\t * @param  string $url      接口回调URL\n\t * @param  string $token    接口Token\n\t */\n\tpublic function setUrlToken($url, $token)\n\t{\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/advanced/advanced?action=interface&t=advanced/interface&lang=zh_CN&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/advanced/callbackprofile?t=ajax-response&lang=zh_CN&token=\".$this->_token;\n\t\t$post['url'] = $url;\n\t\t$post['callback_token'] = $token;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log($send_snoopy->results);\n\t\t$json = json_decode($result,true);\n\t\tif ($json && $json['ret']==0) \n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\t/**\n\t * 快速设置接口\n\t * @param  string $url      接口回调URL\n\t * @param  string $token    接口Token\n\t */\n\tpublic function quickSetInterface($url, $token)\n\t{\n\t\tif ($this->closeEditModel() && $this->openDevModel() && $this->setUrlToken($url, $token))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 模拟登录获取cookie\n\t * @return [type] [description]\n\t */\n\tpublic function login(){\n\t\t$snoopy = new Snoopy; \n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN\";\n\t\t$post[\"username\"] = $this->_account;\n\t\t$post[\"pwd\"] = md5($this->_password);\n\t\t$post[\"f\"] = \"json\";\n\t\t$post[\"imgcode\"] = \"\";\n\t\t$snoopy->referer = \"https://mp.weixin.qq.com/\";\n\t\t$snoopy->submit($submit,$post);\n\t\t$cookie = '';\n\t\t$this->log($snoopy->results);\n\t\t$result = json_decode($snoopy->results,true);\n\t\t\n\t\tif (!isset($result['base_resp']) || $result['base_resp']['ret'] != 0) {\n\t\t\treturn false;\n\t\t}\n        \n\t\tforeach ($snoopy->headers as $key => $value) {\n\t\t\t$value = trim($value);\n\t\t\tif(preg_match('/^set-cookie:[\\s]+([^=]+)=([^;]+)/i', $value,$match))\n\t\t\t\t$cookie .=$match[1].'='.$match[2].'; ';\n\t\t}\n\t\t\n\t\tpreg_match(\"/token=(\\d+)/i\",$result['redirect_url'],$matches);\n\t\tif($matches){\n\t\t\t$this->_token = $matches[1];\n\t\t\t$this->log('token:'.$this->_token);\n\t\t}\n\t\t$cookies='{\"cookie\":\"'.$cookie.'\",\"token\":\"'.$this->_token.'\"}';\n\t\t$this->saveCookie($this->_cookiename,$cookies);\n\t\treturn $cookie;\n\t}\n\n\t/**\n\t * 把cookie写入缓存\n\t * @param  string $filename 缓存文件名\n\t * @param  string $content  文件内容\n\t * @return bool\n\t */\n\tpublic function saveCookie($filename,$content){\n\t\treturn S($filename,$content,$this->_cookieexpired);\n\t}\n\n\t/**\n\t * 读取cookie缓存内容\n\t * @param  string $filename 缓存文件名\n\t * @return string cookie\n\t */\n\tpublic function getCookie($filename){\n\t\t$data = S($filename);\n\t\tif($data){\n\t\t\t$login=json_decode($data,true);\n\t\t\t$send_snoopy = new Snoopy;\n\t\t\t$send_snoopy->rawheaders['Cookie']= $login['cookie'];\n\t\t\t$send_snoopy->maxredirs = 0;\n\t\t\t$url = \"https://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=\".$login['token'];\n\t\t\t$send_snoopy->fetch($url);\n\t\t\t$header = $send_snoopy->headers;\n\t\t\t$this->log('header:'.print_r($send_snoopy->headers,true));\n\t\t\tif( strstr($header[3], 'EXPIRED')){\n\t\t\t\treturn $this->login();\n\t\t\t}else{\n\t\t\t\t$this->_token =$login['token'];\n\t\t\t\treturn $login['cookie'];\n\t\t\t}\n\t\t}else{\n\t\t\treturn $this->login();\n\t\t}\n\t}\n\n\t/**\n\t * 验证cookie的有效性\n\t * @return bool\n\t */\n\tpublic function checkValid()\n\t{\n\t\tif (!$this->cookie || !$this->_token) return false;\n\t\t$send_snoopy = new Snoopy; \n\t\t$post = array('ajax'=>1,'token'=>$this->_token);\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/getregions?id=1017&t=ajax-getregions&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$result = $send_snoopy->results;\n\t\tif(json_decode($result,1)){\n\t\t\treturn true;\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate function log($log){\n\t\tif ($this->debug ) {\n\t\t\tif (function_exists($this->_logcallback)) {\n\t\t\t\tif (is_array($log)) $log = print_r($log,true);\n\t\t\t\treturn call_user_func($this->_logcallback,$log);\n\t\t\t}elseif (class_exists('Log')) {\n\t\t\t\tLog::write('wechat：'.$log, Log::DEBUG);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "old_version/Thinkphp/Wechatpay.class.php",
    "content": "<?php\n/**\n *\t微信公众平台PHP-SDK, 旧版微信支付接口(微信支付V2)\n *  @author  dodge <dodgepudding@gmail.com>\n *  @link https://github.com/dodgepudding/wechat-php-sdk\n *  @version 1.2\n *  参考旧版文档 https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl&lang=zh_CN\n *  usage:\n *   $options = array(\n *\t\t\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id\n *\t\t\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥\n *\t\t\t'partnerid'=>'88888888', //财付通商户身份标识\n *\t\t\t'partnerkey'=>'', //财付通商户权限密钥Key\n *\t\t\t'paysignkey'=>'' //商户签名密钥Key\n *\t\t);\n *\t $payObj = new Wechatpay($options);\n *   $package = $payObj->createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type,$bank_type,$input_charset,$time_start,$time_expire,$transport_fee,$product_fee,$goods_tag,$attach);\n *\n */\nclass Wechatpay\n{\n\tconst API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin';\n\tconst AUTH_URL = '/token?grant_type=client_credential&';\n\tconst API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀\n\tconst PAY_DELIVERNOTIFY = '/pay/delivernotify?';\n\tconst PAY_ORDERQUERY = '/pay/orderquery?';\n\n\tprivate $appid;\n\tprivate $appsecret;\n\tprivate $access_token;\n\tprivate $user_token;\n\tprivate $partnerid;\n\tprivate $partnerkey;\n\tprivate $paysignkey;\n\n\tpublic $debug =  false;\n\tpublic $errCode = 40001;\n\tpublic $errMsg = \"no access\";\n\tprivate $_logcallback;\n\n\tpublic function __construct($options)\n\t{\n\t\t$this->appid = isset($options['appid'])?$options['appid']:'';\n\t\t$this->appsecret = isset($options['appsecret'])?$options['appsecret']:'';\n\t\t$this->partnerid = isset($options['partnerid'])?$options['partnerid']:'';\n\t\t$this->partnerkey = isset($options['partnerkey'])?$options['partnerkey']:'';\n\t\t$this->paysignkey = isset($options['paysignkey'])?$options['paysignkey']:'';\n\t\t$this->debug = isset($options['debug'])?$options['debug']:false;\n\t\t$this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;\n\t}\n\n    private function log($log){\n    \t\tif ($this->debug ) {\n    \t\t\tif (function_exists($this->_logcallback)) {\n    \t\t\tif (is_array($log)) $log = print_r($log,true);\n    \t\t\treturn call_user_func($this->_logcallback,$log);\n    \t\t\t}elseif (class_exists('Log')) {\n    \t\t\t\tLog::write('wechat：'.$log, Log::DEBUG);\n    \t\t\t}\n    \t\t}\n    \t\treturn false;\n    }\n\n\t/**\n\t * GET 请求\n\t * @param string $url\n\t */\n\tprivate function http_get($url){\n\t\t$oCurl = curl_init();\n\t\tif(stripos($url,\"https://\")!==FALSE){\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1\n\t\t}\n\t\tcurl_setopt($oCurl, CURLOPT_URL, $url);\n\t\tcurl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );\n\t\t$sContent = curl_exec($oCurl);\n\t\t$aStatus = curl_getinfo($oCurl);\n\t\tcurl_close($oCurl);\n\t\tif(intval($aStatus[\"http_code\"])==200){\n\t\t\treturn $sContent;\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * POST 请求\n\t * @param string $url\n\t * @param array $param\n\t * @param boolean $post_file 是否文件上传\n\t * @return string content\n\t */\n\tprivate function http_post($url,$param,$post_file=false){\n\t\t$oCurl = curl_init();\n\t\tif(stripos($url,\"https://\")!==FALSE){\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1\n\t\t}\n\t\tif (is_string($param) || $post_file) {\n\t\t\t$strPOST = $param;\n\t\t} else {\n\t\t\t$aPOST = array();\n\t\t\tforeach($param as $key=>$val){\n\t\t\t\t$aPOST[] = $key.\"=\".urlencode($val);\n\t\t\t}\n\t\t\t$strPOST =  join(\"&\", $aPOST);\n\t\t}\n\t\tcurl_setopt($oCurl, CURLOPT_URL, $url);\n\t\tcurl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );\n\t\tcurl_setopt($oCurl, CURLOPT_POST,true);\n\t\tcurl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);\n\t\t$sContent = curl_exec($oCurl);\n\t\t$aStatus = curl_getinfo($oCurl);\n\t\tcurl_close($oCurl);\n\t\tif(intval($aStatus[\"http_code\"])==200){\n\t\t\treturn $sContent;\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * 获取access_token\n\t * @param string $appid 如在类初始化时已提供，则可为空\n\t * @param string $appsecret 如在类初始化时已提供，则可为空\n\t * @param string $token 手动指定access_token，非必要情况不建议用\n\t */\n\tpublic function checkAuth($appid='',$appsecret='',$token=''){\n\t\tif (!$appid || !$appsecret) {\n\t\t\t$appid = $this->appid;\n\t\t\t$appsecret = $this->appsecret;\n\t\t}\n\t\t$authname = 'wechat_access_token'.$appid;\n\t\tif ($token) { //手动指定token，优先使用\n\t\t    $this->access_token=$token;\n\t\t    return $this->access_token;\n\t\t}\n\t\tif ($rs = S($authname))  {\n\t\t\t$this->access_token = $rs;\n\t\t\treturn $rs;\n\t\t}\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::AUTH_URL.'appid='.$appid.'&secret='.$appsecret);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$this->access_token = $json['access_token'];\n\t\t\t$expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;\n\t\t\tS($authname,$this->access_token,$expire);\n\t\t\treturn $this->access_token;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 删除验证数据\n\t * @param string $appid\n\t */\n\tpublic function resetAuth($appid=''){\n\t\tif (!$appid) $appid = $this->appid;\n\t\t$this->access_token = '';\n\t\t$authname = 'wechat_access_token'.$appid;\n\t\tS($authname,null);\n\t\treturn true;\n\t}\n\n\t/**\n\t * 微信api不支持中文转义的json结构\n\t * @param array $arr\n\t */\n\tstatic function json_encode($arr) {\n\t\t$parts = array ();\n\t\t$is_list = false;\n\t\t//Find out if the given array is a numerical array\n\t\t$keys = array_keys ( $arr );\n\t\t$max_length = count ( $arr ) - 1;\n\t\tif (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1\n\t\t\t$is_list = true;\n\t\t\tfor($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position\n\t\t\t\tif ($i != $keys [$i]) { //A key fails at position check.\n\t\t\t\t\t$is_list = false; //It is an associative array.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tforeach ( $arr as $key => $value ) {\n\t\t\tif (is_array ( $value )) { //Custom handling for arrays\n\t\t\t\tif ($is_list)\n\t\t\t\t\t$parts [] = self::json_encode ( $value ); /* :RECURSION: */\n\t\t\t\telse\n\t\t\t\t\t$parts [] = '\"' . $key . '\":' . self::json_encode ( $value ); /* :RECURSION: */\n\t\t\t} else {\n\t\t\t\t$str = '';\n\t\t\t\tif (! $is_list)\n\t\t\t\t\t$str = '\"' . $key . '\":';\n\t\t\t\t//Custom handling for multiple data types\n\t\t\t\tif (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)\n\t\t\t\t\t$str .= $value; //Numbers\n\t\t\t\telseif ($value === false)\n\t\t\t\t$str .= 'false'; //The booleans\n\t\t\t\telseif ($value === true)\n\t\t\t\t$str .= 'true';\n\t\t\t\telse\n\t\t\t\t\t$str .= '\"' . addslashes ( $value ) . '\"'; //All other things\n\t\t\t\t// :TODO: Is there any more datatype we should be in the lookout for? (Object?)\n\t\t\t\t$parts [] = $str;\n\t\t\t}\n\t\t}\n\t\t$json = implode ( ',', $parts );\n\t\tif ($is_list)\n\t\t\treturn '[' . $json . ']'; //Return numerical JSON\n\t\treturn '{' . $json . '}'; //Return associative JSON\n\t}\n\n\t/**\n\t * 获取签名\n\t * @param array $arrdata 签名数组\n\t * @param string $method 签名方法\n\t * @return boolean|string 签名值\n\t */\n\tpublic function getSignature($arrdata,$method=\"sha1\") {\n\t\tif (!function_exists($method)) return false;\n\t\tksort($arrdata);\n\t\t$paramstring = \"\";\n\t\tforeach($arrdata as $key => $value)\n\t\t{\n\t\t\tif(strlen($paramstring) == 0)\n\t\t\t\t$paramstring .= $key . \"=\" . $value;\n\t\t\telse\n\t\t\t\t$paramstring .= \"&\" . $key . \"=\" . $value;\n\t\t}\n\t\t$paySign = $method($paramstring);\n\t\treturn $paySign;\n\t}\n\n\t/**\n\t * 生成随机字串\n\t * @param number $length 长度，默认为16，最长为32字节\n\t * @return string\n\t */\n\tpublic function generateNonceStr($length=16){\n\t\t// 密码字符集，可任意添加你需要的字符\n\t\t$chars = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\n\t\t$str = \"\";\n\t\tfor($i = 0; $i < $length; $i++)\n\t\t{\n\t\t\t$str .= $chars[mt_rand(0, strlen($chars) - 1)];\n\t\t}\n\t\treturn $str;\n\t}\n\n\t/**\n\t * 生成原生支付url\n\t * @param number $productid 商品编号，最长为32字节\n\t * @return string\n\t */\n\tpublic function createNativeUrl($productid){\n\t\t    $nativeObj[\"appid\"] = $this->appid;\n\t\t    $nativeObj[\"appkey\"] = $this->paysignkey;\n\t\t    $nativeObj[\"productid\"] = urlencode($productid);\n\t\t    $nativeObj[\"timestamp\"] = time();\n\t\t    $nativeObj[\"noncestr\"] = $this->generateNonceStr();\n\t\t    $nativeObj[\"sign\"] = $this->getSignature($nativeObj);\n\t\t    unset($nativeObj[\"appkey\"]);\n\t\t    $bizString = \"\";\n\t\t    foreach($nativeObj as $key => $value)\n\t\t    {\n\t\t\tif(strlen($bizString) == 0)\n\t\t\t\t$bizString .= $key . \"=\" . $value;\n\t\t\telse\n\t\t\t\t$bizString .= \"&\" . $key . \"=\" . $value;\n\t\t    }\n\t\t    return \"weixin://wxpay/bizpayurl?\".$bizString;\n\t\t    //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXXX&productid=XXXXXX&timestamp=XXXXXX&noncestr=XXXXXX\n\t}\n\n\n\t/**\n\t * 生成订单package字符串\n\t * @param string $out_trade_no 必填，商户系统内部的订单号,32个字符内,确保在商户系统唯一\n\t * @param string $body 必填，商品描述,128 字节以下\n\t * @param int $total_fee 必填，订单总金额,单位为分\n\t * @param string $notify_url 必填，支付完成通知回调接口，255 字节以内\n\t * @param string $spbill_create_ip 必填，用户终端IP，IPV4字串，15字节内\n\t * @param int $fee_type 必填，现金支付币种，默认1:人民币\n\t * @param string $bank_type 必填，银行通道类型,默认WX\n\t * @param string $input_charset 必填，传入参数字符编码，默认UTF-8，取值有UTF-8和GBK\n\t * @param string $time_start 交易起始时间,订单生成时间,格式yyyyMMddHHmmss\n\t * @param string $time_expire 交易结束时间,也是订单失效时间\n\t * @param int $transport_fee 物流费用,单位为分\n\t * @param int $product_fee 商品费用,单位为分,必须保证 transport_fee + product_fee=total_fee\n\t * @param string $goods_tag 商品标记,优惠券时可能用到\n\t * @param string $attach 附加数据，notify接口原样返回\n\t * @return string\n\t */\n\tpublic function createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type=1,$bank_type=\"WX\",$input_charset=\"UTF-8\",$time_start=\"\",$time_expire=\"\",$transport_fee=\"\",$product_fee=\"\",$goods_tag=\"\",$attach=\"\"){\n\t\t\t$arrdata = array(\"bank_type\" => $bank_type, \"body\" => $body, \"partner\" => $this->partnerid, \"out_trade_no\" => $out_trade_no, \"total_fee\" => $total_fee, \"fee_type\" => $fee_type, \"notify_url\" => $notify_url, \"spbill_create_ip\" => $spbill_create_ip, \"input_charset\" => $input_charset);\n\t\t\tif ($time_start)  $arrdata['time_start'] = $time_start;\n\t\t\tif ($time_expire)  $arrdata['time_expire'] = $time_expire;\n\t\t\tif ($transport_fee)  $arrdata['transport_fee'] = $transport_fee;\n\t\t\tif ($product_fee)  $arrdata['product_fee'] = $product_fee;\n\t\t\tif ($goods_tag)  $arrdata['goods_tag'] = $goods_tag;\n\t\t\tif ($attach)  $arrdata['attach'] = $attach;\n\t\t\tksort($arrdata);\n\t\t\t$paramstring = \"\";\n\t\t\tforeach($arrdata as $key => $value)\n\t\t\t{\n\t\t\tif(strlen($paramstring) == 0)\n\t\t\t\t$paramstring .= $key . \"=\" . $value;\n\t\t\t\telse\n\t\t\t\t$paramstring .= \"&\" . $key . \"=\" . $value;\n\t\t\t}\n\t\t\t$stringSignTemp = $paramstring . \"&key=\" . $this->partnerkey;\n\t\t\t$signValue = strtoupper(md5($stringSignTemp));\n\t\t\t$package = http_build_query($arrdata) . \"&sign=\" . $signValue;\n\t\t\treturn $package;\n\t}\n\n\t/**\n\t * 支付签名(paySign)生成方法\n\t * @param string $package 订单详情字串\n\t * @param string $timeStamp 当前时间戳（需与JS输出的一致）\n\t * @param string $nonceStr 随机串（需与JS输出的一致）\n\t * @return string 返回签名字串\n\t */\n\tpublic function getPaySign($package, $timeStamp, $nonceStr){\n\t\t$arrdata = array(\"appid\" => $this->appid, \"timestamp\" => $timeStamp, \"noncestr\" => $nonceStr, \"package\" => $package, \"appkey\" => $this->paysignkey);\n\t\t$paySign = $this->getSignature($arrdata);\n\t\treturn $paySign;\n\t}\n\n\t/**\n\t * 回调通知签名验证\n\t * @param array $orderxml 返回的orderXml的数组表示，留空则自动从post数据获取\n\t * @return boolean\n\t */\n\tpublic function checkOrderSignature($orderxml=''){\n\t\tif (!$orderxml) {\n\t\t\t$postStr = file_get_contents(\"php://input\");\n\t\t\tif (!empty($postStr)) {\n\t\t\t\t$orderxml = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);\n\t\t\t} else return false;\n\t\t}\n\t\t$arrdata = array('appid'=>$orderxml['AppId'],'appkey'=>$this->paysignkey,'timestamp'=>$orderxml['TimeStamp'],'noncestr'=>$orderxml['NonceStr'],'openid'=>$orderxml['OpenId'],'issubscribe'=>$orderxml['IsSubscribe']);\n\t\t$paySign = $this->getSignature($arrdata);\n\t\tif ($paySign!=$orderxml['AppSignature']) return false;\n\t\treturn true;\n\t}\n\n\t/**\n\t * 发货通知\n\t * @param string $openid 用户open_id\n\t * @param string $transid 交易单号\n\t * @param string $out_trade_no 第三方订单号\n\t * @param int $status 0:发货失败；1:已发货\n\t * @param string $msg 失败原因\n\t * @return boolean|array\n\t */\n\tpublic function sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok'){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$postdata = array(\n\t\t\t\t\"appid\"=>$this->appid,\n\t\t\t\t\"appkey\"=>$this->paysignkey,\n\t\t\t\t\"openid\"=>$openid,\n\t\t\t\t\"transid\"=>strval($transid),\n\t\t\t\t\"out_trade_no\"=>strval($out_trade_no),\n\t\t\t\t\"deliver_timestamp\"=>strval(time()),\n\t\t\t\t\"deliver_status\"=>strval($status),\n\t\t\t\t\"deliver_msg\"=>$msg,\n\t\t);\n\t\t$postdata['app_signature'] = $this->getSignature($postdata);\n\t\t$postdata['sign_method'] = 'sha1';\n\t\tunset($postdata['appkey']);\n\t\t$result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_DELIVERNOTIFY.'access_token='.$this->access_token,self::json_encode($postdata));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 查询订单信息\n\t * @param string $out_trade_no 订单号\n\t * @return boolean|array\n\t */\n\tpublic function getPayOrder($out_trade_no) {\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$sign = strtoupper(md5(\"out_trade_no=$out_trade_no&partner={$this->partnerid}&key={$this->partnerkey}\"));\n\t\t$postdata = array(\n\t\t\t\t\"appid\"=>$this->appid,\n\t\t\t\t\"appkey\"=>$this->paysignkey,\n\t\t\t\t\"package\"=>\"out_trade_no=$out_trade_no&partner={$this->partnerid}&sign=$sign\",\n\t\t\t\t\"timestamp\"=>strval(time()),\n\t\t);\n\t\t$postdata['app_signature'] = $this->getSignature($postdata);\n\t\t$postdata['sign_method'] = 'sha1';\n\t\tunset($postdata['appkey']);\n\t\t$result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_ORDERQUERY.'access_token='.$this->access_token,self::json_encode($postdata));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'].json_encode($postdata);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json[\"order_info\"];\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 设置用户授权密钥\n\t * @param string $user_token\n\t * @return string\n\t */\n\tpublic function setUserToken($user_token) {\n\t\treturn $this->user_token = $user_token;\n\t}\n\n\t/**\n\t * 获取收货地址JS的签名\n\t * @tutorial 参考weixin.js脚本的WeixinJS.editAddress方法调用\n\t * @param string $appId\n\t * @param string $url\n\t * @param int $timeStamp\n\t * @param string $nonceStr\n\t * @param string $user_token\n\t * @return Ambigous <boolean, string>\n\t */\n\tpublic function getAddrSign($url, $timeStamp, $nonceStr, $user_token=''){\n\t\tif (!$user_token) $user_token = $this->user_token;\n\t\tif (!$user_token) {\n\t\t\t$this->errMsg = 'no user access token found!';\n\t\t\treturn false;\n\t\t}\n\t\t$url = htmlspecialchars_decode($url);\n\t\t$arrdata = array(\n\t\t\t\t'appid'=>$this->appid,\n\t\t\t\t'url'=>$url,\n\t\t\t\t'timestamp'=>strval($timeStamp),\n\t\t\t\t'noncestr'=>$nonceStr,\n\t\t\t\t'accesstoken'=>$user_token\n\t\t);\n\t\treturn $this->getSignature($arrdata);\n\t}\n}\n"
  },
  {
    "path": "old_version/snoopy.class.php",
    "content": "<?php\n/*************************************************\n\nSnoopy - the PHP net client\nAuthor: Monte Ohrt <monte@ispi.net>\nCopyright (c): 1999-2008 New Digital Group, all rights reserved\nVersion: 1.2.4\n\n* This library is free software; you can redistribute it and/or\n* modify it under the terms of the GNU Lesser General Public\n* License as published by the Free Software Foundation; either\n* version 2.1 of the License, or (at your option) any later version.\n*\n* This library is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n* Lesser General Public License for more details.\n*\n* You should have received a copy of the GNU Lesser General Public\n* License along with this library; if not, write to the Free Software\n* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\nYou may contact the author of Snoopy by e-mail at:\nmonte@ohrt.com\n\nThe latest version of Snoopy can be obtained from:\nhttp://snoopy.sourceforge.net/\n\n*************************************************/\n\nclass Snoopy\n{\n\t/**** Public variables ****/\n\n\t/* user definable vars */\n\n\tvar $host\t\t\t=\t\"www.php.net\";\t\t// host name we are connecting to\n\tvar $port\t\t\t=\t80;\t\t\t\t\t// port we are connecting to\n\tvar $proxy_host\t\t=\t\"\";\t\t\t\t\t// proxy host to use\n\tvar $proxy_port\t\t=\t\"\";\t\t\t\t\t// proxy port to use\n\tvar $proxy_user\t\t=\t\"\";\t\t\t\t\t// proxy user to use\n\tvar $proxy_pass\t\t=\t\"\";\t\t\t\t\t// proxy password to use\n\n\tvar $agent\t\t\t=\t\"Mozilla/5.0\";\t// agent we masquerade as\n\tvar\t$referer\t\t=\t\"\";\t\t\t\t\t// referer info to pass\n\tvar $cookies\t\t=\tarray();\t\t\t// array of cookies to pass\n\t// $cookies[\"username\"]=\"joe\";\n\tvar\t$rawheaders\t\t=\tarray();\t\t\t// array of raw headers to send\n\t// $rawheaders[\"Content-type\"]=\"text/html\";\n\n\tvar $maxredirs\t\t=\t5;\t\t\t\t\t// http redirection depth maximum. 0 = disallow\n\tvar $lastredirectaddr\t=\t\"\";\t\t\t\t// contains address of last redirected address\n\tvar\t$offsiteok\t\t=\ttrue;\t\t\t\t// allows redirection off-site\n\tvar $maxframes\t\t=\t0;\t\t\t\t\t// frame content depth maximum. 0 = disallow\n\tvar $expandlinks\t=\ttrue;\t\t\t\t// expand links to fully qualified URLs.\n\t// this only applies to fetchlinks()\n\t// submitlinks(), and submittext()\n\tvar $passcookies\t=\ttrue;\t\t\t\t// pass set cookies back through redirects\n\t// NOTE: this currently does not respect\n\t// dates, domains or paths.\n\n\tvar\t$user\t\t\t=\t\"\";\t\t\t\t\t// user for http authentication\n\tvar\t$pass\t\t\t=\t\"\";\t\t\t\t\t// password for http authentication\n\n\t// http accept types\n\tvar $accept\t\t\t=\t\"application/json, text/javascript, */*; q=0.01\";\n\n\tvar $results\t\t=\t\"\";\t\t\t\t\t// where the content is put\n\n\tvar $error\t\t\t=\t\"\";\t\t\t\t\t// error messages sent here\n\tvar\t$response_code\t=\t\"\";\t\t\t\t\t// response code returned from server\n\tvar\t$headers\t\t=\tarray();\t\t\t// headers returned from server sent here\n\tvar\t$maxlength\t\t=\t500000;\t\t\t\t// max return data length (body)\n\tvar $read_timeout\t=\t0;\t\t\t\t\t// timeout on read operations, in seconds\n\t// supported only since PHP 4 Beta 4\n\t// set to 0 to disallow timeouts\n\tvar $timed_out\t\t=\tfalse;\t\t\t\t// if a read operation timed out\n\tvar\t$status\t\t\t=\t0;\t\t\t\t\t// http request status\n\n\tvar $temp_dir\t\t=\t\"/tmp\";\t\t\t\t// temporary directory that the webserver\n\t// has permission to write to.\n\t// under Windows, this should be C:\\temp\n\n\tvar\t$curl_path\t\t=\t\"/usr/local/bin/curl\";\n\t// Snoopy will use cURL for fetching\n\t// SSL content if a full system path to\n\t// the cURL binary is supplied here.\n\t// set to false if you do not have\n\t// cURL installed. See http://curl.haxx.se\n\t// for details on installing cURL.\n\t// Snoopy does *not* use the cURL\n\t// library functions built into php,\n\t// as these functions are not stable\n\t// as of this Snoopy release.\n\n\t/**** Private variables ****/\n\n\tvar\t$_maxlinelen\t=\t4096;\t\t\t\t// max line length (headers)\n\n\tvar $_httpmethod\t=\t\"GET\";\t\t\t\t// default http request method\n\tvar $_httpversion\t=\t\"HTTP/1.0\";\t\t\t// default http request version\n\tvar $_submit_method\t=\t\"POST\";\t\t\t\t// default submit method\n\tvar $_submit_type\t=\t\"application/x-www-form-urlencoded\";\t// default submit type\n\tvar $_mime_boundary\t=   \"\";\t\t\t\t\t// MIME boundary for multipart/form-data submit type\n\tvar $_redirectaddr\t=\tfalse;\t\t\t\t// will be set if page fetched is a redirect\n\tvar $_redirectdepth\t=\t0;\t\t\t\t\t// increments on an http redirect\n\tvar $_frameurls\t\t= \tarray();\t\t\t// frame src urls\n\tvar $_framedepth\t=\t0;\t\t\t\t\t// increments on frame depth\n\n\tvar $_isproxy\t\t=\tfalse;\t\t\t\t// set if using a proxy server\n\tvar $_fp_timeout\t=\t30;\t\t\t\t\t// timeout for socket connection\n\n\t/*======================================================================*\\\n\t Function:\tfetch\n\tPurpose:\tfetch the contents of a web page\n\t(and possibly other protocols in the\n\t\t\tfuture like ftp, nntp, gopher, etc.)\n\tInput:\t\t$URI\tthe location of the page to fetch\n\tOutput:\t\t$this->results\tthe output text from the fetch\n\t\\*======================================================================*/\n\n\tfunction fetch($URI)\n\t{\n\n\t\t//preg_match(\"|^([^:]+)://([^:/]+)(:[\\d]+)*(.*)|\",$URI,$URI_PARTS);\n\t\t$URI_PARTS = parse_url($URI);\n\t\tif (!empty($URI_PARTS[\"user\"]))\n\t\t\t$this->user = $URI_PARTS[\"user\"];\n\t\tif (!empty($URI_PARTS[\"pass\"]))\n\t\t\t$this->pass = $URI_PARTS[\"pass\"];\n\t\tif (empty($URI_PARTS[\"query\"]))\n\t\t\t$URI_PARTS[\"query\"] = '';\n\t\tif (empty($URI_PARTS[\"path\"]))\n\t\t\t$URI_PARTS[\"path\"] = '';\n\n\t\tswitch(strtolower($URI_PARTS[\"scheme\"]))\n\t\t{\n\t\t\tcase \"http\":\n\t\t\t\t$this->host = $URI_PARTS[\"host\"];\n\t\t\t\tif(!empty($URI_PARTS[\"port\"]))\n\t\t\t\t\t$this->port = $URI_PARTS[\"port\"];\n\t\t\t\tif($this->_connect($fp))\n\t\t\t\t{\n\t\t\t\t\tif($this->_isproxy)\n\t\t\t\t\t{\n\t\t\t\t\t\t// using proxy, send entire URI\n\t\t\t\t\t\t$this->_httprequest($URI,$fp,$URI,$this->_httpmethod);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$path = $URI_PARTS[\"path\"].($URI_PARTS[\"query\"] ? \"?\".$URI_PARTS[\"query\"] : \"\");\n\t\t\t\t\t\t// no proxy, send only the path\n\t\t\t\t\t\t$this->_httprequest($path, $fp, $URI, $this->_httpmethod);\n\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t$this->_disconnect($fp);\n\n\t\t\t\t\tif($this->_redirectaddr)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* url was redirected, check if we've hit the max depth */\n\t\t\t\t\t\tif($this->maxredirs > $this->_redirectdepth)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// only follow redirect if it's on this site, or offsiteok is true\n\t\t\t\t\t\t\tif(preg_match(\"|^http://\".preg_quote($this->host).\"|i\",$this->_redirectaddr) || $this->offsiteok)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t/* follow the redirect */\n\t\t\t\t\t\t\t\t$this->_redirectdepth++;\n\t\t\t\t\t\t\t\t$this->lastredirectaddr=$this->_redirectaddr;\n\t\t\t\t\t\t\t\t$this->fetch($this->_redirectaddr);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t$frameurls = $this->_frameurls;\n\t\t\t\t\t\t$this->_frameurls = array();\n\n\t\t\t\t\t\twhile(list(,$frameurl) = each($frameurls))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif($this->_framedepth < $this->maxframes)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t$this->fetch($frameurl);\n\t\t\t\t\t\t\t\t$this->_framedepth++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t\tbreak;\n\t\t\tcase \"https\":\n\t\t\t\tif (!function_exists('curl_init')) {\n\t\t\t\t\tif(!$this->curl_path)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tif(function_exists(\"is_executable\"))\n\t\t\t\t\t\tif (!is_executable($this->curl_path))\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t$this->host = $URI_PARTS[\"host\"];\n\t\t\t\tif(!empty($URI_PARTS[\"port\"]))\n\t\t\t\t\t$this->port = $URI_PARTS[\"port\"];\n\t\t\t\tif($this->_isproxy)\n\t\t\t\t{\n\t\t\t\t\t// using proxy, send entire URI\n\t\t\t\t\t$this->_httpsrequest($URI,$URI,$this->_httpmethod);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$path = $URI_PARTS[\"path\"].($URI_PARTS[\"query\"] ? \"?\".$URI_PARTS[\"query\"] : \"\");\n\t\t\t\t\t// no proxy, send only the path\n\t\t\t\t\t$this->_httpsrequest($path, $URI, $this->_httpmethod);\n\t\t\t\t}\n\n\t\t\t\tif($this->_redirectaddr)\n\t\t\t\t{\n\t\t\t\t\t/* url was redirected, check if we've hit the max depth */\n\t\t\t\t\tif($this->maxredirs > $this->_redirectdepth)\n\t\t\t\t\t{\n\t\t\t\t\t\t// only follow redirect if it's on this site, or offsiteok is true\n\t\t\t\t\t\tif(preg_match(\"|^https://\".preg_quote($this->host).\"|i\",$this->_redirectaddr) || $this->offsiteok)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/* follow the redirect */\n\t\t\t\t\t\t\t$this->_redirectdepth++;\n\t\t\t\t\t\t\t$this->lastredirectaddr=$this->_redirectaddr;\n\t\t\t\t\t\t\t$this->fetch($this->_redirectaddr);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)\n\t\t\t\t{\n\t\t\t\t\t$frameurls = $this->_frameurls;\n\t\t\t\t\t$this->_frameurls = array();\n\n\t\t\t\t\twhile(list(,$frameurl) = each($frameurls))\n\t\t\t\t\t{\n\t\t\t\t\t\tif($this->_framedepth < $this->maxframes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$this->fetch($frameurl);\n\t\t\t\t\t\t\t$this->_framedepth++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// not a valid protocol\n\t\t\t\t$this->error\t=\t'Invalid protocol \"'.$URI_PARTS[\"scheme\"].'\"\\n';\n\t\t\t\treturn false;\n\t\t\t\tbreak;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tsubmit\n\tPurpose:\tsubmit an http form\n\tInput:\t\t$URI\tthe location to post the data\n\t$formvars\tthe formvars to use.\n\tformat: $formvars[\"var\"] = \"val\";\n\t$formfiles  an array of files to submit\n\tformat: $formfiles[\"var\"] = \"/dir/filename.ext\";\n\tOutput:\t\t$this->results\tthe text output from the post\n\t\\*======================================================================*/\n\n\tfunction submit($URI, $formvars=\"\", $formfiles=\"\")\n\t{\n\t\tunset($postdata);\n\n\t\t$postdata = $this->_prepare_post_body($formvars, $formfiles);\n\t\t$URI_PARTS = parse_url($URI);\n\t\tif (!empty($URI_PARTS[\"user\"]))\n\t\t\t$this->user = $URI_PARTS[\"user\"];\n\t\tif (!empty($URI_PARTS[\"pass\"]))\n\t\t\t$this->pass = $URI_PARTS[\"pass\"];\n\t\tif (empty($URI_PARTS[\"query\"]))\n\t\t\t$URI_PARTS[\"query\"] = '';\n\t\tif (empty($URI_PARTS[\"path\"]))\n\t\t\t$URI_PARTS[\"path\"] = '';\n\n\t\tswitch(strtolower($URI_PARTS[\"scheme\"]))\n\t\t{\n\t\t\tcase \"http\":\n\t\t\t\t$this->host = $URI_PARTS[\"host\"];\n\t\t\t\tif(!empty($URI_PARTS[\"port\"]))\n\t\t\t\t\t$this->port = $URI_PARTS[\"port\"];\n\t\t\t\tif($this->_connect($fp))\n\t\t\t\t{\n\t\t\t\t\tif($this->_isproxy)\n\t\t\t\t\t{\n\t\t\t\t\t\t// using proxy, send entire URI\n\t\t\t\t\t\t$this->_httprequest($URI,$fp,$URI,$this->_submit_method,$this->_submit_type,$postdata);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t$path = $URI_PARTS[\"path\"].($URI_PARTS[\"query\"] ? \"?\".$URI_PARTS[\"query\"] : \"\");\n\t\t\t\t\t\t// no proxy, send only the path\n\t\t\t\t\t\t$this->_httprequest($path, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata);\n\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t$this->_disconnect($fp);\n\n\t\t\t\t\tif($this->_redirectaddr)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* url was redirected, check if we've hit the max depth */\n\t\t\t\t\t\tif($this->maxredirs > $this->_redirectdepth)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(!preg_match(\"|^\".$URI_PARTS[\"scheme\"].\"://|\", $this->_redirectaddr))\n\t\t\t\t\t\t\t\t$this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS[\"scheme\"].\"://\".$URI_PARTS[\"host\"]);\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// only follow redirect if it's on this site, or offsiteok is true\n\t\t\t\t\t\t\tif(preg_match(\"|^http://\".preg_quote($this->host).\"|i\",$this->_redirectaddr) || $this->offsiteok)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t/* follow the redirect */\n\t\t\t\t\t\t\t\t$this->_redirectdepth++;\n\t\t\t\t\t\t\t\t$this->lastredirectaddr=$this->_redirectaddr;\n\t\t\t\t\t\t\t\tif( strpos( $this->_redirectaddr, \"?\" ) > 0 )\n\t\t\t\t\t\t\t\t\t$this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t$this->submit($this->_redirectaddr,$formvars, $formfiles);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t$frameurls = $this->_frameurls;\n\t\t\t\t\t\t$this->_frameurls = array();\n\n\t\t\t\t\t\twhile(list(,$frameurl) = each($frameurls))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif($this->_framedepth < $this->maxframes)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t$this->fetch($frameurl);\n\t\t\t\t\t\t\t\t$this->_framedepth++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t\tbreak;\n\t\t\tcase \"https\":\n\t\t\t\tif (!function_exists('curl_init')) {\n\t\t\t\tif(!$this->curl_path)\n\t\t\t\t\treturn false;\n\t\t\t\tif(function_exists(\"is_executable\"))\n\t\t\t\t\tif (!is_executable($this->curl_path))\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t$this->host = $URI_PARTS[\"host\"];\n\t\t\t\tif(!empty($URI_PARTS[\"port\"]))\n\t\t\t\t\t$this->port = $URI_PARTS[\"port\"];\n\t\t\t\tif($this->_isproxy)\n\t\t\t\t{\n\t\t\t\t\t// using proxy, send entire URI\n\t\t\t\t\t$this->_httpsrequest($URI, $URI, $this->_submit_method, $this->_submit_type, $postdata);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$path = $URI_PARTS[\"path\"].($URI_PARTS[\"query\"] ? \"?\".$URI_PARTS[\"query\"] : \"\");\n\t\t\t\t\t// no proxy, send only the path\n\t\t\t\t\t$this->_httpsrequest($path, $URI, $this->_submit_method, $this->_submit_type, $postdata);\n\t\t\t\t}\n\n\t\t\t\tif($this->_redirectaddr)\n\t\t\t\t{\n\t\t\t\t\t/* url was redirected, check if we've hit the max depth */\n\t\t\t\t\tif($this->maxredirs > $this->_redirectdepth)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(!preg_match(\"|^\".$URI_PARTS[\"scheme\"].\"://|\", $this->_redirectaddr))\n\t\t\t\t\t\t\t$this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS[\"scheme\"].\"://\".$URI_PARTS[\"host\"]);\n\n\t\t\t\t\t\t// only follow redirect if it's on this site, or offsiteok is true\n\t\t\t\t\t\tif(preg_match(\"|^https://\".preg_quote($this->host).\"|i\",$this->_redirectaddr) || $this->offsiteok)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/* follow the redirect */\n\t\t\t\t\t\t\t$this->_redirectdepth++;\n\t\t\t\t\t\t\t$this->lastredirectaddr=$this->_redirectaddr;\n\t\t\t\t\t\t\tif( strpos( $this->_redirectaddr, \"?\" ) > 0 )\n\t\t\t\t\t\t\t\t$this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t$this->submit($this->_redirectaddr,$formvars, $formfiles);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)\n\t\t\t\t{\n\t\t\t\t\t$frameurls = $this->_frameurls;\n\t\t\t\t\t$this->_frameurls = array();\n\n\t\t\t\t\twhile(list(,$frameurl) = each($frameurls))\n\t\t\t\t\t{\n\t\t\t\t\t\tif($this->_framedepth < $this->maxframes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$this->fetch($frameurl);\n\t\t\t\t\t\t\t$this->_framedepth++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\t// not a valid protocol\n\t\t\t\t$this->error\t=\t'Invalid protocol \"'.$URI_PARTS[\"scheme\"].'\"\\n';\n\t\t\t\treturn false;\n\t\t\t\tbreak;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tfetchlinks\n\tPurpose:\tfetch the links from a web page\n\tInput:\t\t$URI\twhere you are fetching from\n\tOutput:\t\t$this->results\tan array of the URLs\n\t\\*======================================================================*/\n\n\tfunction fetchlinks($URI)\n\t{\n\t\tif ($this->fetch($URI))\n\t\t{\n\t\t\tif($this->lastredirectaddr)\n\t\t\t\t$URI = $this->lastredirectaddr;\n\t\t\tif(is_array($this->results))\n\t\t\t{\n\t\t\t\tfor($x=0;$x<count($this->results);$x++)\n\t\t\t\t\t$this->results[$x] = $this->_striplinks($this->results[$x]);\n\t\t\t}\n\t\t\telse\n\t\t\t\t$this->results = $this->_striplinks($this->results);\n\n\t\t\tif($this->expandlinks)\n\t\t\t\t$this->results = $this->_expandlinks($this->results, $URI);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tfetchform\n\tPurpose:\tfetch the form elements from a web page\n\tInput:\t\t$URI\twhere you are fetching from\n\tOutput:\t\t$this->results\tthe resulting html form\n\t\\*======================================================================*/\n\n\tfunction fetchform($URI)\n\t{\n\n\t\tif ($this->fetch($URI))\n\t\t{\n\n\t\t\tif(is_array($this->results))\n\t\t\t{\n\t\t\t\tfor($x=0;$x<count($this->results);$x++)\n\t\t\t\t\t$this->results[$x] = $this->_stripform($this->results[$x]);\n\t\t\t}\n\t\t\telse\n\t\t\t\t$this->results = $this->_stripform($this->results);\n\t\t\t\t\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\n\t/*======================================================================*\\\n\t Function:\tfetchtext\n\tPurpose:\tfetch the text from a web page, stripping the links\n\tInput:\t\t$URI\twhere you are fetching from\n\tOutput:\t\t$this->results\tthe text from the web page\n\t\\*======================================================================*/\n\n\tfunction fetchtext($URI)\n\t{\n\t\tif($this->fetch($URI))\n\t\t{\n\t\t\tif(is_array($this->results))\n\t\t\t{\n\t\t\t\tfor($x=0;$x<count($this->results);$x++)\n\t\t\t\t\t$this->results[$x] = $this->_striptext($this->results[$x]);\n\t\t\t}\n\t\t\telse\n\t\t\t\t$this->results = $this->_striptext($this->results);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tsubmitlinks\n\tPurpose:\tgrab links from a form submission\n\tInput:\t\t$URI\twhere you are submitting from\n\tOutput:\t\t$this->results\tan array of the links from the post\n\t\\*======================================================================*/\n\n\tfunction submitlinks($URI, $formvars=\"\", $formfiles=\"\")\n\t{\n\t\tif($this->submit($URI,$formvars, $formfiles))\n\t\t{\n\t\t\tif($this->lastredirectaddr)\n\t\t\t\t$URI = $this->lastredirectaddr;\n\t\t\tif(is_array($this->results))\n\t\t\t{\n\t\t\t\tfor($x=0;$x<count($this->results);$x++)\n\t\t\t\t{\n\t\t\t\t\t$this->results[$x] = $this->_striplinks($this->results[$x]);\n\t\t\t\t\tif($this->expandlinks)\n\t\t\t\t\t\t$this->results[$x] = $this->_expandlinks($this->results[$x],$URI);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->results = $this->_striplinks($this->results);\n\t\t\t\tif($this->expandlinks)\n\t\t\t\t\t$this->results = $this->_expandlinks($this->results,$URI);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tsubmittext\n\tPurpose:\tgrab text from a form submission\n\tInput:\t\t$URI\twhere you are submitting from\n\tOutput:\t\t$this->results\tthe text from the web page\n\t\\*======================================================================*/\n\n\tfunction submittext($URI, $formvars = \"\", $formfiles = \"\")\n\t{\n\t\tif($this->submit($URI,$formvars, $formfiles))\n\t\t{\n\t\t\tif($this->lastredirectaddr)\n\t\t\t\t$URI = $this->lastredirectaddr;\n\t\t\tif(is_array($this->results))\n\t\t\t{\n\t\t\t\tfor($x=0;$x<count($this->results);$x++)\n\t\t\t\t{\n\t\t\t\t\t$this->results[$x] = $this->_striptext($this->results[$x]);\n\t\t\t\t\tif($this->expandlinks)\n\t\t\t\t\t\t$this->results[$x] = $this->_expandlinks($this->results[$x],$URI);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->results = $this->_striptext($this->results);\n\t\t\t\tif($this->expandlinks)\n\t\t\t\t\t$this->results = $this->_expandlinks($this->results,$URI);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\n\n\t/*======================================================================*\\\n\t Function:\tset_submit_multipart\n\tPurpose:\tSet the form submission content type to\n\tmultipart/form-data\n\t\\*======================================================================*/\n\tfunction set_submit_multipart()\n\t{\n\t\t$this->_submit_type = \"multipart/form-data\";\n\t}\n\n\n\t/*======================================================================*\\\n\t Function:\tset_submit_normal\n\tPurpose:\tSet the form submission content type to\n\tapplication/x-www-form-urlencoded\n\t\\*======================================================================*/\n\tfunction set_submit_normal()\n\t{\n\t\t$this->_submit_type = \"application/x-www-form-urlencoded\";\n\t}\n\n\n\n\n\t/*======================================================================*\\\n\t Private functions\n\t\\*======================================================================*/\n\n\n\t/*======================================================================*\\\n\t Function:\t_striplinks\n\tPurpose:\tstrip the hyperlinks from an html document\n\tInput:\t\t$document\tdocument to strip.\n\tOutput:\t\t$match\t\tan array of the links\n\t\\*======================================================================*/\n\n\tfunction _striplinks($document)\n\t{\n\t\tpreg_match_all(\"'<\\s*a\\s.*?href\\s*=\\s*\t\t\t# find <a href=\n\t\t\t\t\t\t([\\\"\\'])?\t\t\t\t\t# find single or double quote\n\t\t\t\t\t\t(?(1) (.*?)\\\\1 | ([^\\s\\>]+))\t\t# if quote found, match up to next matching\n\t\t\t\t\t\t\t\t\t\t\t\t\t# quote, otherwise match up to next space\n\t\t\t\t\t\t'isx\",$document,$links);\n\n\n\t\t// catenate the non-empty matches from the conditional subpattern\n\n\t\twhile(list($key,$val) = each($links[2]))\n\t\t{\n\t\t\tif(!empty($val))\n\t\t\t\t$match[] = $val;\n\t\t}\n\n\t\twhile(list($key,$val) = each($links[3]))\n\t\t{\n\t\t\tif(!empty($val))\n\t\t\t\t$match[] = $val;\n\t\t}\n\n\t\t// return the links\n\t\treturn $match;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\t_stripform\n\tPurpose:\tstrip the form elements from an html document\n\tInput:\t\t$document\tdocument to strip.\n\tOutput:\t\t$match\t\tan array of the links\n\t\\*======================================================================*/\n\n\tfunction _stripform($document)\n\t{\n\t\tpreg_match_all(\"'<\\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\\/?(option|select)[^<>]*>[\\r\\n]*)|(?=[\\r\\n]*))|(?=[\\r\\n]*))'Usi\",$document,$elements);\n\n\t\t// catenate the matches\n\t\t$match = implode(\"\\r\\n\",$elements[0]);\n\n\t\t// return the links\n\t\treturn $match;\n\t}\n\n\n\n\t/*======================================================================*\\\n\t Function:\t_striptext\n\tPurpose:\tstrip the text from an html document\n\tInput:\t\t$document\tdocument to strip.\n\tOutput:\t\t$text\t\tthe resulting text\n\t\\*======================================================================*/\n\n\tfunction _striptext($document)\n\t{\n\n\t\t// I didn't use preg eval (//e) since that is only available in PHP 4.0.\n\t\t// so, list your entities one by one here. I included some of the\n\t\t// more common ones.\n\n\t\t$search = array(\"'<script[^>]*?>.*?</script>'si\",\t// strip out javascript\n\t\t\t\t\"'<[\\/\\!]*?[^<>]*?>'si\",\t\t\t// strip out html tags\n\t\t\t\t\"'([\\r\\n])[\\s]+'\",\t\t\t\t\t// strip out white space\n\t\t\t\t\"'&(quot|#34|#034|#x22);'i\",\t\t// replace html entities\n\t\t\t\t\"'&(amp|#38|#038|#x26);'i\",\t\t\t// added hexadecimal values\n\t\t\t\t\"'&(lt|#60|#060|#x3c);'i\",\n\t\t\t\t\"'&(gt|#62|#062|#x3e);'i\",\n\t\t\t\t\"'&(nbsp|#160|#xa0);'i\",\n\t\t\t\t\"'&(iexcl|#161);'i\",\n\t\t\t\t\"'&(cent|#162);'i\",\n\t\t\t\t\"'&(pound|#163);'i\",\n\t\t\t\t\"'&(copy|#169);'i\",\n\t\t\t\t\"'&(reg|#174);'i\",\n\t\t\t\t\"'&(deg|#176);'i\",\n\t\t\t\t\"'&(#39|#039|#x27);'\",\n\t\t\t\t\"'&(euro|#8364);'i\",\t\t\t\t// europe\n\t\t\t\t\"'&a(uml|UML);'\",\t\t\t\t\t// german\n\t\t\t\t\"'&o(uml|UML);'\",\n\t\t\t\t\"'&u(uml|UML);'\",\n\t\t\t\t\"'&A(uml|UML);'\",\n\t\t\t\t\"'&O(uml|UML);'\",\n\t\t\t\t\"'&U(uml|UML);'\",\n\t\t\t\t\"'&szlig;'i\",\n\t\t);\n\t\t$replace = array(\t\"\",\n\t\t\t\t\"\",\n\t\t\t\t\"\\\\1\",\n\t\t\t\t\"\\\"\",\n\t\t\t\t\"&\",\n\t\t\t\t\"<\",\n\t\t\t\t\">\",\n\t\t\t\t\" \",\n\t\t\t\tchr(161),\n\t\t\t\tchr(162),\n\t\t\t\tchr(163),\n\t\t\t\tchr(169),\n\t\t\t\tchr(174),\n\t\t\t\tchr(176),\n\t\t\t\tchr(39),\n\t\t\t\tchr(128),\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t\t\t\"�\",\n\t\t);\n\t\t\t\n\t\t$text = preg_replace($search,$replace,$document);\n\n\t\treturn $text;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\t_expandlinks\n\tPurpose:\texpand each link into a fully qualified URL\n\tInput:\t\t$links\t\t\tthe links to qualify\n\t$URI\t\t\tthe full URI to get the base from\n\tOutput:\t\t$expandedLinks\tthe expanded links\n\t\\*======================================================================*/\n\n\tfunction _expandlinks($links,$URI)\n\t{\n\n\t\tpreg_match(\"/^[^\\?]+/\",$URI,$match);\n\n\t\t$match = preg_replace(\"|/[^\\/\\.]+\\.[^\\/\\.]+$|\",\"\",$match[0]);\n\t\t$match = preg_replace(\"|/$|\",\"\",$match);\n\t\t$match_part = parse_url($match);\n\t\t$match_root =\n\t\t$match_part[\"scheme\"].\"://\".$match_part[\"host\"];\n\n\t\t$search = array( \t\"|^http://\".preg_quote($this->host).\"|i\",\n\t\t\t\t\"|^(\\/)|i\",\n\t\t\t\t\"|^(?!http://)(?!mailto:)|i\",\n\t\t\t\t\"|/\\./|\",\n\t\t\t\t\"|/[^\\/]+/\\.\\./|\"\n\t\t);\n\n\t\t$replace = array(\t\"\",\n\t\t\t\t$match_root.\"/\",\n\t\t\t\t$match.\"/\",\n\t\t\t\t\"/\",\n\t\t\t\t\"/\"\n\t\t);\n\n\t\t$expandedLinks = preg_replace($search,$replace,$links);\n\n\t\treturn $expandedLinks;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\t_httprequest\n\tPurpose:\tgo get the http data from the server\n\tInput:\t\t$url\t\tthe url to fetch\n\t$fp\t\t\tthe current open file pointer\n\t$URI\t\tthe full URI\n\t$body\t\tbody contents to send if any (POST)\n\tOutput:\n\t\\*======================================================================*/\n\n\tfunction _httprequest($url,$fp,$URI,$http_method,$content_type=\"\",$body=\"\")\n\t{\n\t\t$cookie_headers = '';\n\t\tif($this->passcookies && $this->_redirectaddr)\n\t\t\t$this->setcookies();\n\t\t\t\n\t\t$URI_PARTS = parse_url($URI);\n\t\tif(empty($url))\n\t\t\t$url = \"/\";\n\t\t$headers = $http_method.\" \".$url.\" \".$this->_httpversion.\"\\r\\n\";\n\t\tif(!empty($this->agent))\n\t\t\t$headers .= \"User-Agent: \".$this->agent.\"\\r\\n\";\n\t\tif(!empty($this->host) && !isset($this->rawheaders['Host'])) {\n\t\t\t$headers .= \"Host: \".$this->host;\n\t\t\tif(!empty($this->port) && $this->port!=80)\n\t\t\t\t$headers .= \":\".$this->port;\n\t\t\t$headers .= \"\\r\\n\";\n\t\t}\n\t\tif(!empty($this->accept))\n\t\t\t$headers .= \"Accept: \".$this->accept.\"\\r\\n\";\n\t\tif(!empty($this->referer))\n\t\t\t$headers .= \"Referer: \".$this->referer.\"\\r\\n\";\n\t\tif(!empty($this->cookies))\n\t\t{\n\t\t\tif(!is_array($this->cookies))\n\t\t\t\t$this->cookies = (array)$this->cookies;\n\n\t\t\treset($this->cookies);\n\t\t\tif ( count($this->cookies) > 0 ) {\n\t\t\t\t$cookie_headers .= 'Cookie: ';\n\t\t\t\tforeach ( $this->cookies as $cookieKey => $cookieVal ) {\n\t\t\t\t\t$cookie_headers .= $cookieKey.\"=\".urlencode($cookieVal).\"; \";\n\t\t\t\t}\n\t\t\t\t$headers .= substr($cookie_headers,0,-2) . \"\\r\\n\";\n\t\t\t}\n\t\t}\n\t\tif(!empty($this->rawheaders))\n\t\t{\n\t\t\tif(!is_array($this->rawheaders))\n\t\t\t\t$this->rawheaders = (array)$this->rawheaders;\n\t\t\twhile(list($headerKey,$headerVal) = each($this->rawheaders))\n\t\t\t\t$headers .= $headerKey.\": \".$headerVal.\"\\r\\n\";\n\t\t}\n\t\tif(!empty($content_type)) {\n\t\t\t$headers .= \"Content-type: $content_type\";\n\t\t\tif ($content_type == \"multipart/form-data\")\n\t\t\t\t$headers .= \"; boundary=\".$this->_mime_boundary;\n\t\t\t$headers .= \"\\r\\n\";\n\t\t}\n\t\tif(!empty($body))\n\t\t\t$headers .= \"Content-length: \".strlen($body).\"\\r\\n\";\n\t\tif(!empty($this->user) || !empty($this->pass))\n\t\t\t$headers .= \"Authorization: Basic \".base64_encode($this->user.\":\".$this->pass).\"\\r\\n\";\n\n\t\t//add proxy auth headers\n\t\tif(!empty($this->proxy_user))\n\t\t\t$headers .= 'Proxy-Authorization: ' . 'Basic ' . base64_encode($this->proxy_user . ':' . $this->proxy_pass).\"\\r\\n\";\n\n\n\t\t$headers .= \"\\r\\n\";\n\n\t\t// set the read timeout if needed\n\t\tif ($this->read_timeout > 0)\n\t\t\tsocket_set_timeout($fp, $this->read_timeout);\n\t\t$this->timed_out = false;\n\n\t\tfwrite($fp,$headers.$body,strlen($headers.$body));\n\n\t\t$this->_redirectaddr = false;\n\t\tunset($this->headers);\n\n\t\twhile($currentHeader = fgets($fp,$this->_maxlinelen))\n\t\t{\n\t\t\tif ($this->read_timeout > 0 && $this->_check_timeout($fp))\n\t\t\t{\n\t\t\t\t$this->status=-100;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif($currentHeader == \"\\r\\n\")\n\t\t\t\tbreak;\n\n\t\t\t// if a header begins with Location: or URI:, set the redirect\n\t\t\tif(preg_match(\"/^(Location:|URI:)/i\",$currentHeader))\n\t\t\t{\n\t\t\t\t// get URL portion of the redirect\n\t\t\t\tpreg_match(\"/^(Location:|URI:)[ ]+(.*)/i\",chop($currentHeader),$matches);\n\t\t\t\t// look for :// in the Location header to see if hostname is included\n\t\t\t\tif (!empty($matches)) {\n\t\t\t\t\tif(!preg_match(\"|\\:\\/\\/|\",$matches[2]))\n\t\t\t\t\t{\n\t\t\t\t\t\t// no host in the path, so prepend\n\t\t\t\t\t\t$this->_redirectaddr = $URI_PARTS[\"scheme\"].\"://\".$this->host.\":\".$this->port;\n\t\t\t\t\t\t// eliminate double slash\n\t\t\t\t\t\tif(!preg_match(\"|^/|\",$matches[2]))\n\t\t\t\t\t\t\t$this->_redirectaddr .= \"/\".$matches[2];\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t$this->_redirectaddr .= $matches[2];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\t$this->_redirectaddr = $matches[2];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(preg_match(\"|^HTTP/|\",$currentHeader))\n\t\t\t{\n\t\t\t\tif(preg_match(\"|^HTTP/[^\\s]*\\s(.*?)\\s|\",$currentHeader, $status))\n\t\t\t\t{\n\t\t\t\t\t$this->status= $status[1];\n\t\t\t\t}\n\t\t\t\t$this->response_code = $currentHeader;\n\t\t\t}\n\n\t\t\t$this->headers[] = $currentHeader;\n\t\t}\n\n\t\t$results = '';\n\t\tdo {\n\t\t\t$_data = fread($fp, $this->maxlength);\n\t\t\tif (strlen($_data) == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t$results .= $_data;\n\t\t} while(true);\n\n\t\tif ($this->read_timeout > 0 && $this->_check_timeout($fp))\n\t\t{\n\t\t\t$this->status=-100;\n\t\t\treturn false;\n\t\t}\n\n\t\t// check if there is a a redirect meta tag\n\n\t\tif(preg_match(\"'<meta[\\s]*http-equiv[^>]*?content[\\s]*=[\\s]*[\\\"\\']?\\d+;[\\s]*URL[\\s]*=[\\s]*([^\\\"\\']*?)[\\\"\\']?>'i\",$results,$match))\n\n\t\t{\n\t\t\t$this->_redirectaddr = $this->_expandlinks($match[1],$URI);\n\t\t}\n\n\t\t// have we hit our frame depth and is there frame src to fetch?\n\t\tif(($this->_framedepth < $this->maxframes) && preg_match_all(\"'<frame\\s+.*src[\\s]*=[\\'\\\"]?([^\\'\\\"\\>]+)'i\",$results,$match))\n\t\t{\n\t\t\t$this->results[] = $results;\n\t\t\tfor($x=0; $x<count($match[1]); $x++)\n\t\t\t\t$this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS[\"scheme\"].\"://\".$this->host);\n\t\t}\n\t\t// have we already fetched framed content?\n\t\telseif(is_array($this->results))\n\t\t$this->results[] = $results;\n\t\t// no framed content\n\t\telse\n\t\t\t$this->results = $results;\n\n\t\treturn true;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\t_httpsrequest\n\tPurpose:\tgo get the https data from the server using curl\n\tInput:\t\t$url\t\tthe url to fetch\n\t$URI\t\tthe full URI\n\t$body\t\tbody contents to send if any (POST)\n\tOutput:\n\t\\*======================================================================*/\n\n\tfunction _httpsrequest($url,$URI,$http_method,$content_type=\"\",$body=\"\")\n\t{\n\t\tif($this->passcookies && $this->_redirectaddr)\n\t\t\t$this->setcookies();\n\n\t\t$headers = array();\n\t\t\t\n\t\t$URI_PARTS = parse_url($URI);\n\t\tif(empty($url))\n\t\t\t$url = \"/\";\n\t\t// GET ... header not needed for curl\n\t\t//$headers[] = $http_method.\" \".$url.\" \".$this->_httpversion;\n\t\tif(!empty($this->agent))\n\t\t\t$headers[] = \"User-Agent: \".$this->agent;\n\t\tif(!empty($this->host))\n\t\t\tif(!empty($this->port) && $this->port!=80)\n\t\t\t$headers[] = \"Host: \".$this->host.\":\".$this->port;\n\t\telse\n\t\t\t$headers[] = \"Host: \".$this->host;\n\t\tif(!empty($this->accept))\n\t\t\t$headers[] = \"Accept: \".$this->accept;\n\t\tif(!empty($this->referer))\n\t\t\t$headers[] = \"Referer: \".$this->referer;\n\t\tif(!empty($this->cookies))\n\t\t{\n\t\t\tif(!is_array($this->cookies))\n\t\t\t\t$this->cookies = (array)$this->cookies;\n\n\t\t\treset($this->cookies);\n\t\t\tif ( count($this->cookies) > 0 ) {\n\t\t\t\t$cookie_str = 'Cookie: ';\n\t\t\t\tforeach ( $this->cookies as $cookieKey => $cookieVal ) {\n\t\t\t\t\t$cookie_str .= $cookieKey.\"=\".urlencode($cookieVal).\"; \";\n\t\t\t\t}\n\t\t\t\t$headers[] = substr($cookie_str,0,-2);\n\t\t\t}\n\t\t}\n\t\tif(!empty($this->rawheaders))\n\t\t{\n\t\t\tif(!is_array($this->rawheaders))\n\t\t\t\t$this->rawheaders = (array)$this->rawheaders;\n\t\t\twhile(list($headerKey,$headerVal) = each($this->rawheaders))\n\t\t\t\t$headers[] = $headerKey.\": \".$headerVal;\n\t\t}\n\t\tif(!empty($content_type)) {\n\t\t\tif ($content_type == \"multipart/form-data\")\n\t\t\t\t$headers[] = \"Content-type: $content_type; boundary=\".$this->_mime_boundary;\n\t\t\telse\n\t\t\t\t$headers[] = \"Content-type: $content_type\";\n\t\t}\n\t\tif(!empty($body))\n\t\t\t$headers[] = \"Content-length: \".strlen($body);\n\t\tif(!empty($this->user) || !empty($this->pass))\n\t\t\t$headers[] = \"Authorization: BASIC \".base64_encode($this->user.\":\".$this->pass);\n\t\tif (function_exists('curl_init')) {\n\t\t\t$ch = curl_init();\n\t\t\tcurl_setopt($ch, CURLOPT_URL, $URI);\n\t\t\tcurl_setopt($ch, CURLOPT_HEADER, true); \n\t\t\tcurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);\n\t\t\tcurl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);\n\t\t\tcurl_setopt($ch, CURLOPT_SSLVERSION,3); \n\t\t\tcurl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); \n\t\t\tcurl_setopt($ch, CURLOPT_HTTPHEADER, $headers); \n\t\t\tcurl_setopt($ch, CURLOPT_TIMEOUT, $this->read_timeout);\n\t\t\tif(!empty($body)) {\n\t\t\t\tcurl_setopt($ch, CURLOPT_POST, true);\n\t\t\t\tcurl_setopt($ch, CURLOPT_POSTFIELDS, $body);\n\t\t\t}\n\t\t\t$data = curl_exec($ch);\n\t\t\tif ($data === false) {\n\t\t\t\t$this->error = \"Error: Curl error  \".curl_error($ch);\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$parts = explode(\"\\r\\n\\r\\n\",$data,2);\n\t\t\t$result_headers = explode(\"\\r\\n\",$parts[0]);\n\t\t\t$results = $parts[1];\n\t\t\tunset($parts);\n\t\t} else {\n\t\t\t\tfor($curr_header = 0; $curr_header < count($headers); $curr_header++) {\n\t\t\t\t\t$safer_header = strtr( $headers[$curr_header], \"\\\"\", \" \" );\n\t\t\t\t\t$cmdline_params .= \" -H \\\"\".$safer_header.\"\\\"\";\n\t\t\t\t}\n\t\t\n\t\t\t\tif(!empty($body))\n\t\t\t\t\t$cmdline_params .= \" -d \\\"$body\\\"\";\n\t\t\n\t\t\t\tif($this->read_timeout > 0)\n\t\t\t\t\t$cmdline_params .= \" -m \".$this->read_timeout;\n\t\t\n\t\t\t\t$headerfile = tempnam($temp_dir, \"sno\");\n\t\t\n\t\t\t\texec($this->curl_path.\" -k -D \\\"$headerfile\\\"\".$cmdline_params.\" \\\"\".escapeshellcmd($URI).\"\\\"\",$results,$return);\n\t\t\n\t\t\t\tif($return)\n\t\t\t\t{\n\t\t\t\t\t$this->error = \"Error: cURL could not retrieve the document, error $return.\";\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t\n\t\t\t$results = implode(\"\\r\\n\",$results);\n\t\n\t\t\t$result_headers = file(\"$headerfile\");\n\t\t}\n\t\t$this->_redirectaddr = false;\n\t\tunset($this->headers);\n\n\t\tfor($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++)\n\t\t{\n\t\t\t\t\n\t\t\t// if a header begins with Location: or URI:, set the redirect\n\t\t\tif(preg_match(\"/^(Location: |URI: )/i\",$result_headers[$currentHeader]))\n\t\t\t{\n\t\t\t\t// get URL portion of the redirect\n\t\t\t\tpreg_match(\"/^(Location: |URI:)\\s+(.*)/\",chop($result_headers[$currentHeader]),$matches);\n\t\t\t\t// look for :// in the Location header to see if hostname is included\n\t\t\t\tif (!empty($matches)) {\n\t\t\t\t\tif(!preg_match(\"|\\:\\/\\/|\",$matches[2]))\n\t\t\t\t\t{\n\t\t\t\t\t\t// no host in the path, so prepend\n\t\t\t\t\t\t$this->_redirectaddr = $URI_PARTS[\"scheme\"].\"://\".$this->host;\n\t\t\t\t\t\t// eliminate double slash\n\t\t\t\t\t\tif(!preg_match(\"|^/|\",$matches[2]))\n\t\t\t\t\t\t\t$this->_redirectaddr .= \"/\".$matches[2];\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t$this->_redirectaddr .= $matches[2];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\t$this->_redirectaddr = $matches[2];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(preg_match(\"|^HTTP/|\",$result_headers[$currentHeader]))\n\t\t\t\t$this->response_code = $result_headers[$currentHeader];\n\n\t\t\t$this->headers[] = $result_headers[$currentHeader];\n\t\t}\n\n\t\t// check if there is a a redirect meta tag\n\n\t\tif(preg_match(\"'<meta[\\s]*http-equiv[^>]*?content[\\s]*=[\\s]*[\\\"\\']?\\d+;[\\s]*URL[\\s]*=[\\s]*([^\\\"\\']*?)[\\\"\\']?>'i\",$results,$match))\n\t\t{\n\t\t\t$this->_redirectaddr = $this->_expandlinks($match[1],$URI);\n\t\t}\n\n\t\t// have we hit our frame depth and is there frame src to fetch?\n\t\tif(($this->_framedepth < $this->maxframes) && preg_match_all(\"'<frame\\s+.*src[\\s]*=[\\'\\\"]?([^\\'\\\"\\>]+)'i\",$results,$match))\n\t\t{\n\t\t\t$this->results[] = $results;\n\t\t\tfor($x=0; $x<count($match[1]); $x++)\n\t\t\t\t$this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS[\"scheme\"].\"://\".$this->host);\n\t\t}\n\t\t// have we already fetched framed content?\n\t\telseif(is_array($this->results))\n\t\t\t$this->results[] = $results;\n\t\t// no framed content\n\t\telse\n\t\t\t$this->results = $results;\n\t\tif (isset($headerfile) && file_exists($headerfile))\n\t\t\tunlink($headerfile);\n\n\t\treturn true;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\tsetcookies()\n\tPurpose:\tset cookies for a redirection\n\t\\*======================================================================*/\n\n\tfunction setcookies()\n\t{\n\t\tfor($x=0; $x<count($this->headers); $x++)\n\t\t{\n\t\t\tif(preg_match('/^set-cookie:[\\s]+([^=]+)=([^;]+)/i', $this->headers[$x],$match))\n\t\t\t\t$this->cookies[$match[1]] = urldecode($match[2]);\n\t\t}\n\t}\n\n\n\t/*======================================================================*\\\n\t Function:\t_check_timeout\n\tPurpose:\tchecks whether timeout has occurred\n\tInput:\t\t$fp\tfile pointer\n\t\\*======================================================================*/\n\n\tfunction _check_timeout($fp)\n\t{\n\t\tif ($this->read_timeout > 0) {\n\t\t\t$fp_status = socket_get_status($fp);\n\t\t\tif ($fp_status[\"timed_out\"]) {\n\t\t\t\t$this->timed_out = true;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/*======================================================================*\\\n\t Function:\t_connect\n\tPurpose:\tmake a socket connection\n\tInput:\t\t$fp\tfile pointer\n\t\\*======================================================================*/\n\n\tfunction _connect(&$fp)\n\t{\n\t\tif(!empty($this->proxy_host) && !empty($this->proxy_port))\n\t\t{\n\t\t\t$this->_isproxy = true;\n\n\t\t\t$host = $this->proxy_host;\n\t\t\t$port = $this->proxy_port;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$host = $this->host;\n\t\t\t$port = $this->port;\n\t\t}\n\n\t\t$this->status = 0;\n\n\t\tif($fp = fsockopen(\n\t\t\t\t$host,\n\t\t\t\t$port,\n\t\t\t\t$errno,\n\t\t\t\t$errstr,\n\t\t\t\t$this->_fp_timeout\n\t\t))\n\t\t{\n\t\t\t// socket connection succeeded\n\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// socket connection failed\n\t\t\t$this->status = $errno;\n\t\t\tswitch($errno)\n\t\t\t{\n\t\t\t\tcase -3:\n\t\t\t\t\t$this->error=\"socket creation failed (-3)\";\n\t\t\t\tcase -4:\n\t\t\t\t\t$this->error=\"dns lookup failure (-4)\";\n\t\t\t\tcase -5:\n\t\t\t\t\t$this->error=\"connection refused or timed out (-5)\";\n\t\t\t\tdefault:\n\t\t\t\t\t$this->error=\"connection failed (\".$errno.\")\";\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\t/*======================================================================*\\\n\t Function:\t_disconnect\n\tPurpose:\tdisconnect a socket connection\n\tInput:\t\t$fp\tfile pointer\n\t\\*======================================================================*/\n\n\tfunction _disconnect($fp)\n\t{\n\t\treturn(fclose($fp));\n\t}\n\n\n\t/*======================================================================*\\\n\t Function:\t_prepare_post_body\n\tPurpose:\tPrepare post body according to encoding type\n\tInput:\t\t$formvars  - form variables\n\t$formfiles - form upload files\n\tOutput:\t\tpost body\n\t\\*======================================================================*/\n\n\tfunction _prepare_post_body($formvars, $formfiles)\n\t{\n\t\tsettype($formvars, \"array\");\n\t\tsettype($formfiles, \"array\");\n\t\t$postdata = '';\n\n\t\tif (count($formvars) == 0 && count($formfiles) == 0)\n\t\t\treturn;\n\t\tif (is_string($formvars)) return $formvars;\n\t\tif((count($formvars) == 1) && isset($formvars[0])) return $formvars[0];\n\t\tswitch ($this->_submit_type) {\n\t\t\tcase \"application/x-www-form-urlencoded\":\n\t\t\t\treset($formvars);\n\t\t\t\twhile(list($key,$val) = each($formvars)) {\n\t\t\t\t\tif (is_array($val) || is_object($val)) {\n\t\t\t\t\t\twhile (list($cur_key, $cur_val) = each($val)) {\n\t\t\t\t\t\t\t$postdata .= urlencode($key).\"[]=\".urlencode($cur_val).\"&\";\n\t\t\t\t\t\t}\n\t\t\t\t\t} else\n\t\t\t\t\t\t$postdata .= urlencode($key).\"=\".urlencode($val).\"&\";\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"multipart/form-data\":\n\t\t\t\t$this->_mime_boundary = \"--------\".md5(uniqid(microtime()));\n\n\t\t\t\treset($formvars);\n\t\t\t\twhile(list($key,$val) = each($formvars)) {\n\t\t\t\t\tif (is_array($val) || is_object($val)) {\n\t\t\t\t\t\twhile (list($cur_key, $cur_val) = each($val)) {\n\t\t\t\t\t\t\t$postdata .= \"--\".$this->_mime_boundary.\"\\r\\n\";\n\t\t\t\t\t\t\t$postdata .= \"Content-Disposition: form-data; name=\\\"$key\\[\\]\\\"\\r\\n\\r\\n\";\n\t\t\t\t\t\t\t$postdata .= \"$cur_val\\r\\n\";\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$postdata .= \"--\".$this->_mime_boundary.\"\\r\\n\";\n\t\t\t\t\t\t$postdata .= \"Content-Disposition: form-data; name=\\\"$key\\\"\\r\\n\\r\\n\";\n\t\t\t\t\t\t$postdata .= \"$val\\r\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treset($formfiles);\n\t\t\t\twhile (list($field_name, $file_names) = each($formfiles)) {\n\t\t\t\t\tsettype($file_names, \"array\");\n\t\t\t\t\twhile (list(, $file_name) = each($file_names)) {\n\t\t\t\t\t\t$file_content = file_get_contents($file_name);\n\t\t\t\t\t\tif (!$file_content) continue;\n\n\t\t\t\t\t\t$base_name = basename($file_name);\n\n\t\t\t\t\t\t$postdata .= \"--\".$this->_mime_boundary.\"\\r\\n\";\n\t\t\t\t\t\t$postdata .= \"Content-Disposition: form-data; name=\\\"$field_name\\\"; filename=\\\"$base_name\\\"\\r\\nContent-Type: image/jpeg\\r\\n\\r\\n\";\n\t\t\t\t\t\t$postdata .= \"$file_content\\r\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t$postdata .= \"--\".$this->_mime_boundary.\"--\\r\\n\";\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn $postdata;\n\t}\n}\n"
  },
  {
    "path": "old_version/test/test2.php",
    "content": "<?php\n/**\n * 微信扩展接口测试\n */\n\tinclude(\"../wechatext.class.php\");\n\t\n\tfunction logdebug($text){\n\t\tfile_put_contents('../data/log.txt',$text.\"\\n\",FILE_APPEND);\t\t\n\t};\n\t\n\t$options = array(\n\t\t'account'=>'demo@domain.com',\n\t\t'password'=>'demo',\n\t\t'datapath'=>'../data/cookie_',\n\t\t\t'debug'=>true,\n\t\t\t'logcallback'=>'logdebug'\t\n\t); \n\t$wechat = new Wechatext($options);\n\tif ($wechat->checkValid()) {\n\t\t//获取分组列表\n\t\t$grouplist = $wechat->getGroupList();\n\t\tvar_dump($grouplist);\n\t\t//获取用户列表\n\t\t$userlist = $wechat->getUserlist(0,10);\n\t\tvar_dump($userlist);\n\t\t$user = $userlist[0];\n\t\t// 获取用户信息\n\t\t$userdata = $wechat->getInfo($user['id']);\n\t\tvar_dump($userdata);\n\t\t// 获取已保存的图文消息\n\t\t$newslist = $wechat->getNewsList(0,10);\n\t\tvar_dump($newslist);\n\t\t//获取用户最新消息\n\t\t$topmsg = $wechat->getTopMsg();\n\t\tvar_dump($topmsg);\n\t\t$msglist = $wechat->getMsg();\n\t\tvar_dump($msglist);\n\t\t// 主动回复消息\n\t\tif ($topmsg && $topmsg['has_reply']==0){\n\t\t    $wechat->send($user['id'],'hi '.$topmsg['nick_name'].',rev:'.$topmsg['content']);\n\t\t    $content = '这是一条Wechatext发出的测试微信';\n\t\t    $imgdata = file_get_contents('http://github.global.ssl.fastly.net/images/modules/dashboard/bootcamp/octocat_fork.png');\n\t\t    $img = '../data/send.png';\n\t\t    file_put_contents($img,$imgdata);\n\t\t    //上传图片\n\t\t    $fileid = $wechat->uploadFile($img);\n\t\t    echo 'fileid:'.$fileid;\n\t\t    //if ($fileid) $re = $wechat->sendImage($user['id'],$fileid);\n\t\t    //发送图文信息\n\t\t    $re = $wechat->sendPreview($userdata['user_name'],$content,$content,$content,$fileid,'http://github.com/dodgepudding/wechat-php-sdk');\n\t\t    var_dump($re);\n\t\t    //发送视频\n\t\t    //$re = $wechat->sendVideo($user['id'],$fileid);\n\t\t\t$re = $wechat->getFileList(2,0,10);\n\t\t\tvar_dump($re);\n\t\t} else {\n\t\t\techo 'no top msg';\n\t\t}\t\n\t} else {\n\t\techo \"login error\";\n\t}"
  },
  {
    "path": "old_version/test/test3.php",
    "content": "<?php\n/**\n * 微信二维码登陆测试\n */\n\tinclude(\"../wechatauth.class.php\");\n\tsession_start();\n\tfunction logdebug($text){\n\t\tfile_put_contents('../log/logwechat.txt',$text.\"\\n\",FILE_APPEND);\t\t\n\t};\n\t$sid  = session_id();\n\t$options = array(\n\t\t'account'=>$sid,\n\t\t'datapath'=>'../log/cookiecode_',\n\t\t\t'debug'=>true,\n\t\t\t'logcallback'=>'logdebug'\t\n\t); \n\t$wechat = new Wechatauth($options);\n\t\n\tif (isset($_POST['code'])) {\n\t\t$logincode = $_POST['code'];\n\t\t$vres = $wechat->set_login_code($logincode)->verify_code();\n\t\tif ($vres===false) {\n\t\t\t$result = array('status'=>0);\n\t\t} else {\n\t\t\t$result = array('status'=>$vres);\n\t\t\tif ($vres==200) {\n\t\t\t\t$result['info'] = $wechat->get_login_info();\n\t\t\t\t$result['cookie'] = $wechat->get_login_cookie(true);\n\t\t\t}\n\t\t}\n\t\t\n\t\tdie(json_encode($result));\t\n\t}\n\t$logincode =  $wechat->get_login_code();\n\t$qrimg = $wechat->get_code_image();\n\t\n?>\n\n<!DOCTYPE html>\n<html>\n<head>\n<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<script type=\"text/javascript\" src=\"http://code.jquery.com/jquery-1.9.1.min.js\"></script>\n<title>微信二维码登陆接口</title>\n</head>\n<body>\n<h2>微信里扫描以下二维码实现登陆</h2>\n<div id=\"content\">\n<img src=\"<?php echo $qrimg;?>\" />\n</div>\n<script type=\"text/javascript\">\nvar ajaxlock = false;\nvar ajaxhandle;\nfunction synclogin(){\n\tif (!ajaxlock) {\n\t\tajaxlock = true;\n\t\t$.post(location.href,{code:'<?php echo $logincode;?>'},function(json){\n\t\t\t//console.log(json);\n\t\t\tif (json.status) {\n\t\t\t\tconsole.log(json.status);\n\t\t\t\tif (json.status==200) {\n\t\t\t\t\tvar nick,uid,username,sex,avatar;\n\t\t\t\t\tif (json.info && json.info.User){\n\t\t\t\t\t\tuid = json.info.User.Uin;\n\t\t\t\t\t\tnick = json.info.User.NickName;\n\t\t\t\t\t\tusername = json.info.User.UserName;\n\t\t\t\t\t\tsex = json.info.User.Sex;\n\t\t\t\t\t\tavatar = json.info.User.HeadImgUrl;\n\t\t\t\t\t\t$('#content').html('<h2>用户信息</h2><ul><li><b>Uid:</b>'+uid\n\t\t\t\t\t\t\t\t+'</li><li><b>Nick:</b>'+nick\n\t\t\t\t\t\t\t\t+'</li><li><b>username:</b>'+username\n\t\t\t\t\t\t\t\t+'</li><li><b>sex:</b>'+(sex==1?'男':'女')\n\t\t\t\t\t\t\t\t+'</li><li><b>avatar:</b>'+avatar\n\t\t\t\t\t\t\t\t+'</li></ul>');\n\t\t\t\t\t}\n\t\t\t\t\talert('login success, welcome '+nick);\n\n\t\t\t\t\tclearInterval(ajaxhandle);\n\t\t\t\t}\n\t\t\t}\n\t\t\tajaxlock = false;\n\t\t},'json');\n\t}\n}\n$(function(){\n\tajaxhandle = setInterval(\"synclogin()\",5000);\n});\n</script>\n</body>"
  },
  {
    "path": "old_version/test/weshare.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<script src=\"../wechat.js\" type=\"text/javascript\"></script>\n<script>\nvar dataForWeixin={\n\t   appId:\"\",\n\t   MsgImg:\"https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon_res_download_wxlogo.png\",\n\t   TLImg:\"https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon_res_download_wxlogo.png\",\n\t   url:\"https://raw.githubusercontent.com/dodgepudding/wechat-php-sdk/master/test/weshare.html\",\n\t   title:\"微信分享测试文件\",\n\t   desc:\"这里演示了微信分享前调和回调方法\",\n\t   fakeid:\"\",\n\t   prepare:function(e){\n\t\t   var log = '';\n\t\t\tfor (var i in e) {\n\t\t\t\tlog+= i+':'+e[i]+';';\n\t\t\t}\n\t\t\talert(log);\n\t   },\n\t   callback:function(e){\n\t\t   var log = '';\n\t\t\tfor (var i in e) {\n\t\t\t\tlog+= i+':'+e[i]+';';\n\t\t\t}\n\t\t\talert(log);\n\t   }\n\t};\nWeixinJS.hideToolbar();\nWeixinJS.getNetworkType(\n\tfunction(e){\n\t\talert(e);\n\t});\n</script>\n<title>weshare</title>\n</head>\n\n<body>\n<h3>这里演示了微信分享前调和回调方法，点击微信右上角分享相关的功能，即可看到各类返回的alert信息</h3>\n</body>\n</html>\n"
  },
  {
    "path": "old_version/wechat.js",
    "content": "/**\n * 微信网页端调用JS(官方于微信6.0.2版本发布新版JSAPI接口，此接口文件废弃)\n * @author dodge\n * @contact dodgepudding@gmail.com\n * @link http://blog.4wer.com/wechat-timeline-share\n * @version 1.1\n * \n * 自定义分享使用：\n * WeixinJS.hideOptionMenu() 隐藏右上角按钮\n * WeixinJS.showOptionMenu() 显示右上角按钮\n * WeixinJS.hideToolbar() 隐藏工具栏\n * WeixinJS.showToolbar() 显示工具栏\n * WeixinJS.getNetworkType() 获取网络状态\n * WeixinJS.closeWindow() 关闭窗口\n * WeixinJS.scanQRCode() 扫描二维码\n * WeixinJS.openUrlByExtBrowser(url) 使用浏览器打开网址\n * WeixinJS.jumpToBizProfile(username) 跳转到指定公众账号页面\n * WeixinJS.sendEmail(title,content) 发送邮件\n * WeixinJS.openProductView(latitude,longitude,name,address,scale,infoUrl) 查看地图\n * WeixinJS.addContact(username) 添加微信账号\n * WeixinJS.imagePreview(urls,current) 调出微信内图片预览\n * WeixinJS.payCallback(appId,package,timeStamp,nonceStr,signType,paySign,callback) 微信JsApi支付接口\n * WeixinJS.editAddress(appId,addrSign,timeStamp,nonceStr,callback) 微信JsApi支付接口\n * 自定义分享内容数据格式：\n * var dataForWeixin={\n\t   appId:\"\",\n\t   MsgImg:\"消息图片路径\",\n\t   TLImg:\"时间线图路径\",\n\t   url:\"分享url路径\",\n\t   title:\"标题\",\n\t   desc:\"描述\",\n\t   fakeid:\"\",\n\t   prepare:function(argv){\n\t   if (typeof argv.shareTo!='undefined') \n\t   \tswitch(argv.shareTo) {\n\t   \t\tcase 'friend':\n\t   \t\t//发送给朋友\n\t   \t\talert(argv.scene); //friend\n\t   \t\tbreak;\n\t   \t\tcase 'timeline':\n\t   \t\t//发送给朋友\n\t   \t\tbreak;\n\t   \t\tcase 'weibo':\n\t   \t\t//发送到微博\n\t   \t\talert(argv.url);\n\t   \t\tbreak;\n\t   \t\tcase 'favorite':\n\t   \t\t//收藏\n\t   \t\talert(argv.scene);//favorite\n\t   \t\tbreak;\n\t   \t\tcase 'connector':\n\t   \t\t//分享到第三方应用\n\t   \t\talert(argv.scene);//connector\n\t   \t\tbreak;\n\t   \t\tdefault：\n\t   \t}\n\t   },\n\t   callback:function(res){\n\t   \t//发送给好友或应用\n\t   \tif (res.err_msg=='send_app_msg:confirm') {\n\t   \t\t//todo:func1();\n\t   \t\talert(res.err_desc);\n\t   \t}\n\t   \tif (res.err_msg=='send_app_msg:cancel') {\n\t   \t\t//todo:func2();\n\t   \t\talert(res.err_desc);\n\t   \t}\n\t   \t//分享到朋友圈\n\t   \tif (res.err_msg=='share_timeline:ok') {\n\t   \t\t//todo:func1();\n\t   \t\talert(res.err_desc);\n\t   \t}\n\t   \tif (res.err_msg=='share_timeline:cancel') {\n\t   \t\t//todo:func1();\n\t   \t\talert(res.err_desc);\n\t   \t}\n\t   \t//分享到微博\n\t   \tif (res.err_msg=='share_weibo:confirm') {\n\t   \t\t//todo:func1();\n\t   \t\talert(res.err_desc);\n\t   \t}\n\t   \tif (res.err_msg=='share_weibo:cancel') {\n\t   \t\t//todo:func1();\n\t   \t\talert(res.err_desc);\n\t   \t}\n\t   \t//收藏或分享到应用\n\t   \tif (res.err_msg=='send_app_msg:ok') {\n\t   \t\t//todo:func1();\n\t   \t\talert(res.err_desc);\n\t   \t}   \t\n\t   }\n\t};\n */\n\nWeixinJS = typeof WeixinJS!='undefined' || {};\n//隐藏右上角按钮\nWeixinJS.hideOptionMenu = function() {\n\tdocument.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {\n\t\tif (typeof WeixinJSBridge!='undefined')\tWeixinJSBridge.call('hideOptionMenu');\n\t});\n};\n//显示右上角按钮\nWeixinJS.showOptionMenu = function() {\n\tdocument.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {\n\t\tif (typeof WeixinJSBridge!='undefined')\tWeixinJSBridge.call('showOptionMenu');\n\t});\n};\n//隐藏底部导航栏\nWeixinJS.hideToolbar = function() {\n\tdocument.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {\n\t\tif (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('hideToolbar');\n\t});\n};\n//显示底部导航栏\nWeixinJS.showToolbar = function() {\n\tdocument.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {\n\t\tif (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('showToolbar');\n\t});\n};\n//网页获取用户网络状态\nnetType={\"network_type:wifi\":\"wifi网络\",\"network_type:edge\":\"非wifi,包含3G/2G\",\"network_type:fail\":\"网络断开连接\",\"network_type:wwan\":\"2g或者3g\"};\nWeixinJS.getNetworkType = function(callback) {\n\tdocument.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {\n\t\tif (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke('getNetworkType',{},\n\t\tfunction(res){\n\t\t\t//result: network_type:wifi,network_type:edge,network_type:fail,network_type:wwan\n\t\t\t//netType[e.err_msg]\n\t\t\tcallback(res.err_msg);\n\t    });\n\t});\n};\n//关闭窗口\nWeixinJS.closeWindow = function() {\n\tif (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke(\"closeWindow\", {});\n};\n//扫描二维码\nWeixinJS.scanQRCode = function() {\n\tif (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke(\"scanQRCode\", {});\n};\n//使用浏览器打开网址\nWeixinJS.openUrlByExtBrowser=function(url){\n\tif (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke(\"openUrlByExtBrowser\",{\"url\" : url});\n};\n//跳转到指定公众账号页面\nWeixinJS.jumpToBizProfile=function(username){\n\tif (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke(\"jumpToBizProfile\",{\"tousername\" : username});\n};\n//发送邮件\nWeixinJS.sendEmail=function(title,content){\n\tif (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke(\"sendEmail\",{\n\t    \"title\" : title,\n\t    \"content\" : content\n\t});\n};\n//查看地图\nWeixinJS.openProductView=function(latitude,longitude,name,address,scale,infoUrl){\n\tif (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke(\"openProductView\",{\n\t    \"latitude\" : latitude, //纬度\n\t    \"longitude\" : longitude, //经度\n\t    \"name\" : name, //名称\n\t    \"address\" : address, //地址\n\t    \"scale\" : scale, //地图缩放级别\n\t    \"infoUrl\" : infoUrl, //查看位置界面底部的超链接            \n\t});\n};\n//添加微信账号\nWeixinJS.addContact=function weixinAddContact(username){\n    if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke(\"addContact\", {\n    \t\"webtype\": \"1\",\n    \t\"username\": username\n    }, function(e) {\n\t    WeixinJSBridge.log(e.err_msg);\n\t    //e.err_msg:add_contact:added 已经添加\n\t    //e.err_msg:add_contact:cancel 取消添加\n\t    //e.err_msg:add_contact:ok 添加成功\n\t    if(e.err_msg == 'add_contact:added' || e.err_msg == 'add_contact:ok'){\n\t            //关注成功，或者已经关注过\n\t    }\n    });\n};\n\n/**\n * 调出微信内图片预览scrollview\n * @param array urls 图片url数组\n * @param string current 当前图片url\n */\nWeixinJS.imagePreview = function(urls,current) {\n\tif (typeof WeixinJSBridge!='undefined') \n\t\tWeixinJSBridge.invoke(\"imagePreview\", {\n\t\t\tcurrent: current,\n\t\t\turls: urls\n\t\t});\n};\n\n//微信JsApi支付接口\nWeixinJS.payCallback = function(appId,package,timeStamp,nonceStr,signType,paySign,callback){\n\tif (typeof WeixinJSBridge!='undefined')\n\tWeixinJSBridge.invoke('getBrandWCPayRequest',{\n        \"appId\" : appId.toString(),\n        \"timeStamp\" : timeStamp.toString(),\n        \"nonceStr\" : nonceStr.toString(),\n        \"package\" : package.toString(),\n        \"signType\" : signType.toString(),\n        \"paySign\" : paySign.toString()\n        \n    },function(res){\n    \t// res.err_msg == \"get_brand_wcpay_request:ok\" return true;\n    \t// res.err_msg == \"get_brand_wcpay_request:cancel\" return false;\n    \tcallback(res);\n    });\n};\n//编辑收货地址\nWeixinJS.editAddress = function(appId,addrSign,timeStamp,nonceStr,callback){\n\tvar postdata = {\n\t\t\t\"appId\" : appId.toString(),\n            \"scope\" : \"jsapi_address\",\n            \"signType\" : \"sha1\",\n            \"addrSign\" : addrSign.toString(),\n            \"timeStamp\" : timeStamp.toString(),\n            \"nonceStr\" : nonceStr.toString()\n\t};\n\tif (typeof WeixinJSBridge!='undefined')\n\tWeixinJSBridge.invoke('editAddress',postdata, function(res){\n\t//return res.proviceFirstStageName,res.addressCitySecondStageName,res.addressCountiesThirdStageName,res.addressDetailInfo,res.userName,res.addressPostalCode,res.telNumber\n\t//error return res.err_msg\n\tcallback(res);\n\t});\n};\n\n(function(){\n   if (typeof dataForWeixin==\"undefined\") return;\n   var onBridgeReady=function(){\n   WeixinJSBridge.on('menu:share:appmessage', function(argv){\n\t  (dataForWeixin.prepare)(argv);\n      WeixinJSBridge.invoke('sendAppMessage',{\n         \"appid\":dataForWeixin.appId,\n         \"img_url\":dataForWeixin.MsgImg,\n         \"img_width\":\"120\",\n         \"img_height\":\"120\",\n         \"link\":dataForWeixin.url,\n         \"desc\":dataForWeixin.desc,\n         \"title\":dataForWeixin.title\n      }, function(res){(dataForWeixin.callback)(res);});\n   });\n   WeixinJSBridge.on('menu:share:timeline', function(argv){\n\t  (dataForWeixin.prepare)(argv);\n      WeixinJSBridge.invoke('shareTimeline',{\n         \"img_url\":dataForWeixin.TLImg,\n         \"img_width\":\"120\",\n         \"img_height\":\"120\",\n         \"link\":dataForWeixin.url,\n         \"desc\":dataForWeixin.desc,\n         \"title\":dataForWeixin.title\n      }, function(res){(dataForWeixin.callback)(res);});\n   });\n   WeixinJSBridge.on('menu:share:weibo', function(argv){\n\t  (dataForWeixin.prepare)(argv);\n      WeixinJSBridge.invoke('shareWeibo',{\n         \"content\":dataForWeixin.title,\n         \"url\":dataForWeixin.url\n      }, function(res){(dataForWeixin.callback)(res);});\n   });\n   WeixinJSBridge.on('menu:share:facebook', function(argv){\n\t  (dataForWeixin.prepare)(argv);\n      WeixinJSBridge.invoke('shareFB',{\n         \"img_url\":dataForWeixin.TLImg,\n         \"img_width\":\"120\",\n         \"img_height\":\"120\",\n         \"link\":dataForWeixin.url,\n         \"desc\":dataForWeixin.desc,\n         \"title\":dataForWeixin.title\n      }, function(res){(dataForWeixin.callback)(res);});\n   });\n};\nif(document.addEventListener){\n   document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);\n}else if(document.attachEvent){\n   document.attachEvent('WeixinJSBridgeReady'   , onBridgeReady);\n   document.attachEvent('onWeixinJSBridgeReady' , onBridgeReady);\n}\n})();\n"
  },
  {
    "path": "old_version/wechatauth.class.php",
    "content": "<?php\n/**\n *\t微信公众平台PHP-SDK\n *  Wechatauth为非官方微信登陆API\n *  用户通过扫描网页提供的二维码实现登陆信息获取\n *  主要实现如下功能:\n *  get_login_code() 获取登陆授权码, 通过授权码才能获取二维码\n *  get_code_image($code='') 将上面获取的授权码转换为图片二维码\n *  verify_code() 鉴定是否登陆成功,返回200为最终授权成功.\n *  get_login_info() 鉴定成功后调用此方法即可获取用户基本信息\n *  get_avatar($url) 获取用户头像图片数据\n *  @author dodge <dodgepudding@gmail.com>\n *  @link https://github.com/dodgepudding/wechat-php-sdk\n *  @version 1.1\n *  \n */\ninclude \"snoopy.class.php\";\nclass Wechatauth\n{\n\tprivate $cookie;\n\tprivate $skey;\n\tprivate $_cookiename;\n\tprivate $_cookieexpired = 3600;\n\tprivate $_account = 'test';\n\tprivate $_datapath = './data/cookie_';\n\tprivate $debug;\n\tprivate $_logcallback;\n\tpublic $login_user; //当前登陆用户, 调用get_login_info后获取\n\t\n\tpublic function __construct($options)\n\t{\n\t\t$this->_account = isset($options['account'])?$options['account']:'';\n\t\t$this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath;\n\t\t$this->debug = isset($options['debug'])?$options['debug']:false;\n\t\t$this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;\n\t\t$this->_cookiename = $this->_datapath.$this->_account;\n\t\t$this->getCookie($this->_cookiename);\n\t}\n\t/**\n\t * 把cookie写入缓存\n\t * @param  string $filename 缓存文件名\n\t * @param  string $content  文件内容\n\t * @return bool\n\t */\n\tpublic function saveCookie($filename,$content){\n\t\treturn file_put_contents($filename,$content);\n\t}\n\n\t/**\n\t * 读取cookie缓存内容\n\t * @param  string $filename 缓存文件名\n\t * @return string cookie\n\t */\n\tpublic function getCookie($filename){\n\t\tif (file_exists($filename)) {\n\t\t\t$mtime = filemtime($filename);\n\t\t\tif ($mtime<time()-$this->_cookieexpired) return false;\n\t\t\t$data = file_get_contents($filename);\n\t\t\tif ($data) $this->cookie = $data;\n\t\t} \n\t\treturn $this->cookie;\n\t}\n\t\n\t/*\n\t * 删除cookie\n\t */\n\tpublic function deleteCookie($filename) {\n\t\t$this->cookie = '';\n\t\t@unlink($filename);\n\t\treturn true;\n\t}\n\t\n\tprivate function log($log){\n\t\tif ($this->debug && function_exists($this->_logcallback)) {\n\t\t\tif (is_array($log)) $log = print_r($log,true);\n\t\t\treturn call_user_func($this->_logcallback,$log);\n\t\t}\n\t}\n\t\n\t/**\n\t * 获取登陆二维码对应的授权码\n\t */\n\tpublic function get_login_code(){\n\t\tif ($this->_logincode) return $this->_logincode;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$codeurl = 'https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_='.$t;\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->fetch($codeurl);\n\t\t$result = $send_snoopy->results;\n\t\tif ($result) {\n\t\t\tpreg_match(\"/window.QRLogin.uuid\\s+=\\s+\\\"([^\\\"]+)\\\"/\",$result,$matches);\n\t\t\tif(count($matches)>1) {\n\t\t\t\t$this->_logincode = $matches[1];\n\t\t\t\t$_SESSION['login_step'] = 0;\n\t\t\t\treturn $this->_logincode;\n\t\t\t}\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * 通过授权码获取对应的二维码图片地址\n\t * @param string $code\n\t * @return string image url\n\t */\n\tpublic function get_code_image($code=''){\n\t\tif ($code=='') $code = $this->_logincode;\n\t\tif (!$code) return false;\n\t\treturn 'http://login.weixin.qq.com/qrcode/'.$this->_logincode.'?t=webwx';\n\t}\n\t\n\t/**\n\t * 设置二维码对应的授权码\n\t * @param string $code\n\t * @return class $this\n\t */\n\tpublic  function set_login_code($code) {\n\t\t$this->_logincode = $code;\n\t\treturn $this;\n\t}\n\t\n\t/**\n\t * 二维码登陆验证\n\t *\n\t * @return status:\n\t * >=400: invaild code; 408: not auth and wait, 400,401: not valid or expired\n\t * 201: just scaned but not confirm\n\t * 200: confirm then you can get user info\n\t */\n\tpublic function verify_code() {\n\t\tif (!$this->_logincode) return false;\n\t\t$t = time().strval(mt_rand(100,999));\n\n\t\t\t$url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid='.$this->_logincode.'&tip=0&_='.$t;\n\t\t\t$send_snoopy = new Snoopy; \n\t\t\t$send_snoopy->referer = \"https://wx.qq.com/\";\n\t\t\t$send_snoopy->fetch($url);\n\t\t\t$result = $send_snoopy->results;\n\t\t\t$this->log('step1:'.$result);\n\t\t\tif ($result) {\n\t\t\t\tpreg_match(\"/window\\.code=(\\d+)/\",$result,$matches);\n\t\t\t\tif(count($matches)>1) {\n\t\t\t\t\t$status = intval($matches[1]);\n\t\t\t\t\tif ($status==201) $_SESSION['login_step'] = 1;\n\t\t\t\t\tif ($status==200) {\n\t\t\t\t\t\tpreg_match(\"/ticket=([0-9a-z-_]+)&lang=zh_CN&scan=(\\d+)/\",$result,$matches);\n\t\t\t\t\t\tpreg_match(\"/window.redirect_uri=\\\"([^\\\"]+)\\\"/\",$result,$matcheurl);\n\t\t\t\t\t\t$this->log('step2:'.print_r($matches,true));\n\t\t\t\t\t\tif (count($matcheurl)>1) {\n\t\t\t\t\t\t\t$ticket = $matches[1];\n\t\t\t\t\t\t\t$scan = $matches[2];\n\t\t\t\t\t\t\t//$loginurl = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket='.$ticket.'&lang=zh_CN&scan='.$scan.'&fun=new';\n\t\t\t\t\t\t\t$loginurl = str_replace(\"wx.qq.com\", \"wx2.qq.com\", $matcheurl[1]).'&fun=old';\n\t\t\t\t\t\t\t$urlpart = parse_url($loginurl);\n\t\t\t\t\t\t\t$send_snoopy = new Snoopy; \n\t\t\t\t\t\t\t$send_snoopy->referer = \"https://{$urlpart['host']}/cgi-bin/mmwebwx-bin/webwxindex?t=chat\";\n\t\t\t\t\t\t\t$send_snoopy->fetch($loginurl);\n\t\t\t\t\t\t\t$result = $send_snoopy->results;\n\t\t\t\t\t\t\t$xml = simplexml_load_string($result);\n\t\t\t\t\t\t\tif ($xml->ret==\"0\") $this->skey = $xml->skey;\n\t\t\t\t\t\t\tforeach ($send_snoopy->headers as $key => $value) {\n\t\t\t\t\t\t\t\t$value = trim($value);\n\t\t\t\t\t\t\t\tif(strpos($value,'Set-Cookie: ') !== false){\n\t\t\t\t\t\t\t\t\t$tmp = str_replace(\"Set-Cookie: \",\"\",$value);\n\t\t\t\t\t\t\t\t\t$tmparray = explode(';', $tmp);\n\t\t\t\t\t\t\t\t\t$item = trim($tmparray[0]);\n\t\t\t\t\t\t\t\t\t$cookie.=$item.';';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$cookie .=\"Domain=.qq.com;\";\n\t\t\t\t\t\t\t$this->cookie = $cookie;\n\t\t\t\t\t\t\t$this->log('step3:'.$loginurl.';cookie:'.$cookie.';respond:'.$result);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t$this->saveCookie($this->_cookiename,$this->cookie);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn $status;\n\t\t\t\t}\n\t\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取登陆的cookie\n\t *\n\t * @param bool $is_array 是否以数值方式返回，默认否，返回字符串\n\t * @return string|array\n\t */\n\tpublic function get_login_cookie($is_array = false){\n\t\tif (!$is_array)\treturn $this->cookie;\n\t\t$c_arr = explode(';',$this->cookie);\n\t\t$cookie = array();\n\t\tforeach($c_arr as $item) {\n\t\t\t$kitem = explode('=',trim($item));\n\t\t\tif (count($kitem)>1) {\n\t\t\t\t$key = trim($kitem[0]);\n\t\t\t\t$val = trim($kitem[1]);\n\t\t\t\tif (!empty($val)) $cookie[$key] = $val;\n\t\t\t}\n\t\t}\n\t\treturn $cookie;\n\t}\n\t\n\t/**\n\t * \t 授权登陆后获取用户登陆信息\n\t */\n\tpublic function get_login_info(){\n\t\tif (!$this->cookie) return false;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$send_snoopy = new Snoopy; \n\t\t$submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r='.$t.'&skey='.urlencode($this->skey);\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://wx2.qq.com/\";\n\t\t$citems = $this->get_login_cookie(true);\n\t\t$post = array(\n\t\t\t\"BaseRequest\"=>array(\n\t\t\t\tarray(\n\t\t\t\t\t\"Uin\"=>$citems['wxuin'],\n\t\t\t\t\t\"Sid\"=>$citems['wxsid'],\n\t\t\t\t\t\"Skey\"=>$this->skey,\n\t\t\t\t\t\"DeviceID\"=>''\n\t\t\t\t)\n\t\t\t)\n\t\t);\n\t\t$send_snoopy->submit($submit,json_encode($post));\n\t\t$this->log('login_info:'.$send_snoopy->results);\n\t\t$result = json_decode($send_snoopy->results,true);\n\t\tif ($result['BaseResponse']['Ret']<0) return false;\n\t\t$this->_login_user = $result['User'];\n\t\treturn $result;\n\t}\n\t\n\t/**\n\t *  获取头像\n\t *  @param string $url 传入从用户信息接口获取到的头像地址\n\t */\n\tpublic function get_avatar($url) {\n\t\tif (!$this->cookie) return false;\n\t\tif (strpos($url, 'http')===false) {\n\t\t\t$url = 'http://wx2.qq.com'.$url;\n\t\t}\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://wx2.qq.com/\";\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\t\tif ($result) \n\t\t\treturn $result;\n\t\telse\n\t\t\treturn false;\n\t}\n\t\n\t/**\n\t * 登出当前登陆用户\n\t */\n\tpublic function logout(){\n\t\tif (!$this->cookie) return false;\n\t\tpreg_match(\"/wxuin=(\\w+);/\",$this->cookie,$matches);\n\t\tif (count($matches)>1) $uid = $matches[1];\n\t\tpreg_match(\"/wxsid=(\\w+);/\",$this->cookie,$matches);\n\t\tif (count($matches)>1) $sid = $matches[1];\n\t\t$this->log('logout: uid='.$uid.';sid='.$sid);\n\t\t$send_snoopy = new Snoopy; \n\t\t$submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1';\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://wx2.qq.com/\";\n\t\t$send_snoopy->submit($submit,array('uin'=>$uid,'sid'=>$sid));\n\t\t$this->deleteCookie($this->_cookiename);\n\t\treturn true;\n\t}\n}"
  },
  {
    "path": "old_version/wechatext.class.php",
    "content": "<?php\n/**\n *\t微信公众平台PHP-SDK\n *  Wechatext为非官方微信发送API\n *  注: 用户id为通过getMsg()方法获取的FakeId值\n *  主要实现如下功能:\n *  send($id,$content) 向某用户id发送微信文字信息\n *  getUserList($page,$pagesize,$groupid) 获取用户信息\n *  getGroupList($page,$pagesize) 获取群组信息\n *  sendNews($id,$msgid) 发送图文消息\n *  getNewsList($page,$pagesize) 获取图文信息列表\n *  uploadFile($filepath,$type) 上传附件,包括图片/音频/视频\n *  addPreview($title,$author,$summary,$content,$photoid,$srcurl='')   创建新的图文信息 \n *  getFileList($type,$page,$pagesize) 获取素材库文件列表\n *  sendImage($id,$fid) 发送图片消息\n *  sendAudio($id,$fid) 发送音频消息\n *  sendVideo($id,$fid) 发送视频消息\n *  getInfo($id) 根据id获取用户资料\n *  getNewMsgNum($lastid) 获取从$lastid算起新消息的数目\n *  getTopMsg() 获取最新一条消息的数据, 此方法获取的消息id可以作为检测新消息的$lastid依据\n *  getMsg($lastid,$offset=0,$perpage=50,$day=0,$today=0,$star=0) 获取最新的消息列表, 列表将返回消息id, 用户id, 消息类型, 文字消息等参数\n *  消息返回结构:  {\"id\":\"消息id\",\"type\":\"类型号(1为文字,2为图片,3为语音)\",\"fileId\":\"0\",\"hasReply\":\"0\",\"fakeId\":\"用户uid\",\"nickName\":\"昵称\",\"dateTime\":\"时间戳\",\"content\":\"文字内容\"} \n *  getMsgImage($msgid,$mode='large') 若消息type类型为2, 调用此方法获取图片数据\n *  getMsgVoice($msgid) 若消息type类型为3, 调用此方法获取语音数据\n *  quickSetInterface($url, $token) 快速设置接口信息\n *  getCommonInfo($dir) 获取公众账号基本信息, 其中包含：nickname,avatar,type,qrcode,appid,appsecret\n *  @author dodge <dodgepudding@gmail.com>\n *  @link https://github.com/dodgepudding/wechat-php-sdk\n *  @version 1.2\n *  \n */\n\ninclude \"snoopy.class.php\";\nclass Wechatext\n{\n\tprivate $cookie;\n\tprivate $_cookiename;\n\tprivate $_cookieexpired = 3600;\n\tprivate $_account;\n\tprivate $_password;\n\tprivate $_datapath = './data/cookie_';\n\tprivate $debug;\n\tprivate $_logcallback;\n\tprivate $_token;\n\t\n\tpublic function __construct($options)\n\t{\n\t\t$this->_account = isset($options['account'])?$options['account']:'';\n\t\t$this->_password = isset($options['password'])?$options['password']:'';\n\t\t$this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath;\n\t\t$this->debug = isset($options['debug'])?$options['debug']:false;\n\t\t$this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;\n\t\t$this->_cookiename = $this->_datapath.$this->_account;\n\t\t$this->cookie = $this->getCookie($this->_cookiename);\n\t}\n\n\t/**\n\t * 主动发消息\n\t * @param  string $id      用户的uid(即FakeId)\n\t * @param  string $content 发送的内容\n\t */\n\tpublic function send($id,$content)\n\t{\n\t\t$send_snoopy = new Snoopy; \n\t\t$post = array();\n\t\t$post['tofakeid'] = $id;\n\t\t$post['type'] = 1;\n\t\t$post['token'] = $this->_token;\n\t\t$post['content'] = $content;\n\t\t$post['ajax'] = 1;\n        $send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=$id&token={$this->_token}&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response\";\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\treturn $send_snoopy->results;\n\t}\n\t\n\t/**\n\t * 群发功能 纯文本\n\t * @param string $content\n\t * @return string\n\t */\n\tpublic function mass($content) {\n\t\t$send_snoopy = new Snoopy;\n\t\t$post = array();\n\t\t$post['type'] = 1;\n\t\t$post['token'] = $this->_token;\n\t\t$post['content'] = $content;\n\t\t$post['ajax'] = 1;\n\t\t$post['city']='';\n\t\t$post['country']='';\n\t\t$post['f']='json';\n\t\t$post['groupid']='-1';\n\t\t$post['imgcode']='';\n\t\t$post['lang']='zh_CN';\n\t\t$post['province']='';\n\t\t$post['random']=  rand(0, 1);\n\t\t$post['sex']=0;\n\t\t$post['synctxnews']=0;\n\t\t$post['synctxweibo']=0;\n\t\t$post['t']='ajax-response';\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/masssend\";\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\treturn $send_snoopy->results;\n\t}\n\t\n\t/**\n\t * 群发功能 图文素材\n\t * @param int $appmsgid 图文素材ID\n\t * @return string\n\t */\n\tfunction massNews($appmsgid){\n\t\t$send_snoopy = new Snoopy;\n\t\t$post = array();\n\t\t$post['type'] = 10;\n\t\t$post['token'] = $this->_token;\n\t\t$post['appmsgid'] = $appmsgid;\n\t\t$post['ajax'] = 1;\n\t\t$post['city']='';\n\t\t$post['country']='';\n\t\t$post['f']='json';\n\t\t$post['groupid']='-1';\n\t\t$post['imgcode']='';\n\t\t$post['lang']='zh_CN';\n\t\t$post['province']='';\n\t\t$post['random']=  rand(0, 1);\n\t\t$post['sex']=0;\n\t\t$post['synctxnews']=0;\n\t\t$post['synctxweibo']=0;\n\t\t$post['t']='ajax-response';\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token={$this->_token}&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/masssend\";\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\treturn $send_snoopy->results;\n\t}\n\t\n\t/**\n\t * 获取用户列表列表\n\t * @param $page 页码(从0开始)\n\t * @param $pagesize 每页大小\n\t * @param $groupid 分组id\n\t * @return array ({contacts:[{id:12345667,nick_name:\"昵称\",remark_name:\"备注名\",group_id:0},{}....]})\n\t */\n\tfunction getUserList($page=0,$pagesize=10,$groupid=0){\n\t\t$send_snoopy = new Snoopy;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=\".$pagesize.\"&pageidx=\".$page.\"&type=0&groupid=0&lang=zh_CN&token=\".$this->_token;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=\".$pagesize.\"&pageidx=\".$page.\"&type=0&groupid=$groupid&lang=zh_CN&f=json&token=\".$this->_token;\n\t\t$send_snoopy->fetch($submit);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('userlist:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['contact_list'])) {\n\t\t\t$json = json_decode($json['contact_list'],true);\n\t\t\tif (isset($json['contacts']))\n\t\t\t\treturn $json['contacts'];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取分组列表\n\t * \n\t */\n\tfunction getGroupList(){\n\t\t$send_snoopy = new Snoopy;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&lang=zh_CN&token=\".$this->_token;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&lang=zh_CN&f=json&token=\".$this->_token;\n\t\t$send_snoopy->fetch($submit);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('userlist:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['group_list'])){\n\t\t\t$json = json_decode($json['group_list'],true);\n\t\t\tif (isset($json['groups']))\n\t\t\t\treturn $json['groups'];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取图文信息列表\n\t * @param $page 页码(从0开始)\n\t * @param $pagesize 每页大小\n\t * @return array\n\t */\n\tpublic function getNewsList($page,$pagesize=10) {\n\t\t$send_snoopy = new Snoopy;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$type=10;\n\t\t$begin = $page*$pagesize;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=\".$this->_token.\"&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/appmsg?token=\".$this->_token.\"&lang=zh_CN&type=$type&action=list&begin=$begin&count=$pagesize&f=json&random=0.\".$t;\n\t\t$send_snoopy->fetch($submit);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('newslist:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['app_msg_info'])) {\n\t\t\treturn $json['app_msg_info'];\n\t\t} \n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取与指定用户的对话内容\n\t * @param  $fakeid\n\t * @return  array\n\t */\n\tpublic function getDialogMsg($fakeid) {\n\t\t$send_snoopy = new Snoopy;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=\".$this->_token.\"&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=\".$fakeid.\"&token=\".$this->_token.\"&lang=zh_CN&f=json&random=\".$t;\n\t\t$send_snoopy->fetch($submit);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('DialogMsg:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['page_info'])) {\n\t\t\treturn $json['page_info'];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 发送图文信息,必须从图文库里选取消息ID发送\n\t * @param  string $id      用户的uid(即FakeId)\n\t * @param  string $msgid 图文消息id\n\t */\n\tpublic function sendNews($id,$msgid)\n\t{\n\t\t$send_snoopy = new Snoopy; \n\t\t$post = array();\n\t\t$post['tofakeid'] = $id;\n\t\t$post['type'] = 10;\n\t\t$post['token'] = $this->_token;\n\t\t$post['fid'] = $msgid;\n\t\t$post['appmsgid'] = $msgid;\n\t\t$post['error'] = 'false';\n\t\t$post['ajax'] = 1;\n        $send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response\";\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\treturn $send_snoopy->results;\n\t}\n\t\n\t/**\n\t * 上传附件(图片/音频/视频)\n\t * @param string $filepath 本地文件地址\n\t * @param int $type 文件类型: 2:图片 3:音频 4:视频\n\t */\n\tpublic function uploadFile($filepath,$type=2) {\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->referer = \"http://mp.weixin.qq.com/cgi-bin/indexpage?t=wxm-upload&lang=zh_CN&type=2&formId=1\";\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$post = array('formId'=>'');\n\t\t$postfile = array('uploadfile'=>$filepath);\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->set_submit_multipart();\n\t\t$submit = \"http://mp.weixin.qq.com/cgi-bin/uploadmaterial?cgi=uploadmaterial&type=$type&token=\".$this->_token.\"&t=iframe-uploadfile&lang=zh_CN&formId=\tfile_from_\".$t;\n\t\t$send_snoopy->submit($submit,$post,$postfile);\n\t\t$tmp = $send_snoopy->results;\n\t\t$this->log('upload:'.$tmp);\n\t\tpreg_match(\"/formId,.*?\\'(\\d+)\\'/\",$tmp,$matches);\n\t\tif (isset($matches[1])) {\n\t\t\treturn $matches[1];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 创建图文消息\n\t * @param array $title 标题\n\t * @param array $summary 摘要\n\t * @param array $content 内容\n\t * @param array $photoid 素材库里的图片id(可通过uploadFile上传后获取)\n\t * @param array $srcurl 原文链接\n\t * @return json\n\t */\n\tpublic function addPreview($title,$author,$summary,$content,$photoid,$srcurl='') {\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&token='.$this->_token;\n\t\t\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/operate_appmsg?lang=zh_CN&t=ajax-response&sub=create&token=\".$this->_token;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t\n\t\t$send_snoopy->set_submit_normal();\n\t\t$post = array(\n\t\t\t\t'token'=>$this->_token,\n\t\t\t\t'type'=>10,\n\t\t\t\t'lang'=>'zh_CN',\n\t\t\t\t'sub'=>'create',\n\t\t\t\t'ajax'=>1,\n\t\t\t\t'AppMsgId'=>'',\t\t\t\t\n\t\t\t\t'error'=>'false',\n\t\t);\n\t\tif (count($title)==count($author)&&count($title)==count($summary)&&count($title)==count($content)&&count($title)==count($photoid))\n\t\t{\n\t\t\t$i = 0;\n\t\t\tforeach($title as $v) {\n\t\t\t\t$post['title'.$i] = $title[$i];\n\t\t\t\t$post['author'.$i] = $author[$i];\n\t\t\t\t$post['digest'.$i] = $summary[$i];\n\t\t\t\t$post['content'.$i] = $content[$i];\n\t\t\t\t$post['fileid'.$i] = $photoid[$i];\n\t\t\t\tif ($srcurl[$i]) $post['sourceurl'.$i] = $srcurl[$i];\n\t\t\t\t\n\t\t\t\t$i++;\n\t\t\t\t}\n\t\t}\n\t\t$post['count'] = $i;\n\t\t$post['token'] = $this->_token;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$tmp = $send_snoopy->results;\n\t\t$this->log('step2:'.$tmp);\n\t\t$json = json_decode($tmp,true);\n\t\treturn $json;\n\t}\n\t\n\t/**\n\t * 发送媒体文件\n\t * @param $id 用户的uid(即FakeId)\n\t * @param $fid 文件id\n\t * @param $type 文件类型\n\t */\n\tpublic function sendFile($id,$fid,$type) {\n\t\t$send_snoopy = new Snoopy; \n\t\t$post = array();\n\t\t$post['tofakeid'] = $id;\n\t\t$post['type'] = $type;\n\t\t$post['token'] = $this->_token;\n\t\t$post['fid'] = $fid;\n\t\t$post['fileid'] = $fid;\n\t\t$post['error'] = 'false';\n\t\t$post['ajax'] = 1;\n        $send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid={$id}&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response\";\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('sendfile:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif ($json && $json['ret']==0) \n\t\t\treturn true;\n\t\telse\n\t\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取素材库文件列表\n\t * @param $type 文件类型: 2:图片 3:音频 4:视频\n\t * @param $page 页码(从0开始)\n\t * @param $pagesize 每页大小\n\t * @return array\n\t */\n\tpublic function getFileList($type,$page,$pagesize=10) {\n\t\t$send_snoopy = new Snoopy;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$begin = $page*$pagesize;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/masssendpage?t=mass/send&token=\".$this->_token.\"&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/filepage?token=\".$this->_token.\"&lang=zh_CN&type=$type&random=0.\".$t.\"&begin=$begin&count=$pagesize&f=json\";\n\t\t$send_snoopy->fetch($submit);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('filelist:'.$result);\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['page_info']))\n\t\t\treturn $json['page_info'];\n\t\telse\n\t\t\treturn false;\n\t}\n\t\n\t/**\n\t * 发送图文信息,必须从库里选取文件ID发送\n\t * @param  string $id      用户的uid(即FakeId)\n\t * @param  string $fid 文件id\n\t */\n\tpublic function sendImage($id,$fid)\n\t{\n\t\treturn $this->sendFile($id,$fid,2);\n\t}\n\t\n\t/**\n\t * 发送语音信息,必须从库里选取文件ID发送\n\t * @param  string $id      用户的uid(即FakeId)\n\t * @param  string $fid 语音文件id\n\t */\n\tpublic function sendAudio($id,$fid)\n\t{\n\t\treturn $this->sendFile($id,$fid,3);\n\t}\n\t\n\t/**\n\t * 发送视频信息,必须从库里选取文件ID发送\n\t * @param  string $id      用户的uid(即FakeId)\n\t * @param  string $fid 视频文件id\n\t */\n\tpublic function sendVideo($id,$fid)\n\t{\n\t\treturn $this->sendFile($id,$fid,4);\n\t}\n\t\n\t/**\n\t * 发送预览图文消息\n\t * @param string $account 账户名称(user_name)\n\t * @param string $title 标题\n\t * @param string $summary 摘要\n\t * @param string $content 内容\n\t * @param string $photoid 素材库里的图片id(可通过uploadFile上传后获取)\n\t * @param string $srcurl 原文链接\n\t * @return json\n\t */\n\tpublic function sendPreview($account,$title,$summary,$content,$photoid,$srcurl='') {\n\t\t$send_snoopy = new Snoopy;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=preview&t=ajax-appmsg-preview\";\n\t\t$send_snoopy->set_submit_normal();\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = 'https://mp.weixin.qq.com/cgi-bin/operate_appmsg?sub=edit&t=wxm-appmsgs-edit-new&type=10&subtype=3&lang=zh_CN';\n\t\t$post = array(\n\t\t\t\t'AppMsgId'=>'',\n\t\t\t\t'ajax'=>1,\n\t\t\t\t'content0'=>$content,\n\t\t\t\t'count'=>1,\n\t\t\t\t'digest0'=>$summary,\n\t\t\t\t'error'=>'false',\n\t\t\t\t'fileid0'=>$photoid,\n\t\t\t\t'preusername'=>$account,\n\t\t\t\t'sourceurl0'=>$srcurl,\n\t\t\t\t'title0'=>$title,\n\t\t);\n\t\t$post['token'] = $this->_token;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$tmp = $send_snoopy->results;\n\t\t$this->log('sendpreview:'.$tmp);\n\t\t$json = json_decode($tmp,true);\n\t\treturn $json;\n\t}\n\t\n\t/**\n\t * 获取用户的信息\n\t * @param  string $id 用户的uid(即FakeId)\n\t * @return array  {fake_id:100001,nick_name:'昵称',user_name:'用户名',signature:'签名档',country:'中国',province:'广东',city:'广州',gender:'1',group_id:'0'},groups:{[id:0,name:'未分组',cnt:20]}\n\t */\n\tpublic function getInfo($id)\n\t{\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$t = time().strval(mt_rand(100,999));\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/getcontactinfo\";\n\t\t$post = array('ajax'=>1,'lang'=>'zh_CN','random'=>'0.'.$t,'token'=>$this->_token,'t'=>'ajax-getcontactinfo','fakeid'=>$id);\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\t$result = json_decode($send_snoopy->results,true);\n\t\tif(isset($result['contact_info'])){\n\t\t\treturn $result['contact_info'];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获得头像数据\n\t *\n\t * @param FakeId $fakeid\n\t * @return JPG二进制数据\n\t */\n\tpublic function getHeadImg($fakeid){\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com/misc/getheadimg?fakeid=$fakeid&token=\".$this->_token.\"&lang=zh_CN\";\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('Head image:'.$fakeid.'; length:'.strlen($result));\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * 获取消息更新数目\n\t * @param int $lastid 最近获取的消息ID,为0时获取总消息数目\n\t * @return int 数目\n\t */\n\tpublic function getNewMsgNum($lastid=0){\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/getnewmsgnum?t=ajax-getmsgnum&lastmsgid=\".$lastid;\n\t\t$post = array('ajax'=>1,'token'=>$this->_token);\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$this->log($send_snoopy->results);\n\t\t$result = json_decode($send_snoopy->results,1);\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn intval($result['newTotalMsgCount']);\n\t}\n\t\n\t/**\n\t * 获取最新一条消息\n\t * @return array {\"id\":\"最新一条id\",\"type\":\"类型号(1为文字,2为图片,3为语音)\",\"fileId\":\"0\",\"hasReply\":\"0\",\"fakeId\":\"用户uid\",\"nickName\":\"昵称\",\"dateTime\":\"时间戳\",\"content\":\"文字内容\",\"playLength\":\"0\",\"length\":\"0\",\"source\":\"\",\"starred\":\"0\",\"status\":\"4\"}        \n\t */\n\tpublic function getTopMsg(){\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=20&day=7&lang=zh_CN&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&count=20&day=7&lang=zh_CN&token=\".$this->_token;\n\t\t$send_snoopy->fetch($submit);\n\t\t$this->log($send_snoopy->results);\n\t\t$result = $send_snoopy->results;\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['msg_items'])) {\n\t\t\t$json = json_decode($json['msg_items'],true);\n\t\t\tif(isset($json['msg_item']))\n\t\t\t\treturn array_shift($json['msg_item']);\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取新消息\n\t * @param $lastid 传入最后的消息id编号,为0则从最新一条起倒序获取\n\t * @param $offset lastid起算第一条的偏移量\n\t * @param $perpage 每页获取多少条\n\t * @param $day 最近几天消息(0:今天,1:昨天,2:前天,3:更早,7:五天内)\n\t * @param $today 是否只显示今天的消息, 与$day参数不能同时大于0\n\t * @param $star 是否星标组信息\n\t * @return array[] 同getTopMsg()返回的字段结构相同\n\t */\n\tpublic function getMsg($lastid=0,$offset=0,$perpage=20,$day=7,$today=0,$star=0){\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/message?t=message/list&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$lastid = $lastid===0 ? '':$lastid;\n\t\t$addstar = $star?'&action=star':'';\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/message?t=message/list&f=json&lang=zh_CN{$addstar}&count=$perpage&timeline=$today&day=$day&frommsgid=$lastid&offset=$offset&token=\".$this->_token;\n\t\t$send_snoopy->fetch($submit);\n\t\t$this->log($send_snoopy->results);\n\t\t$result = $send_snoopy->results;\n\t\t$json = json_decode($result,true);\n\t\tif (isset($json['msg_items'])) {\n\t\t\t$json = json_decode($json['msg_items'],true);\n\t\t\tif(isset($json['msg_item']))\n\t\t\t\treturn $json['msg_item'];\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t/**\n\t * 获取图片消息\n\t * @param int $msgid 消息id\n\t * @param string $mode 图片尺寸(large/small)\n\t * @return jpg二进制文件\n\t */\n\tpublic function getMsgImage($msgid,$mode='large'){\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com/cgi-bin/getimgdata?token=\".$this->_token.\"&msgid=$msgid&mode=$mode&source=&fileId=0\";\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('msg image:'.$msgid.';length:'.strlen($result));\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn $result;\n\t}\n\t\n\t/**\n\t * 获取语音消息\n\t * @param int $msgid 消息id\n\t * @return mp3二进制文件\n\t */\n\tpublic function getMsgVoice($msgid){\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/getmessage?t=wxm-message&lang=zh_CN&count=50&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com/cgi-bin/getvoicedata?token=\".$this->_token.\"&msgid=$msgid&fileId=0\";\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log('msg voice:'.$msgid.';length:'.strlen($result));\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn $result;\n\t}\n\n\t/**\n\t * 开启开发者模式\n\t */\n\tpublic function openDevModel()\n\t{\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN\";\n\t\t$post['flag']=1;\n        $post['type']=2;   \n        $post['token']=$this->_token;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log($send_snoopy->results);\n\t\t$json = json_decode($result,true);\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * 关闭编辑模式\n\t */\n\tpublic function closeEditModel()\n\t{\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform&lang=zh_CN\";\n\t\t$post['flag']=0;\n        $post['type']=1;   \n        $post['token']=$this->_token;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log($send_snoopy->results);\n\t\t$json = json_decode($result,true);\n\t\tif(!$result){\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * 配置接口信息\n\t * @param  string $url      接口回调URL\n\t * @param  string $token    接口Token\n\t */\n\tpublic function setUrlToken($url, $token)\n\t{\n\t\t$send_snoopy = new Snoopy;\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/advanced/advanced?action=interface&t=advanced/interface&lang=zh_CN&token=\".$this->_token;\n\t\t$submit = \"https://mp.weixin.qq.com/advanced/callbackprofile?t=ajax-response&lang=zh_CN&token=\".$this->_token;\n\t\t$post['url'] = $url;\n\t\t$post['callback_token'] = $token;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$result = $send_snoopy->results;\n\t\t$this->log($send_snoopy->results);\n\t\t$json = json_decode($result,true);\n\t\tif ($json && $json['ret']==0) \n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\t/**\n\t * 快速设置接口\n\t * @param  string $url      接口回调URL\n\t * @param  string $token    接口Token\n\t */\n\tpublic function quickSetInterface($url, $token)\n\t{\n\t\tif ($this->closeEditModel() && $this->openDevModel() && $this->setUrlToken($url, $token))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取公众账号基本信息\n\t * @param  [string] $dir [指定相对于网站根目录的下载路径，因为需要下载二维码和用户头像]\n\t * @return [array]       [公众账号信息，其中包含：nickname,avatar,type,qrcode,appid,appsecret]\n\t */\n\tpublic function getCommonInfo($dir)\n\t{\n\t\t$userInfo = array();\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=20&day=7&lang=zh_CN&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=\".$this->_token;\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\t\t// 分析首页内容，获取nickname,avatar,usertype\n\t\tpreg_match_all('/class=\\\"nickname\\\">(.*)<\\/a>/', $result, $matches1);\n        preg_match_all('/<img src=\\\"(.*)\\\" class=\\\"avatar\\\"/', $result, $matches2);\n        preg_match_all('/<label for=\\\"\\\" class=\\\"type icon_service_label\\\">(.*)<\\/label>/', $result, $matches3);\n        $userInfo[\"nickname\"] = $nickname = $matches1[1][0];\n        if(strpos($nickname, '<') !== false)\n        {\n        \t$userInfo[\"nickname\"] = $nickname = substr($nickname, 0, strpos($nickname, '<'));\n        }\n        $userInfo[\"avatar\"] = $avatar = $matches2[1][0];\n        if( ! empty($matches3[1][0]))\n        {\n            $userInfo[\"type\"] = $usertype = $matches3[1][0];\n        }\n        else\n        {\n            $userInfo[\"type\"] = $usertype = \"订阅号\";\n        }\n\t\t$this->log('Analysis account info:'. \"\\nNickname:\". $nickname. \"\\nAvatar:\". $avatar. \"\\nUsertype:\". $usertype);\n\t\t// 分析设置页面，获取二维码\n\t\t$send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=20&day=7&lang=zh_CN&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/index&action=index&lang=zh_CN&token=\".$this->_token;\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\t\t// $this->log(\"QRCODE contents:\". $result);\n\t\tpreg_match_all('/<img src=\\\"(.*)\\\" width=\\\"150\\\"/', $result, $matches4);\n        $userInfo[\"qrcode\"] = $qrcode = $matches4[1][0];\n        // downloads the avatar\n        $send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/index&action=index&lang=zh_CN&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com\". $avatar;\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n        $userInfo[\"avatar\"] = $this->downloadImage($result, $dir. DIRECTORY_SEPARATOR. 'avatars');\n        // downloads the qrcode\n        $send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/index&action=index&lang=zh_CN&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com\". $qrcode;\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n        $userInfo[\"qrcode\"] = $this->downloadImage($result, $dir. DIRECTORY_SEPARATOR. 'qrcodes');\n        // 获取appid和appsecret\n        $send_snoopy = new Snoopy; \n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->referer = \"https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/index&action=index&lang=zh_CN&token=\".$this->_token;\n\t\t$url = \"https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&lang=zh_CN&token=\".$this->_token;\n\t\t$send_snoopy->fetch($url);\n\t\t$result = $send_snoopy->results;\n\n\t\tpreg_match_all('/name:\\\"AppId\\\",value:\\\"(.*)\\\"/', $result, $matches_id);\n\t\tpreg_match_all('/name:\\\"AppSecret\\\",value:\\\"(.*)\\\"/', $result, $matches_secret);\n\t\t\n        $userInfo[\"appid\"] = $AppId = $matches_id[1][0];\n        $userInfo[\"appsecret\"] = $AppSecret = $matches_secret[1][0];\n        \n\t\tif(! empty($userInfo)){\n\t\t\treturn $userInfo;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 下载图片资源\n\t * @param  [string] $from    [资源链接]\n\t * @param  [string] $dir     [相对于网站根目录]\n\t * @return [string]          [返回相对地址]\n\t */\n\tpublic function downloadImage($from, $dir)\n\t{\n\t    $random_name =  str_replace('.', '', microtime(true)). rand(2, 10000);\n\t    if( ! is_dir($dir))\n\t    {\n\t    \tmkdir($dir, 0755, true);\t\n\t    }\n\t    $savefile = preg_replace('/[\\\\\\\\\\/]+/', DIRECTORY_SEPARATOR, $dir. '/'. $random_name);\n\t    file_put_contents($savefile, $from);\n\t    $filesize = filesize($savefile);\n\t    $avatar_type = $this->checkImgType($savefile);\n\t    if ($filesize <= 0 || $avatar_type=='unknown') \n\t    {\n\t        unlink($savefile);\n\t    }\n\t    exec(\"mv $savefile \". $savefile. '.'. $avatar_type);\n\t    return $dir. '/'. $random_name. '.'. $avatar_type;\n\t}\n\n\t/**\n\t * 检测图片类型\n\t * @param  [string] $imgName [文件路径]\n\t * @return [string]          [文件类型]\n\t */\n\tpublic function checkImgType($imgName){\n\t    $file = fopen($imgName, \"rb\");\n\t    $bin = fread($file, 2);\n\t    $strInfo  = @unpack(\"C2chars\", $bin);\n\t    $typeCode = intval($strInfo['chars1'].$strInfo['chars2']);\n\t    switch($typeCode)\n\t    {\n\t        case '255216':\n\t            return 'jpg';\n\t            break;\n\t        case '7173':\n\t            return 'gif';\n\t            break;\n\t        case '13780':\n\t            return 'png';\n\t            break;\n\t        case '6677':\n\t            return 'bmp';\n\t            break;\n\t        default:\n\t            return 'unknown';\n\t            break;\n\t    }\n\t}\n\t\n\t/**\n\t * 模拟登录获取cookie\n\t * @return [type] [description]\n\t */\n\tpublic function login(){\n\t\t$snoopy = new Snoopy; \n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN\";\n\t\t$post[\"username\"] = $this->_account;\n\t\t$post[\"pwd\"] = md5($this->_password);\n\t\t$post[\"f\"] = \"json\";\n\t\t$post[\"imgcode\"] = \"\";\n\t\t$snoopy->referer = \"https://mp.weixin.qq.com/\";\n\t\t$snoopy->submit($submit,$post);\n\t\t$cookie = '';\n\t\t$this->log($snoopy->results);\n\t\t$result = json_decode($snoopy->results,true);\n\t\t\n\t\tif (!isset($result['base_resp']) || $result['base_resp']['ret'] != 0) {\n\t\t\treturn false;\n\t\t}\n        \n\t\tforeach ($snoopy->headers as $key => $value) {\n\t\t\t$value = trim($value);\n\t\t\tif(preg_match('/^set-cookie:[\\s]+([^=]+)=([^;]+)/i', $value,$match))\n\t\t\t\t$cookie .=$match[1].'='.$match[2].'; ';\n\t\t}\n\t\t\n\t\tpreg_match(\"/token=(\\d+)/i\",$result['redirect_url'],$matches);\n\t\tif($matches){\n\t\t\t$this->_token = $matches[1];\n\t\t\t$this->log('token:'.$this->_token);\n\t\t}\n\t\t$cookies='{\"cookie\":\"'.$cookie.'\",\"token\":\"'.$this->_token.'\"}';\n\t\t$this->saveCookie($this->_cookiename,$cookies);\n\t\treturn $cookie;\n\t}\n\n\t/**\n\t * 把cookie写入缓存\n\t * @param  string $filename 缓存文件名\n\t * @param  string $content  文件内容\n\t * @return bool\n\t */\n\tpublic function saveCookie($filename,$content){\n\t\treturn file_put_contents($filename,$content);\n\t}\n\n\t/**\n\t * 读取cookie缓存内容\n\t * @param  string $filename 缓存文件名\n\t * @return string cookie\n\t */\n\tpublic function getCookie($filename){\n\t\tif (file_exists($filename)) {\n\t\t\t$mtime = filemtime($filename);\n\t\t\tif ($mtime<time()-$this->_cookieexpired) \n\t\t\t\t$data = '';\n\t\t\telse\n\t\t\t\t$data = file_get_contents($filename);\n\t\t} else\n\t\t\t$data = '';\n\t\tif($data){\n\t\t\t$login=json_decode($data,true);\n\t\t\t$send_snoopy = new Snoopy;\n\t\t\t$send_snoopy->rawheaders['Cookie']= $login['cookie'];\n\t\t\t$send_snoopy->maxredirs = 0;\n\t\t\t$url = \"https://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=\".$login['token'];\n\t\t\t$send_snoopy->fetch($url);\n\t\t\t$header = $send_snoopy->headers;\n\t\t\t$this->log('header:'.print_r($send_snoopy->headers,true));\n\t\t\tif( strstr($header[3], 'EXPIRED')){\n\t\t\t\treturn $this->login();\n\t\t\t}else{\n\t\t\t\t$this->_token =$login['token'];\n\t\t\t\t$this->log('token:'.$this->_token);\n\t\t\t\treturn $login['cookie'];\n\t\t\t}\n\t\t}else{\n\t\t\treturn $this->login();\n\t\t}\n\t}\n\n\t/**\n\t * 验证cookie的有效性\n\t * @return bool\n\t */\n\tpublic function checkValid()\n\t{\n\t\tif (!$this->cookie || !$this->_token) return false;\n\t\t$send_snoopy = new Snoopy; \n\t\t$post = array('ajax'=>1,'token'=>$this->_token);\n\t\t$submit = \"https://mp.weixin.qq.com/cgi-bin/getregions?id=1017&t=ajax-getregions&lang=zh_CN\";\n\t\t$send_snoopy->rawheaders['Cookie']= $this->cookie;\n\t\t$send_snoopy->submit($submit,$post);\n\t\t$result = $send_snoopy->results;\n\t\tif(json_decode($result,1)){\n\t\t\treturn true;\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tprivate function log($log){\n\t\tif ($this->debug && function_exists($this->_logcallback)) {\n\t\t\tif (is_array($log)) $log = print_r($log,true);\n\t\t\treturn call_user_func($this->_logcallback,$log);\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "old_version/wechatpay.class.php",
    "content": "<?php\n/**\n *\t微信公众平台PHP-SDK, 旧版微信支付接口(微信支付V2)\n *  @author  dodge <dodgepudding@gmail.com>\n *  @link https://github.com/dodgepudding/wechat-php-sdk\n *  @version 1.2\n *  参考旧版文档 https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl&lang=zh_CN\n *  usage:\n *   $options = array(\n *\t\t\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id\n *\t\t\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥\n *\t\t\t'partnerid'=>'88888888', //财付通商户身份标识\n *\t\t\t'partnerkey'=>'', //财付通商户权限密钥Key\n *\t\t\t'paysignkey'=>'' //商户签名密钥Key\n *\t\t);\n *\t $payObj = new Wechatpay($options);\n *   $package = $payObj->createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type,$bank_type,$input_charset,$time_start,$time_expire,$transport_fee,$product_fee,$goods_tag,$attach);\n *\n */\nclass Wechatpay\n{\n\tconst API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin';\n\tconst AUTH_URL = '/token?grant_type=client_credential&';\n\tconst API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀\n\tconst PAY_DELIVERNOTIFY = '/pay/delivernotify?';\n\tconst PAY_ORDERQUERY = '/pay/orderquery?';\n\n\tprivate $appid;\n\tprivate $appsecret;\n\tprivate $access_token;\n\tprivate $user_token;\n\tprivate $partnerid;\n\tprivate $partnerkey;\n\tprivate $paysignkey;\n\n\tpublic $debug =  false;\n\tpublic $errCode = 40001;\n\tpublic $errMsg = \"no access\";\n\tprivate $_logcallback;\n\n\tpublic function __construct($options)\n\t{\n\t\t$this->appid = isset($options['appid'])?$options['appid']:'';\n\t\t$this->appsecret = isset($options['appsecret'])?$options['appsecret']:'';\n\t\t$this->partnerid = isset($options['partnerid'])?$options['partnerid']:'';\n\t\t$this->partnerkey = isset($options['partnerkey'])?$options['partnerkey']:'';\n\t\t$this->paysignkey = isset($options['paysignkey'])?$options['paysignkey']:'';\n\t\t$this->debug = isset($options['debug'])?$options['debug']:false;\n\t\t$this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;\n\t}\n\n    private function log($log){\n    \t\tif ($this->debug && function_exists($this->_logcallback)) {\n    \t\t\tif (is_array($log)) $log = print_r($log,true);\n    \t\t\treturn call_user_func($this->_logcallback,$log);\n    \t\t}\n    }\n\n\t/**\n\t * GET 请求\n\t * @param string $url\n\t */\n\tprivate function http_get($url){\n\t\t$oCurl = curl_init();\n\t\tif(stripos($url,\"https://\")!==FALSE){\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1\n\t\t}\n\t\tcurl_setopt($oCurl, CURLOPT_URL, $url);\n\t\tcurl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );\n\t\t$sContent = curl_exec($oCurl);\n\t\t$aStatus = curl_getinfo($oCurl);\n\t\tcurl_close($oCurl);\n\t\tif(intval($aStatus[\"http_code\"])==200){\n\t\t\treturn $sContent;\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * POST 请求\n\t * @param string $url\n\t * @param array $param\n\t * @param boolean $post_file 是否文件上传\n\t * @return string content\n\t */\n\tprivate function http_post($url,$param,$post_file=false){\n\t\t$oCurl = curl_init();\n\t\tif(stripos($url,\"https://\")!==FALSE){\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1\n\t\t}\n\t\tif (is_string($param) || $post_file) {\n\t\t\t$strPOST = $param;\n\t\t} else {\n\t\t\t$aPOST = array();\n\t\t\tforeach($param as $key=>$val){\n\t\t\t\t$aPOST[] = $key.\"=\".urlencode($val);\n\t\t\t}\n\t\t\t$strPOST =  join(\"&\", $aPOST);\n\t\t}\n\t\tcurl_setopt($oCurl, CURLOPT_URL, $url);\n\t\tcurl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );\n\t\tcurl_setopt($oCurl, CURLOPT_POST,true);\n\t\tcurl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);\n\t\t$sContent = curl_exec($oCurl);\n\t\t$aStatus = curl_getinfo($oCurl);\n\t\tcurl_close($oCurl);\n\t\tif(intval($aStatus[\"http_code\"])==200){\n\t\t\treturn $sContent;\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * 获取access_token\n\t * @param string $appid 如在类初始化时已提供，则可为空\n\t * @param string $appsecret 如在类初始化时已提供，则可为空\n\t * @param string $token 手动指定access_token，非必要情况不建议用\n\t */\n\tpublic function checkAuth($appid='',$appsecret='',$token=''){\n\t\tif (!$appid || !$appsecret) {\n\t\t\t$appid = $this->appid;\n\t\t\t$appsecret = $this->appsecret;\n\t\t}\n\t\tif ($token) { //手动指定token，优先使用\n\t\t    $this->access_token=$token;\n\t\t    return $this->access_token;\n\t\t}\n\t\t//TODO: get the cache access_token\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::AUTH_URL.'appid='.$appid.'&secret='.$appsecret);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$this->access_token = $json['access_token'];\n\t\t\t$expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;\n\t\t\t//TODO: cache access_token\n\t\t\treturn $this->access_token;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 删除验证数据\n\t * @param string $appid\n\t */\n\tpublic function resetAuth($appid=''){\n\t\tif (!$appid) $appid = $this->appid;\n\t\t$this->access_token = '';\n\t\t//TODO: remove cache\n\t\treturn true;\n\t}\n\n\t/**\n\t * 微信api不支持中文转义的json结构\n\t * @param array $arr\n\t */\n\tstatic function json_encode($arr) {\n\t\t$parts = array ();\n\t\t$is_list = false;\n\t\t//Find out if the given array is a numerical array\n\t\t$keys = array_keys ( $arr );\n\t\t$max_length = count ( $arr ) - 1;\n\t\tif (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1\n\t\t\t$is_list = true;\n\t\t\tfor($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position\n\t\t\t\tif ($i != $keys [$i]) { //A key fails at position check.\n\t\t\t\t\t$is_list = false; //It is an associative array.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tforeach ( $arr as $key => $value ) {\n\t\t\tif (is_array ( $value )) { //Custom handling for arrays\n\t\t\t\tif ($is_list)\n\t\t\t\t\t$parts [] = self::json_encode ( $value ); /* :RECURSION: */\n\t\t\t\telse\n\t\t\t\t\t$parts [] = '\"' . $key . '\":' . self::json_encode ( $value ); /* :RECURSION: */\n\t\t\t} else {\n\t\t\t\t$str = '';\n\t\t\t\tif (! $is_list)\n\t\t\t\t\t$str = '\"' . $key . '\":';\n\t\t\t\t//Custom handling for multiple data types\n\t\t\t\tif (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)\n\t\t\t\t\t$str .= $value; //Numbers\n\t\t\t\telseif ($value === false)\n\t\t\t\t$str .= 'false'; //The booleans\n\t\t\t\telseif ($value === true)\n\t\t\t\t$str .= 'true';\n\t\t\t\telse\n\t\t\t\t\t$str .= '\"' . addslashes ( $value ) . '\"'; //All other things\n\t\t\t\t// :TODO: Is there any more datatype we should be in the lookout for? (Object?)\n\t\t\t\t$parts [] = $str;\n\t\t\t}\n\t\t}\n\t\t$json = implode ( ',', $parts );\n\t\tif ($is_list)\n\t\t\treturn '[' . $json . ']'; //Return numerical JSON\n\t\treturn '{' . $json . '}'; //Return associative JSON\n\t}\n\n\t/**\n\t * 获取签名\n\t * @param array $arrdata 签名数组\n\t * @param string $method 签名方法\n\t * @return boolean|string 签名值\n\t */\n\tpublic function getSignature($arrdata,$method=\"sha1\") {\n\t\tif (!function_exists($method)) return false;\n\t\tksort($arrdata);\n\t\t$paramstring = \"\";\n\t\tforeach($arrdata as $key => $value)\n\t\t{\n\t\t\tif(strlen($paramstring) == 0)\n\t\t\t\t$paramstring .= $key . \"=\" . $value;\n\t\t\telse\n\t\t\t\t$paramstring .= \"&\" . $key . \"=\" . $value;\n\t\t}\n\t\t$paySign = $method($paramstring);\n\t\treturn $paySign;\n\t}\n\n\t/**\n\t * 生成随机字串\n\t * @param number $length 长度，默认为16，最长为32字节\n\t * @return string\n\t */\n\tpublic function generateNonceStr($length=16){\n\t\t// 密码字符集，可任意添加你需要的字符\n\t\t$chars = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\n\t\t$str = \"\";\n\t\tfor($i = 0; $i < $length; $i++)\n\t\t{\n\t\t\t$str .= $chars[mt_rand(0, strlen($chars) - 1)];\n\t\t}\n\t\treturn $str;\n\t}\n\n\t/**\n\t * 生成原生支付url\n\t * @param number $productid 商品编号，最长为32字节\n\t * @return string\n\t */\n\tpublic function createNativeUrl($productid){\n\t\t    $nativeObj[\"appid\"] = $this->appid;\n\t\t    $nativeObj[\"appkey\"] = $this->paysignkey;\n\t\t    $nativeObj[\"productid\"] = urlencode($productid);\n\t\t    $nativeObj[\"timestamp\"] = time();\n\t\t    $nativeObj[\"noncestr\"] = $this->generateNonceStr();\n\t\t    $nativeObj[\"sign\"] = $this->getSignature($nativeObj);\n\t\t    unset($nativeObj[\"appkey\"]);\n\t\t    $bizString = \"\";\n\t\t    foreach($nativeObj as $key => $value)\n\t\t    {\n\t\t\tif(strlen($bizString) == 0)\n\t\t\t\t$bizString .= $key . \"=\" . $value;\n\t\t\telse\n\t\t\t\t$bizString .= \"&\" . $key . \"=\" . $value;\n\t\t    }\n\t\t    return \"weixin://wxpay/bizpayurl?\".$bizString;\n\t\t    //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXXX&productid=XXXXXX&timestamp=XXXXXX&noncestr=XXXXXX\n\t}\n\n\n\t/**\n\t * 生成订单package字符串\n\t * @param string $out_trade_no 必填，商户系统内部的订单号,32个字符内,确保在商户系统唯一\n\t * @param string $body 必填，商品描述,128 字节以下\n\t * @param int $total_fee 必填，订单总金额,单位为分\n\t * @param string $notify_url 必填，支付完成通知回调接口，255 字节以内\n\t * @param string $spbill_create_ip 必填，用户终端IP，IPV4字串，15字节内\n\t * @param int $fee_type 必填，现金支付币种，默认1:人民币\n\t * @param string $bank_type 必填，银行通道类型,默认WX\n\t * @param string $input_charset 必填，传入参数字符编码，默认UTF-8，取值有UTF-8和GBK\n\t * @param string $time_start 交易起始时间,订单生成时间,格式yyyyMMddHHmmss\n\t * @param string $time_expire 交易结束时间,也是订单失效时间\n\t * @param int $transport_fee 物流费用,单位为分\n\t * @param int $product_fee 商品费用,单位为分,必须保证 transport_fee + product_fee=total_fee\n\t * @param string $goods_tag 商品标记,优惠券时可能用到\n\t * @param string $attach 附加数据，notify接口原样返回\n\t * @return string\n\t */\n\tpublic function createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type=1,$bank_type=\"WX\",$input_charset=\"UTF-8\",$time_start=\"\",$time_expire=\"\",$transport_fee=\"\",$product_fee=\"\",$goods_tag=\"\",$attach=\"\"){\n\t\t\t$arrdata = array(\"bank_type\" => $bank_type, \"body\" => $body, \"partner\" => $this->partnerid, \"out_trade_no\" => $out_trade_no, \"total_fee\" => $total_fee, \"fee_type\" => $fee_type, \"notify_url\" => $notify_url, \"spbill_create_ip\" => $spbill_create_ip, \"input_charset\" => $input_charset);\n\t\t\tif ($time_start)  $arrdata['time_start'] = $time_start;\n\t\t\tif ($time_expire)  $arrdata['time_expire'] = $time_expire;\n\t\t\tif ($transport_fee)  $arrdata['transport_fee'] = $transport_fee;\n\t\t\tif ($product_fee)  $arrdata['product_fee'] = $product_fee;\n\t\t\tif ($goods_tag)  $arrdata['goods_tag'] = $goods_tag;\n\t\t\tif ($attach)  $arrdata['attach'] = $attach;\n\t\t\tksort($arrdata);\n\t\t\t$paramstring = \"\";\n\t\t\tforeach($arrdata as $key => $value)\n\t\t\t{\n\t\t\tif(strlen($paramstring) == 0)\n\t\t\t\t$paramstring .= $key . \"=\" . $value;\n\t\t\t\telse\n\t\t\t\t$paramstring .= \"&\" . $key . \"=\" . $value;\n\t\t\t}\n\t\t\t$stringSignTemp = $paramstring . \"&key=\" . $this->partnerkey;\n\t\t\t$signValue = strtoupper(md5($stringSignTemp));\n\t\t\t$package = http_build_query($arrdata) . \"&sign=\" . $signValue;\n\t\t\treturn $package;\n\t}\n\n\t/**\n\t * 支付签名(paySign)生成方法\n\t * @param string $package 订单详情字串\n\t * @param string $timeStamp 当前时间戳（需与JS输出的一致）\n\t * @param string $nonceStr 随机串（需与JS输出的一致）\n\t * @return string 返回签名字串\n\t */\n\tpublic function getPaySign($package, $timeStamp, $nonceStr){\n\t\t$arrdata = array(\"appid\" => $this->appid, \"timestamp\" => $timeStamp, \"noncestr\" => $nonceStr, \"package\" => $package, \"appkey\" => $this->paysignkey);\n\t\t$paySign = $this->getSignature($arrdata);\n\t\treturn $paySign;\n\t}\n\n\t/**\n\t * 回调通知签名验证\n\t * @param array $orderxml 返回的orderXml的数组表示，留空则自动从post数据获取\n\t * @return boolean\n\t */\n\tpublic function checkOrderSignature($orderxml=''){\n\t\tif (!$orderxml) {\n\t\t\t$postStr = file_get_contents(\"php://input\");\n\t\t\tif (!empty($postStr)) {\n\t\t\t\t$orderxml = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);\n\t\t\t} else return false;\n\t\t}\n\t\t$arrdata = array('appid'=>$orderxml['AppId'],'appkey'=>$this->paysignkey,'timestamp'=>$orderxml['TimeStamp'],'noncestr'=>$orderxml['NonceStr'],'openid'=>$orderxml['OpenId'],'issubscribe'=>$orderxml['IsSubscribe']);\n\t\t$paySign = $this->getSignature($arrdata);\n\t\tif ($paySign!=$orderxml['AppSignature']) return false;\n\t\treturn true;\n\t}\n\n\t/**\n\t * 发货通知\n\t * @param string $openid 用户open_id\n\t * @param string $transid 交易单号\n\t * @param string $out_trade_no 第三方订单号\n\t * @param int $status 0:发货失败；1:已发货\n\t * @param string $msg 失败原因\n\t * @return boolean|array\n\t */\n\tpublic function sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok'){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$postdata = array(\n\t\t\t\t\"appid\"=>$this->appid,\n\t\t\t\t\"appkey\"=>$this->paysignkey,\n\t\t\t\t\"openid\"=>$openid,\n\t\t\t\t\"transid\"=>strval($transid),\n\t\t\t\t\"out_trade_no\"=>strval($out_trade_no),\n\t\t\t\t\"deliver_timestamp\"=>strval(time()),\n\t\t\t\t\"deliver_status\"=>strval($status),\n\t\t\t\t\"deliver_msg\"=>$msg,\n\t\t);\n\t\t$postdata['app_signature'] = $this->getSignature($postdata);\n\t\t$postdata['sign_method'] = 'sha1';\n\t\tunset($postdata['appkey']);\n\t\t$result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_DELIVERNOTIFY.'access_token='.$this->access_token,self::json_encode($postdata));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 查询订单信息\n\t * @param string $out_trade_no 订单号\n\t * @return boolean|array\n\t */\n\tpublic function getPayOrder($out_trade_no) {\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$sign = strtoupper(md5(\"out_trade_no=$out_trade_no&partner={$this->partnerid}&key={$this->partnerkey}\"));\n\t\t$postdata = array(\n\t\t\t\t\"appid\"=>$this->appid,\n\t\t\t\t\"appkey\"=>$this->paysignkey,\n\t\t\t\t\"package\"=>\"out_trade_no=$out_trade_no&partner={$this->partnerid}&sign=$sign\",\n\t\t\t\t\"timestamp\"=>strval(time()),\n\t\t);\n\t\t$postdata['app_signature'] = $this->getSignature($postdata);\n\t\t$postdata['sign_method'] = 'sha1';\n\t\tunset($postdata['appkey']);\n\t\t$result = $this->http_post(self::API_BASE_URL_PREFIX.self::PAY_ORDERQUERY.'access_token='.$this->access_token,self::json_encode($postdata));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'].json_encode($postdata);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json[\"order_info\"];\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 设置用户授权密钥\n\t * @param string $user_token\n\t * @return string\n\t */\n\tpublic function setUserToken($user_token) {\n\t\treturn $this->user_token = $user_token;\n\t}\n\n\t/**\n\t * 获取收货地址JS的签名\n\t * @tutorial 参考weixin.js脚本的WeixinJS.editAddress方法调用\n\t * @param string $appId\n\t * @param string $url\n\t * @param int $timeStamp\n\t * @param string $nonceStr\n\t * @param string $user_token\n\t * @return Ambigous <boolean, string>\n\t */\n\tpublic function getAddrSign($url, $timeStamp, $nonceStr, $user_token=''){\n\t\tif (!$user_token) $user_token = $this->user_token;\n\t\tif (!$user_token) {\n\t\t\t$this->errMsg = 'no user access token found!';\n\t\t\treturn false;\n\t\t}\n\t\t$url = htmlspecialchars_decode($url);\n\t\t$arrdata = array(\n\t\t\t\t'appid'=>$this->appid,\n\t\t\t\t'url'=>$url,\n\t\t\t\t'timestamp'=>strval($timeStamp),\n\t\t\t\t'noncestr'=>$nonceStr,\n\t\t\t\t'accesstoken'=>$user_token\n\t\t);\n\t\treturn $this->getSignature($arrdata);\n\t}\n}\n"
  },
  {
    "path": "qyerrCode.php",
    "content": "<?php\n/**\n *\t微信公众平台企业号PHP-SDK, 全局返回码类\n *  @author  binsee <binsee@163.com>\n *  @link https://github.com/binsee/wechat-php-sdk\n *  @version 1.0\n *  usage:\n *      $ret=ErrCode::getErrText(40001); //错误码可以通过公众号类库的公开变量errCode得到\n *      if ($ret)\n *      \techo $ret;\n *      else\n *          echo \"未找到对应的内容\";\n */\nclass ErrCode\n{\n\tpublic static $errCode=array(\n\t        '-1'=>'系统繁忙',\n\t        '0'=>'请求成功',\n\t        '40001'=>'获取access_token时AppSecret错误，或者access_token无效',\n\t        '40002'=>'不合法的凭证类型',\n\t        '40003'=>'不合法的UserID',\n\t        '40004'=>'不合法的媒体文件类型',\n\t        '40005'=>'不合法的文件类型',\n\t        '40006'=>'不合法的文件大小',\n\t        '40007'=>'不合法的媒体文件id',\n\t        '40008'=>'不合法的消息类型',\n\t        '40013'=>'不合法的corpid',\n\t        '40014'=>'不合法的access_token',\n\t        '40015'=>'不合法的菜单类型',\n\t        '40016'=>'不合法的按钮个数',\n\t        '40017'=>'不合法的按钮类型',\n\t        '40018'=>'不合法的按钮名字长度',\n\t        '40019'=>'不合法的按钮KEY长度',\n\t        '40020'=>'不合法的按钮URL长度',\n\t        '40021'=>'不合法的菜单版本号',\n\t        '40022'=>'不合法的子菜单级数',\n\t        '40023'=>'不合法的子菜单按钮个数',\n\t        '40024'=>'不合法的子菜单按钮类型',\n\t        '40025'=>'不合法的子菜单按钮名字长度',\n\t        '40026'=>'不合法的子菜单按钮KEY长度',\n\t        '40027'=>'不合法的子菜单按钮URL长度',\n\t        '40028'=>'不合法的自定义菜单使用员工',\n\t        '40029'=>'不合法的oauth_code',\n\t        '40031'=>'不合法的UserID列表',\n\t        '40032'=>'不合法的UserID列表长度',\n\t        '40033'=>'不合法的请求字符，不能包含\\uxxxx格式的字符',\n\t        '40035'=>'不合法的参数',\n\t        '40038'=>'不合法的请求格式',\n\t        '40039'=>'不合法的URL长度',\n\t        '40040'=>'不合法的插件token',\n\t        '40041'=>'不合法的插件id',\n\t        '40042'=>'不合法的插件会话',\n\t        '40048'=>'url中包含不合法domain',\n\t        '40054'=>'不合法的子菜单url域名',\n\t        '40055'=>'不合法的按钮url域名',\n\t        '40056'=>'不合法的agentid',\n\t        '40057'=>'不合法的callbackurl',\n\t        '40058'=>'不合法的红包参数',\n\t        '40059'=>'不合法的上报地理位置标志位',\n\t        '40060'=>'设置上报地理位置标志位时没有设置callbackurl',\n\t        '40061'=>'设置应用头像失败',\n\t        '40062'=>'不合法的应用模式',\n\t        '40063'=>'红包参数为空',\n\t        '40064'=>'管理组名字已存在',\n\t        '40065'=>'不合法的管理组名字长度',\n\t        '40066'=>'不合法的部门列表',\n\t        '40067'=>'标题长度不合法',\n\t        '40068'=>'不合法的标签ID',\n\t        '40069'=>'不合法的标签ID列表',\n\t        '40070'=>'列表中所有标签（用户）ID都不合法',\n\t        '40071'=>'不合法的标签名字，标签名字已经存在',\n\t        '40072'=>'不合法的标签名字长度',\n\t        '40073'=>'不合法的openid',\n\t        '40074'=>'news消息不支持指定为高保密消息',\n\t        '41001'=>'缺少access_token参数',\n\t        '41002'=>'缺少corpid参数',\n\t        '41003'=>'缺少refresh_token参数',\n\t        '41004'=>'缺少secret参数',\n\t        '41005'=>'缺少多媒体文件数据',\n\t        '41006'=>'缺少media_id参数',\n\t        '41007'=>'缺少子菜单数据',\n\t        '41008'=>'缺少oauth code',\n\t        '41009'=>'缺少UserID',\n\t        '41010'=>'缺少url',\n\t        '41011'=>'缺少agentid',\n\t        '41012'=>'缺少应用头像mediaid',\n\t        '41013'=>'缺少应用名字',\n\t        '41014'=>'缺少应用描述',\n\t        '41015'=>'缺少Content',\n\t        '41016'=>'缺少标题',\n\t        '41017'=>'缺少标签ID',\n\t        '41018'=>'缺少标签名字',\n\t        '42001'=>'access_token超时',\n\t        '42002'=>'refresh_token超时',\n\t        '42003'=>'oauth_code超时',\n\t        '42004'=>'插件token超时',\n\t        '43001'=>'需要GET请求',\n\t        '43002'=>'需要POST请求',\n\t        '43003'=>'需要HTTPS',\n\t        '43004'=>'需要接收者关注',\n\t        '43005'=>'需要好友关系',\n\t        '43006'=>'需要订阅',\n\t        '43007'=>'需要授权',\n\t        '43008'=>'需要支付授权',\n\t        '43009'=>'需要员工已关注',\n\t        '43010'=>'需要处于企业模式',\n\t        '43011'=>'需要企业授权',\n\t        '44001'=>'多媒体文件为空',\n\t        '44002'=>'POST的数据包为空',\n\t        '44003'=>'图文消息内容为空',\n\t        '44004'=>'文本消息内容为空',\n\t        '45001'=>'多媒体文件大小超过限制',\n\t        '45002'=>'消息内容超过限制',\n\t        '45003'=>'标题字段超过限制',\n\t        '45004'=>'描述字段超过限制',\n\t        '45005'=>'链接字段超过限制',\n\t        '45006'=>'图片链接字段超过限制',\n\t        '45007'=>'语音播放时间超过限制',\n\t        '45008'=>'图文消息超过限制',\n\t        '45009'=>'接口调用超过限制',\n\t        '45010'=>'创建菜单个数超过限制',\n\t        '45015'=>'回复时间超过限制',\n\t        '45016'=>'系统分组，不允许修改',\n\t        '45017'=>'分组名字过长',\n\t        '45018'=>'分组数量超过上限',\n\t        '46001'=>'不存在媒体数据',\n\t        '46002'=>'不存在的菜单版本',\n\t        '46003'=>'不存在的菜单数据',\n\t        '46004'=>'不存在的员工',\n\t        '47001'=>'解析JSON/XML内容错误',\n\t        '48002'=>'Api禁用',\n\t        '50001'=>'redirect_uri未授权',\n\t        '50002'=>'员工不在权限范围',\n\t        '50003'=>'应用已停用',\n\t        '50004'=>'员工状态不正确（未关注状态）',\n\t        '50005'=>'企业已禁用',\n\t        '60001'=>'部门长度不符合限制',\n\t        '60002'=>'部门层级深度超过限制',\n\t        '60003'=>'部门不存在',\n\t        '60004'=>'父亲部门不存在',\n\t        '60005'=>'不允许删除有成员的部门',\n\t        '60006'=>'不允许删除有子部门的部门',\n\t        '60007'=>'不允许删除根部门',\n\t        '60008'=>'部门名称已存在',\n\t        '60009'=>'部门名称含有非法字符',\n\t        '60010'=>'部门存在循环关系',\n\t        '60011'=>'管理员权限不足，（user/department/agent）无权限',\n\t        '60012'=>'不允许删除默认应用',\n\t        '60013'=>'不允许关闭应用',\n\t        '60014'=>'不允许开启应用',\n\t        '60015'=>'不允许修改默认应用可见范围',\n\t        '60016'=>'不允许删除存在成员的标签',\n\t        '60017'=>'不允许设置企业',\n\t        '60102'=>'UserID已存在',\n\t        '60103'=>'手机号码不合法',\n\t        '60104'=>'手机号码已存在',\n\t        '60105'=>'邮箱不合法',\n\t        '60106'=>'邮箱已存在',\n\t        '60107'=>'微信号不合法',\n\t        '60108'=>'微信号已存在',\n\t        '60109'=>'QQ号已存在',\n\t        '60110'=>'部门个数超出限制',\n\t        '60111'=>'UserID不存在',\n\t        '60112'=>'成员姓名不合法',\n\t        '60113'=>'身份认证信息（微信号/手机/邮箱）不能同时为空',\n\t        '60114'=>'性别不合法',\n\t        '60119'=>'用户已关注',\n\t        '60120'=>'用户已禁用',\n\t        '60121'=>'找不到该用户',\n\t        '60023'=>'应用已授权予第三方，不允许通过分级管理员修改菜单',\n\t        '80001'=>'可信域名没有IPC备案，后续将不能在该域名下正常使用jssdk',\n\n\t);\n\n\tpublic static function getErrText($err) {\n\t\tif (isset(self::$errCode[$err])) {\n\t\t\treturn self::$errCode[$err];\n\t\t}else {\n\t\t    return false;\n\t\t};\n\t}\n}\n\n?>"
  },
  {
    "path": "qywechat.class.php",
    "content": "<?php\n/**\n *\t微信公众平台企业号PHP-SDK, 官方API类库\n *  @author  binsee <binsee@163.com>\n *  @link https://github.com/binsee/wechat-php-sdk\n *  @version 1.0\n *  usage:\n *   $options = array(\n *\t\t\t'token'=>'tokenaccesskey', //填写应用接口的Token\n *\t\t\t'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey\n *\t\t\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id\n *\t\t\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥\n *\t\t\t'agentid'=>'1', //应用的id\n *\t\t\t'debug'=>false, //调试开关\n *\t\t\t'logcallback'=>'logg', //调试输出方法，需要有一个string类型的参数\n *\t\t);\n *\n */\nclass Wechat\n{\n    const MSGTYPE_TEXT \t\t= 'text';\n    const MSGTYPE_IMAGE \t= 'image';\n    const MSGTYPE_LOCATION \t= 'location';\n    const MSGTYPE_LINK \t\t= 'link';    \t//暂不支持\n    const MSGTYPE_EVENT \t= 'event';\n    const MSGTYPE_MUSIC \t= 'music';    \t//暂不支持\n    const MSGTYPE_NEWS \t\t= 'news';\n    const MSGTYPE_VOICE \t= 'voice';\n    const MSGTYPE_VIDEO \t= 'video';\n\n    const EVENT_SUBSCRIBE \t= 'subscribe';      //订阅\n    const EVENT_UNSUBSCRIBE = 'unsubscribe'; \t//取消订阅\n    const EVENT_LOCATION \t= 'LOCATION';       //上报地理位置\n    const EVENT_ENTER_AGENT = 'enter_agent';   \t//用户进入应用\n\n    const EVENT_MENU_VIEW \t\t\t= 'VIEW'; \t\t\t\t//菜单 - 点击菜单跳转链接\n    const EVENT_MENU_CLICK \t\t\t= 'CLICK';              //菜单 - 点击菜单拉取消息\n    const EVENT_MENU_SCAN_PUSH \t\t= 'scancode_push';      //菜单 - 扫码推事件(客户端跳URL)\n    const EVENT_MENU_SCAN_WAITMSG \t= 'scancode_waitmsg'; \t//菜单 - 扫码推事件(客户端不跳URL)\n    const EVENT_MENU_PIC_SYS \t\t= 'pic_sysphoto';       //菜单 - 弹出系统拍照发图\n    const EVENT_MENU_PIC_PHOTO \t\t= 'pic_photo_or_album'; //菜单 - 弹出拍照或者相册发图\n    const EVENT_MENU_PIC_WEIXIN \t= 'pic_weixin';         //菜单 - 弹出微信相册发图器\n    const EVENT_MENU_LOCATION \t\t= 'location_select';    //菜单 - 弹出地理位置选择器\n\n    const EVENT_SEND_MASS = 'MASSSENDJOBFINISH';        //发送结果 - 高级群发完成\n    const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果\n\n    const API_URL_PREFIX = 'https://qyapi.weixin.qq.com/cgi-bin';\n\n    const USER_CREATE_URL \t\t= '/user/create?';\n    const USER_UPDATE_URL \t\t= '/user/update?';\n    const USER_DELETE_URL \t\t= '/user/delete?';\n    const USER_BATCHDELETE_URL \t= '/user/batchdelete?';\n    const USER_GET_URL \t\t\t= '/user/get?';\n    const USER_LIST_URL \t\t= '/user/simplelist?';\n    const USER_LIST_INFO_URL \t= '/user/list?';\n    const USER_GETINFO_URL \t\t= '/user/getuserinfo?';\n    const USER_INVITE_URL \t\t= '/invite/send?';\n    const DEPARTMENT_CREATE_URL = '/department/create?';\n    const DEPARTMENT_UPDATE_URL = '/department/update?';\n    const DEPARTMENT_DELETE_URL = '/department/delete?';\n    const DEPARTMENT_MOVE_URL \t= '/department/move?';\n    const DEPARTMENT_LIST_URL \t= '/department/list?';\n    const TAG_CREATE_URL \t\t= '/tag/create?';\n    const TAG_UPDATE_URL \t\t= '/tag/update?';\n    const TAG_DELETE_URL \t\t= '/tag/delete?';\n    const TAG_GET_URL \t\t\t= '/tag/get?';\n    const TAG_ADDUSER_URL \t\t= '/tag/addtagusers?';\n    const TAG_DELUSER_URL \t\t= '/tag/deltagusers?';\n    const TAG_LIST_URL \t\t\t= '/tag/list?';\n    const MEDIA_UPLOAD_URL \t\t= '/media/upload?';\n    const MEDIA_GET_URL \t\t= '/media/get?';\n    const AUTHSUCC_URL \t\t\t= '/user/authsucc?';\n    const MASS_SEND_URL \t\t= '/message/send?';\n    const MENU_CREATE_URL \t\t= '/menu/create?';\n    const MENU_GET_URL \t\t\t= '/menu/get?';\n    const MENU_DELETE_URL \t\t= '/menu/delete?';\n    const TOKEN_GET_URL \t\t= '/gettoken?';\n    const TICKET_GET_URL \t\t= '/get_jsapi_ticket?';\n\tconst CALLBACKSERVER_GET_URL = '/getcallbackip?';\n\tconst OAUTH_PREFIX \t\t\t= 'https://open.weixin.qq.com/connect/oauth2';\n\tconst OAUTH_AUTHORIZE_URL \t= '/authorize?';\n\n\tprivate $token;\n\tprivate $encodingAesKey;\n\tprivate $appid;         //也就是企业号的CorpID\n\tprivate $appsecret;\n\tprivate $access_token;\n    private $agentid;       //应用id   AgentID\n\tprivate $postxml;\n    private $agentidxml;    //接收的应用id   AgentID\n\tprivate $_msg;\n\tprivate $_receive;\n\tprivate $_sendmsg;      //主动发送消息的内容\n\tprivate $_text_filter = true;\n\tpublic $debug =  false;\n\tpublic $errCode = 40001;\n\tpublic $errMsg = \"no access\";\n\tpublic $logcallback;\n\n\tpublic function __construct($options)\n\t{\n\t\t$this->token = isset($options['token'])?$options['token']:'';\n\t\t$this->encodingAesKey = isset($options['encodingaeskey'])?$options['encodingaeskey']:'';\n\t\t$this->appid = isset($options['appid'])?$options['appid']:'';\n\t\t$this->appsecret = isset($options['appsecret'])?$options['appsecret']:'';\n\t\t$this->agentid = isset($options['agentid'])?$options['agentid']:'';\n\t\t$this->debug = isset($options['debug'])?$options['debug']:false;\n\t\t$this->logcallback = isset($options['logcallback'])?$options['logcallback']:false;\n\t}\n\n\tprotected function log($log){\n\t    if ($this->debug && function_exists($this->logcallback)) {\n\t        if (is_array($log)) $log = print_r($log,true);\n\t        return call_user_func($this->logcallback,$log);\n\t    }\n\t}\n\n\t/**\n\t * 数据XML编码\n\t * @param mixed $data 数据\n\t * @return string\n\t */\n\tpublic static function data_to_xml($data) {\n\t    $xml = '';\n\t    foreach ($data as $key => $val) {\n\t        is_numeric($key) && $key = \"item id=\\\"$key\\\"\";\n\t        $xml    .=  \"<$key>\";\n\t        $xml    .=  ( is_array($val) || is_object($val)) ? self::data_to_xml($val)  : self::xmlSafeStr($val);\n\t        list($key, ) = explode(' ', $key);\n\t        $xml    .=  \"</$key>\";\n\t    }\n\t    return $xml;\n\t}\n\n\tpublic static function xmlSafeStr($str)\n\t{\n\t    return '<![CDATA['.preg_replace(\"/[\\\\x00-\\\\x08\\\\x0b-\\\\x0c\\\\x0e-\\\\x1f]/\",'',$str).']]>';\n\t}\n\n\t/**\n\t * XML编码\n\t * @param mixed $data 数据\n\t * @param string $root 根节点名\n\t * @param string $item 数字索引的子节点名\n\t * @param string $attr 根节点属性\n\t * @param string $id   数字索引子节点key转换的属性名\n\t * @param string $encoding 数据编码\n\t * @return string\n\t */\n\tpublic function xml_encode($data, $root='xml', $item='item', $attr='', $id='id', $encoding='utf-8') {\n\t    if(is_array($attr)){\n\t        $_attr = array();\n\t        foreach ($attr as $key => $value) {\n\t            $_attr[] = \"{$key}=\\\"{$value}\\\"\";\n\t        }\n\t        $attr = implode(' ', $_attr);\n\t    }\n\t    $attr   = trim($attr);\n\t    $attr   = empty($attr) ? '' : \" {$attr}\";\n\t    $xml   = \"<{$root}{$attr}>\";\n\t    $xml   .= self::data_to_xml($data, $item, $id);\n\t    $xml   .= \"</{$root}>\";\n\t    return $xml;\n\t}\n\n\n\t/**\n\t * 微信api不支持中文转义的json结构\n\t * @param array $arr\n\t */\n\tstatic function json_encode($arr) {\n\t    $parts = array ();\n\t    $is_list = false;\n\t    //Find out if the given array is a numerical array\n\t    $keys = array_keys ( $arr );\n\t    $max_length = count ( $arr ) - 1;\n\t    if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1\n\t        $is_list = true;\n\t        for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position\n\t            if ($i != $keys [$i]) { //A key fails at position check.\n\t                $is_list = false; //It is an associative array.\n\t                break;\n\t            }\n\t        }\n\t    }\n\t    foreach ( $arr as $key => $value ) {\n\t        if (is_array ( $value )) { //Custom handling for arrays\n\t            if ($is_list)\n\t                $parts [] = self::json_encode ( $value ); /* :RECURSION: */\n\t            else\n\t                $parts [] = '\"' . $key . '\":' . self::json_encode ( $value ); /* :RECURSION: */\n\t        } else {\n\t            $str = '';\n\t            if (! $is_list)\n\t                $str = '\"' . $key . '\":';\n\t            //Custom handling for multiple data types\n\t            if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)\n\t                $str .= $value; //Numbers\n\t            elseif ($value === false)\n\t            $str .= 'false'; //The booleans\n\t            elseif ($value === true)\n\t            $str .= 'true';\n\t            else\n\t                $str .= '\"' .addcslashes($value, \"\\\\\\\"\\n\\r\\t/\"). '\"'; //All other things\n\t            // :TODO: Is there any more datatype we should be in the lookout for? (Object?)\n\t            $parts [] = $str;\n\t        }\n\t    }\n\t    $json = implode ( ',', $parts );\n\t    if ($is_list)\n\t        return '[' . $json . ']'; //Return numerical JSON\n\t    return '{' . $json . '}'; //Return associative JSON\n\t}\n\n\t/**\n\t * 过滤文字回复\\r\\n换行符\n\t * @param string $text\n\t * @return string|mixed\n\t */\n\tprivate function _auto_text_filter($text) {\n\t    if (!$this->_text_filter) return $text;\n\t    return str_replace(\"\\r\\n\", \"\\n\", $text);\n\t}\n\n\t/**\n\t * GET 请求\n\t * @param string $url\n\t */\n\tprivate function http_get($url){\n\t    $oCurl = curl_init();\n\t    if(stripos($url,\"https://\")!==FALSE){\n\t        curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);\n\t        curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);\n\t        curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1\n\t    }\n\t    curl_setopt($oCurl, CURLOPT_URL, $url);\n\t    curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );\n\t    $sContent = curl_exec($oCurl);\n\t    $aStatus = curl_getinfo($oCurl);\n\t    curl_close($oCurl);\n\t    if(intval($aStatus[\"http_code\"])==200){\n\t        return $sContent;\n\t    }else{\n\t        return false;\n\t    }\n\t}\n\n\t/**\n\t * POST 请求\n\t * @param string $url\n\t * @param array $param\n\t * @param boolean $post_file 是否文件上传\n\t * @return string content\n\t */\n\tprivate function http_post($url,$param,$post_file=false){\n\t\t$oCurl = curl_init();\n\t\tif(stripos($url,\"https://\")!==FALSE){\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1\n\t\t}\n\t\tif (is_string($param) || $post_file) {\n\t\t\t$strPOST = $param;\n\t\t} else {\n\t\t\t$aPOST = array();\n\t\t\tforeach($param as $key=>$val){\n\t\t\t\t$aPOST[] = $key.\"=\".urlencode($val);\n\t\t\t}\n\t\t\t$strPOST =  join(\"&\", $aPOST);\n\t\t}\n\t\tcurl_setopt($oCurl, CURLOPT_URL, $url);\n\t\tcurl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );\n\t\tcurl_setopt($oCurl, CURLOPT_POST,true);\n\t\tif(PHP_VERSION_ID >= 50500){\n\t\t\tcurl_setopt($oCurl, CURLOPT_SAFE_UPLOAD, FALSE);\n\t\t}\n\t\tcurl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);\n\n\t\t$sContent = curl_exec($oCurl);\n\t\t$aStatus = curl_getinfo($oCurl);\n\t\tcurl_close($oCurl);\n\t\tif(intval($aStatus[\"http_code\"])==200){\n\t\t\treturn $sContent;\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * For weixin server validation\n\t */\n\tprivate function checkSignature($str)\n\t{\n\t    $signature = isset($_GET[\"msg_signature\"])?$_GET[\"msg_signature\"]:'';\n\t    $timestamp = isset($_GET[\"timestamp\"])?$_GET[\"timestamp\"]:'';\n\t    $nonce = isset($_GET[\"nonce\"])?$_GET[\"nonce\"]:'';\n\t    $tmpArr = array($str,$this->token, $timestamp, $nonce);//比普通公众平台多了一个加密的密文\n\t    sort($tmpArr, SORT_STRING);\n\t    $tmpStr = implode($tmpArr);\n\t    $shaStr = sha1($tmpStr);\n\t    if( $shaStr == $signature ){\n\t        return true;\n\t    }else{\n\t        return false;\n\t    }\n\t}\n\n\t/**\n\t * 微信验证，包括post来的xml解密\n\t * @param bool $return 是否返回\n\t */\n\tpublic function valid($return=false)\n    {\n        $encryptStr=\"\";\n        if ($_SERVER['REQUEST_METHOD'] == \"POST\") {\n            $postStr = file_get_contents(\"php://input\");\n            $array = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);\n            $this->log($postStr);\n            if (isset($array['Encrypt'])) {\n                $encryptStr = $array['Encrypt'];\n                $this->agentidxml = isset($array['AgentID']) ? $array['AgentID']: '';\n            }\n        } else {\n            $encryptStr = isset($_GET[\"echostr\"]) ? $_GET[\"echostr\"]: '';\n\n        }\n        if ($encryptStr) {\n            $ret=$this->checkSignature($encryptStr);\n        }\n        if (!isset($ret) || !$ret) {\n        \tif (!$return) {\n        \t    die('no access');\n        \t} else {\n        \t    return false;\n        \t}\n        }\n        $pc = new Prpcrypt($this->encodingAesKey);\n        $array = $pc->decrypt($encryptStr,$this->appid);\n        if (!isset($array[0]) || ($array[0] != 0)) {\n            if (!$return) {\n        \t    die('解密失败！');\n        \t} else {\n        \t    return false;\n        \t}\n        }\n        if ($_SERVER['REQUEST_METHOD'] == \"POST\") {\n            $this->postxml = $array[1];\n            //$this->log($array[1]);\n            return ($this->postxml!=\"\");\n        } else {\n            $echoStr = $array[1];\n            if ($return) {\n            \treturn $echoStr;\n            } else {\n                die($echoStr);\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 获取微信服务器发来的信息\n     */\n\tpublic function getRev()\n\t{\n\t\tif ($this->_receive) return $this;\n\t\t$postStr = $this->postxml;\n\t\t$this->log($postStr);\n\t\tif (!empty($postStr)) {\n\t\t\t$this->_receive = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);\n\t\t\tif (!isset($this->_receive['AgentID'])) {\n\t\t\t     $this->_receive['AgentID']=$this->agentidxml; //当前接收消息的应用id\n\t\t\t}\n\t\t}\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 获取微信服务器发来的信息\n\t */\n\tpublic function getRevData()\n\t{\n\t\treturn $this->_receive;\n\t}\n\n\t/**\n\t * 获取微信服务器发来的原始加密信息\n\t */\n\tpublic function getRevPostXml()\n\t{\n\t    return $this->postxml;\n\t}\n\n\t/**\n\t * 获取消息发送者\n\t */\n\tpublic function getRevFrom() {\n\t\tif (isset($this->_receive['FromUserName']))\n\t\t\treturn $this->_receive['FromUserName'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取消息接受者\n\t */\n\tpublic function getRevTo() {\n\t\tif (isset($this->_receive['ToUserName']))\n\t\t\treturn $this->_receive['ToUserName'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收消息的应用id\n\t */\n\tpublic function getRevAgentID() {\n\t\tif (isset($this->_receive['AgentID']))\n\t\t\treturn $this->_receive['AgentID'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收消息的类型\n\t */\n\tpublic function getRevType() {\n\t\tif (isset($this->_receive['MsgType']))\n\t\t\treturn $this->_receive['MsgType'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取消息ID\n\t */\n\tpublic function getRevID() {\n\t\tif (isset($this->_receive['MsgId']))\n\t\t\treturn $this->_receive['MsgId'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取消息发送时间\n\t */\n\tpublic function getRevCtime() {\n\t\tif (isset($this->_receive['CreateTime']))\n\t\t\treturn $this->_receive['CreateTime'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收消息内容正文\n\t */\n\tpublic function getRevContent(){\n\t\tif (isset($this->_receive['Content']))\n\t\t\treturn $this->_receive['Content'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收消息图片\n\t */\n\tpublic function getRevPic(){\n\t\tif (isset($this->_receive['PicUrl']))\n\t\t\treturn array(\n\t\t\t\t'mediaid'=>$this->_receive['MediaId'],\n\t\t\t\t'picurl'=>(string)$this->_receive['PicUrl'],    //防止picurl为空导致解析出错\n\t\t\t);\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收地理位置\n\t */\n\tpublic function getRevGeo(){\n\t\tif (isset($this->_receive['Location_X'])){\n\t\t\treturn array(\n\t\t\t\t'x'=>$this->_receive['Location_X'],\n\t\t\t\t'y'=>$this->_receive['Location_Y'],\n\t\t\t\t'scale'=>(string)$this->_receive['Scale'],\n\t\t\t\t'label'=>(string)$this->_receive['Label']\n\t\t\t);\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取上报地理位置事件\n\t */\n\tpublic function getRevEventGeo(){\n        \tif (isset($this->_receive['Latitude'])){\n        \t\t return array(\n\t\t\t\t'x'=>$this->_receive['Latitude'],\n\t\t\t\t'y'=>$this->_receive['Longitude'],\n\t\t\t\t'precision'=>$this->_receive['Precision'],\n\t\t\t);\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收事件推送\n\t */\n\tpublic function getRevEvent(){\n\t\tif (isset($this->_receive['Event'])){\n\t\t\t$array['event'] = $this->_receive['Event'];\n\t\t}\n\t\tif (isset($this->_receive['EventKey']) && !empty($this->_receive['EventKey'])){\n\t\t\t$array['key'] = $this->_receive['EventKey'];\n\t\t}\n\t\tif (isset($array) && count($array) > 0) {\n\t\t\treturn $array;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * 获取自定义菜单的扫码推事件信息\n\t *\n\t * 事件类型为以下两种时则调用此方法有效\n\t * Event\t 事件类型，scancode_push\n\t * Event\t 事件类型，scancode_waitmsg\n\t *\n\t * @return: array | false\n\t * array (\n\t *     'ScanType'=>'qrcode',\n\t *     'ScanResult'=>'123123'\n\t * )\n\t */\n\tpublic function getRevScanInfo(){\n\t    if (isset($this->_receive['ScanCodeInfo'])){\n\t        if (!is_array($this->_receive['SendPicsInfo'])) {\n\t            $array=(array)$this->_receive['ScanCodeInfo'];\n\t            $this->_receive['ScanCodeInfo']=$array;\n\t        }else {\n\t            $array=$this->_receive['ScanCodeInfo'];\n\t        }\n\t    }\n\t    if (isset($array) && count($array) > 0) {\n\t        return $array;\n\t    } else {\n\t        return false;\n\t    }\n\t}\n\n\t/**\n\t * 获取自定义菜单的图片发送事件信息\n\t *\n\t * 事件类型为以下三种时则调用此方法有效\n\t * Event\t 事件类型，pic_sysphoto        弹出系统拍照发图的事件推送\n\t * Event\t 事件类型，pic_photo_or_album  弹出拍照或者相册发图的事件推送\n\t * Event\t 事件类型，pic_weixin          弹出微信相册发图器的事件推送\n\t *\n\t * @return: array | false\n\t * array (\n\t *   'Count' => '2',\n\t *   'PicList' =>array (\n\t *         'item' =>array (\n\t *             0 =>array ('PicMd5Sum' => 'aaae42617cf2a14342d96005af53624c'),\n\t *             1 =>array ('PicMd5Sum' => '149bd39e296860a2adc2f1bb81616ff8'),\n\t *         ),\n\t *   ),\n\t * )\n\t *\n\t */\n\tpublic function getRevSendPicsInfo(){\n\t    if (isset($this->_receive['SendPicsInfo'])){\n\t        if (!is_array($this->_receive['SendPicsInfo'])) {\n\t            $array=(array)$this->_receive['SendPicsInfo'];\n\t            if (isset($array['PicList'])){\n\t                $array['PicList']=(array)$array['PicList'];\n\t                $item=$array['PicList']['item'];\n\t                $array['PicList']['item']=array();\n\t                foreach ( $item as $key => $value ){\n\t                    $array['PicList']['item'][$key]=(array)$value;\n\t                }\n\t            }\n\t            $this->_receive['SendPicsInfo']=$array;\n\t        } else {\n\t            $array=$this->_receive['SendPicsInfo'];\n\t        }\n\t    }\n\t    if (isset($array) && count($array) > 0) {\n\t        return $array;\n\t    } else {\n\t        return false;\n\t    }\n\t}\n\n\t/**\n\t * 获取自定义菜单的地理位置选择器事件推送\n\t *\n\t * 事件类型为以下时则可以调用此方法有效\n\t * Event\t 事件类型，location_select        弹出系统拍照发图的事件推送\n\t *\n\t * @return: array | false\n\t * array (\n\t *   'Location_X' => '33.731655000061',\n\t *   'Location_Y' => '113.29955200008047',\n\t *   'Scale' => '16',\n\t *   'Label' => '某某市某某区某某路',\n\t *   'Poiname' => '',\n\t * )\n\t *\n\t */\n\tpublic function getRevSendGeoInfo(){\n\t    if (isset($this->_receive['SendLocationInfo'])){\n\t        if (!is_array($this->_receive['SendLocationInfo'])) {\n\t            $array=(array)$this->_receive['SendLocationInfo'];\n\t            if (empty($array['Poiname'])) {\n\t                $array['Poiname']=\"\";\n\t            }\n\t            if (empty($array['Label'])) {\n\t                $array['Label']=\"\";\n\t            }\n\t            $this->_receive['SendLocationInfo']=$array;\n\t        } else {\n\t            $array=$this->_receive['SendLocationInfo'];\n\t        }\n\t    }\n\t    if (isset($array) && count($array) > 0) {\n\t        return $array;\n\t    } else {\n\t        return false;\n\t    }\n\t}\n\n\t/**\n\t * 获取接收语音推送\n\t */\n\tpublic function getRevVoice(){\n\t\tif (isset($this->_receive['MediaId'])){\n\t\t\treturn array(\n\t\t\t\t'mediaid'=>$this->_receive['MediaId'],\n\t\t\t\t'format'=>$this->_receive['Format'],\n\t\t\t);\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收视频推送\n\t */\n\tpublic function getRevVideo(){\n\t\tif (isset($this->_receive['MediaId'])){\n\t\t\treturn array(\n\t\t\t\t\t'mediaid'=>$this->_receive['MediaId'],\n\t\t\t\t\t'thumbmediaid'=>$this->_receive['ThumbMediaId']\n\t\t\t);\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 设置回复消息\n\t * Example: $obj->text('hello')->reply();\n\t * @param string $text\n\t */\n\tpublic function text($text='')\n\t{\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'MsgType'=>self::MSGTYPE_TEXT,\n\t\t\t'Content'=>$this->_auto_text_filter($text),\n\t\t\t'CreateTime'=>time(),\n\t\t);\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 设置回复消息\n\t * Example: $obj->image('media_id')->reply();\n\t * @param string $mediaid\n\t */\n\tpublic function image($mediaid='')\n\t{\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'MsgType'=>self::MSGTYPE_IMAGE,\n\t\t\t'Image'=>array('MediaId'=>$mediaid),\n\t\t\t'CreateTime'=>time(),\n\t\t);\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 设置回复消息\n\t * Example: $obj->voice('media_id')->reply();\n\t * @param string $mediaid\n\t */\n\tpublic function voice($mediaid='')\n\t{\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'MsgType'=>self::MSGTYPE_IMAGE,\n\t\t\t'Voice'=>array('MediaId'=>$mediaid),\n\t\t\t'CreateTime'=>time(),\n\t\t);\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 设置回复消息\n\t * Example: $obj->video('media_id','title','description')->reply();\n\t * @param string $mediaid\n\t */\n\tpublic function video($mediaid='',$title='',$description='')\n\t{\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'MsgType'=>self::MSGTYPE_IMAGE,\n\t\t\t'Video'=>array(\n\t\t\t        'MediaId'=>$mediaid,\n\t\t\t        'Title'=>$title,\n\t\t\t        'Description'=>$description\n\t\t\t),\n\t\t\t'CreateTime'=>time(),\n\t\t);\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 设置回复图文\n\t * @param array $newsData\n\t * 数组结构:\n\t *  array(\n\t *  \t\"0\"=>array(\n\t *  \t\t'Title'=>'msg title',\n\t *  \t\t'Description'=>'summary text',\n\t *  \t\t'PicUrl'=>'http://www.domain.com/1.jpg',\n\t *  \t\t'Url'=>'http://www.domain.com/1.html'\n\t *  \t),\n\t *  \t\"1\"=>....\n\t *  )\n\t */\n\tpublic function news($newsData=array())\n\t{\n\n\t\t$count = count($newsData);\n\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'MsgType'=>self::MSGTYPE_NEWS,\n\t\t\t'CreateTime'=>time(),\n\t\t\t'ArticleCount'=>$count,\n\t\t\t'Articles'=>$newsData,\n\n\t\t);\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 设置发送消息\n\t * @param array $msg 消息数组\n\t * @param bool $append 是否在原消息数组追加\n\t */\n\tpublic function Message($msg = '',$append = false){\n\t    if (is_null($msg)) {\n\t        $this->_msg =array();\n\t    }elseif (is_array($msg)) {\n\t        if ($append)\n\t            $this->_msg = array_merge($this->_msg,$msg);\n\t        else\n\t            $this->_msg = $msg;\n\t        return $this->_msg;\n\t    } else {\n\t        return $this->_msg;\n\t    }\n\t}\n\n\t/**\n\t *\n\t * 回复微信服务器, 此函数支持链式操作\n\t * Example: $this->text('msg tips')->reply();\n\t * @param string $msg 要发送的信息, 默认取$this->_msg\n\t * @param bool $return 是否返回信息而不抛出到浏览器 默认:否\n\t */\n\tpublic function reply($msg=array(),$return = false)\n\t{\n\t\tif (empty($msg))\n\t\t\t$msg = $this->_msg;\n\t\t$xmldata=  $this->xml_encode($msg);\n\t\t$this->log($xmldata);\n\t\t$pc = new Prpcrypt($this->encodingAesKey);\n\t\t$array = $pc->encrypt($xmldata, $this->appid);\n\t\t$ret = $array[0];\n\t\tif ($ret != 0) {\n\t\t    $this->log('encrypt err!');\n\t\t    return false;\n\t\t}\n\t\t$timestamp = time();\n\t\t$nonce = rand(77,999)*rand(605,888)*rand(11,99);\n\t\t$encrypt = $array[1];\n\t\t$tmpArr = array($this->token, $timestamp, $nonce,$encrypt);//比普通公众平台多了一个加密的密文\n\t\tsort($tmpArr, SORT_STRING);\n\t\t$signature = implode($tmpArr);\n\t\t$signature = sha1($signature);\n\t\t$smsg = $this->generate($encrypt, $signature, $timestamp, $nonce);\n\t\t$this->log($smsg);\n\t\tif ($return)\n\t\t    return $smsg;\n\t\telseif ($smsg){\n\t\t\techo $smsg;\n\t\t    return true;\n\n\t\t}else\n\t\t    return false;\n\t}\n\n\tprivate function generate($encrypt, $signature, $timestamp, $nonce)\n\t{\n\t    //格式化加密信息\n\t    $format = \"<xml>\n<Encrypt><![CDATA[%s]]></Encrypt>\n<MsgSignature><![CDATA[%s]]></MsgSignature>\n<TimeStamp>%s</TimeStamp>\n<Nonce><![CDATA[%s]]></Nonce>\n</xml>\";\n\t    return sprintf($format, $encrypt, $signature, $timestamp, $nonce);\n\t}\n\n\t/**\n\t * 设置缓存，按需重载\n\t * @param string $cachename\n\t * @param mixed $value\n\t * @param int $expired\n\t * @return boolean\n\t */\n\tprotected function setCache($cachename,$value,$expired){\n\t\t//TODO: set cache implementation\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取缓存，按需重载\n\t * @param string $cachename\n\t * @return mixed\n\t */\n\tprotected function getCache($cachename){\n\t\t//TODO: get cache implementation\n\t\treturn false;\n\t}\n\n\t/**\n\t * 清除缓存，按需重载\n\t * @param string $cachename\n\t * @return boolean\n\t */\n\tprotected function removeCache($cachename){\n\t\t//TODO: remove cache implementation\n\t\treturn false;\n\t}\n\n\t/**\n\t * 通用auth验证方法\n\t * @param string $appid\n\t * @param string $appsecret\n\t * @param string $token 手动指定access_token，非必要情况不建议用\n\t */\n\tpublic function checkAuth($appid='',$appsecret='',$token=''){\n\t\tif (!$appid || !$appsecret) {\n\t\t\t$appid = $this->appid;\n\t\t\t$appsecret = $this->appsecret;\n\t\t}\n\t\tif ($token) { //手动指定token，优先使用\n\t\t    $this->access_token=$token;\n\t\t    return $this->access_token;\n\t\t}\n\n\t\t$authname = 'qywechat_access_token'.$appid;\n\t\tif ($rs = $this->getCache($authname))  {\n\t\t\t$this->access_token = $rs;\n\t\t\treturn $rs;\n\t\t}\n\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::TOKEN_GET_URL.'corpid='.$appid.'&corpsecret='.$appsecret);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$this->access_token = $json['access_token'];\n\t\t\t$expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;\n\t\t\t$this->setCache($authname,$this->access_token,$expire);\n\t\t\treturn $this->access_token;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 删除验证数据\n\t * @param string $appid\n\t */\n\tpublic function resetAuth($appid=''){\n\t\tif (!$appid) $appid = $this->appid;\n\t\t$this->access_token = '';\n\t\t$authname = 'qywechat_access_token'.$appid;\n\t\t$this->removeCache($authname);\n\t\treturn true;\n\t}\n\n\t/**\n\t * 删除JSAPI授权TICKET\n\t * @param string $appid 用于多个appid时使用\n\t */\n\tpublic function resetJsTicket($appid=''){\n\t\tif (!$appid) $appid = $this->appid;\n\t\t$this->jsapi_ticket = '';\n\t\t$authname = 'qywechat_jsapi_ticket'.$appid;\n\t\t$this->removeCache($authname);\n\t\treturn true;\n\t}\n\n\t/**\n\t * 获取JSAPI授权TICKET\n\t * @param string $appid 用于多个appid时使用,可空\n\t * @param string $jsapi_ticket 手动指定jsapi_ticket，非必要情况不建议用\n\t */\n\tpublic function getJsTicket($appid='',$jsapi_ticket=''){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\tif (!$appid) $appid = $this->appid;\n\t\tif ($jsapi_ticket) { //手动指定token，优先使用\n\t\t    $this->jsapi_ticket = $jsapi_ticket;\n\t\t    return $this->jsapi_ticket;\n\t\t}\n\t\t$authname = 'qywechat_jsapi_ticket'.$appid;\n\t\tif ($rs = $this->getCache($authname))  {\n\t\t\t$this->jsapi_ticket = $rs;\n\t\t\treturn $rs;\n\t\t}\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::TICKET_GET_URL.'access_token='.$this->access_token);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$this->jsapi_ticket = $json['ticket'];\n\t\t\t$expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;\n\t\t\t$this->setCache($authname, $this->jsapi_ticket, $expire);\n\t\t\treturn $this->jsapi_ticket;\n\t\t}\n\t\treturn false;\n\t}\n\n\n\t/**\n\t * 获取JsApi使用签名\n\t * @param string $url 网页的URL，自动处理#及其后面部分\n\t * @param string $timestamp 当前时间戳 (为空则自动生成)\n\t * @param string $noncestr 随机串 (为空则自动生成)\n\t * @param string $appid 用于多个appid时使用,可空\n\t * @return array|bool 返回签名字串\n\t */\n\tpublic function getJsSign($url, $timestamp=0, $noncestr='', $appid=''){\n\t    if (!$this->jsapi_ticket && !$this->getJsTicket($appid) || !$url) return false;\n\t    if (!$timestamp)\n\t        $timestamp = time();\n\t    if (!$noncestr)\n\t        $noncestr = $this->generateNonceStr();\n\t    $ret = strpos($url,'#');\n\t    if ($ret)\n\t        $url = substr($url,0,$ret);\n\t    $url = trim($url);\n\t    if (empty($url))\n\t        return false;\n\t    $arrdata = array(\"timestamp\" => $timestamp, \"noncestr\" => $noncestr, \"url\" => $url, \"jsapi_ticket\" => $this->jsapi_ticket);\n\t    $sign = $this->getSignature($arrdata);\n\t    if (!$sign)\n\t        return false;\n\t    $signPackage = array(\n\t            \"appid\"     => $this->appid,\n\t            \"noncestr\"  => $noncestr,\n\t            \"timestamp\" => $timestamp,\n\t            \"url\"       => $url,\n\t            \"signature\" => $sign\n\t    );\n\t    return $signPackage;\n\t}\n\n\t/**\n\t * 获取签名\n\t * @param array $arrdata 签名数组\n\t * @param string $method 签名方法\n\t * @return boolean|string 签名值\n\t */\n\tpublic function getSignature($arrdata,$method=\"sha1\") {\n\t\tif (!function_exists($method)) return false;\n\t\tksort($arrdata);\n\t\t$paramstring = \"\";\n\t\tforeach($arrdata as $key => $value)\n\t\t{\n\t\t\tif(strlen($paramstring) == 0)\n\t\t\t\t$paramstring .= $key . \"=\" . $value;\n\t\t\telse\n\t\t\t\t$paramstring .= \"&\" . $key . \"=\" . $value;\n\t\t}\n\t\t$Sign = $method($paramstring);\n\t\treturn $Sign;\n\t}\n\n\t/**\n\t * 生成随机字串\n\t * @param number $length 长度，默认为16，最长为32字节\n\t * @return string\n\t */\n\tpublic function generateNonceStr($length=16){\n\t\t// 密码字符集，可任意添加你需要的字符\n\t\t$chars = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\n\t\t$str = \"\";\n\t\tfor($i = 0; $i < $length; $i++)\n\t\t{\n\t\t\t$str .= $chars[mt_rand(0, strlen($chars) - 1)];\n\t\t}\n\t\treturn $str;\n\t}\n\n\n\t/**\n\t * 创建菜单\n\t * @param array $data 菜单数组数据\n\t * example:\n     * \tarray (\n     * \t    'button' => array (\n     * \t      0 => array (\n     * \t        'name' => '扫码',\n     * \t        'sub_button' => array (\n     * \t            0 => array (\n     * \t              'type' => 'scancode_waitmsg',\n     * \t              'name' => '扫码带提示',\n     * \t              'key' => 'rselfmenu_0_0',\n     * \t            ),\n     * \t            1 => array (\n     * \t              'type' => 'scancode_push',\n     * \t              'name' => '扫码推事件',\n     * \t              'key' => 'rselfmenu_0_1',\n     * \t            ),\n     * \t        ),\n     * \t      ),\n     * \t      1 => array (\n     * \t        'name' => '发图',\n     * \t        'sub_button' => array (\n     * \t            0 => array (\n     * \t              'type' => 'pic_sysphoto',\n     * \t              'name' => '系统拍照发图',\n     * \t              'key' => 'rselfmenu_1_0',\n     * \t            ),\n     * \t            1 => array (\n     * \t              'type' => 'pic_photo_or_album',\n     * \t              'name' => '拍照或者相册发图',\n     * \t              'key' => 'rselfmenu_1_1',\n     * \t            )\n     * \t        ),\n     * \t      ),\n     * \t      2 => array (\n     * \t        'type' => 'location_select',\n     * \t        'name' => '发送位置',\n     * \t        'key' => 'rselfmenu_2_0'\n     * \t      ),\n     * \t    ),\n     * \t)\n     * type可以选择为以下几种，会收到相应类型的事件推送。请注意，3到8的所有事件，仅支持微信iPhone5.4.1以上版本，\n     * 和Android5.4以上版本的微信用户，旧版本微信用户点击后将没有回应，开发者也不能正常接收到事件推送。\n     * 1、click：点击推事件\n     * 2、view：跳转URL\n     * 3、scancode_push：扫码推事件\n     * 4、scancode_waitmsg：扫码推事件且弹出“消息接收中”提示框\n     * 5、pic_sysphoto：弹出系统拍照发图\n     * 6、pic_photo_or_album：弹出拍照或者相册发图\n     * 7、pic_weixin：弹出微信相册发图器\n     * 8、location_select：弹出地理位置选择器\n\t */\n\tpublic function createMenu($data,$agentid=''){\n\t    if ($agentid=='') {\n\t    \t$agentid=$this->agentid;\n\t    }\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MENU_CREATE_URL.'access_token='.$this->access_token.'&agentid='.$agentid,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取菜单\n\t * @return array('menu'=>array(....s))\n\t */\n\tpublic function getMenu($agentid=''){\n\t    if ($agentid=='') {\n\t    \t$agentid=$this->agentid;\n\t    }\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::MENU_GET_URL.'access_token='.$this->access_token.'&agentid='.$agentid);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || isset($json['errcode']) || $json['errcode']!=0) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 删除菜单\n\t * @return boolean\n\t */\n\tpublic function deleteMenu($agentid=''){\n\t    if ($agentid=='') {\n\t    \t$agentid=$this->agentid;\n\t    }\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::MENU_DELETE_URL.'access_token='.$this->access_token.'&agentid='.$agentid);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 上传多媒体文件 (只有三天的有效期，过期自动被删除)\n\t * 注意：上传大文件时可能需要先调用 set_time_limit(0) 避免超时\n\t * 注意：数组的键值任意，但文件名前必须加@，使用单引号以避免本地路径斜杠被转义\n\t * @param array $data {\"media\":'@Path\\filename.jpg'}\n\t * @param type 媒体文件类型:图片（image）、语音（voice）、视频（video），普通文件(file)\n\t * @return boolean|array\n\t * {\n\t *    \"type\": \"image\",\n\t *    \"media_id\": \"0000001\",\n\t *    \"created_at\": \"1380000000\"\n\t * }\n\t */\n\tpublic function uploadMedia($data, $type){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_UPLOAD_URL.'access_token='.$this->access_token.'&type='.$type,$data,true);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 根据媒体文件ID获取媒体文件\n\t * @param string $media_id 媒体文件id\n\t * @return raw data\n\t */\n\tpublic function getMedia($media_id){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::MEDIA_GET_URL.'access_token='.$this->access_token.'&media_id='.$media_id);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $result;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取企业微信服务器IP地址列表\n\t * @return array('127.0.0.1','127.0.0.1')\n\t */\n\tpublic function getServerIp(){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::CALLBACKSERVER_GET_URL.'access_token='.$this->access_token);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json['ip_list'];\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 创建部门\n\t * @param array $data \t结构体为:\n\t * array (\n\t *     \"name\" => \"邮箱产品组\",   //部门名称\n\t *     \"parentid\" => \"1\"         //父部门id\n\t *     \"order\" =>  \"1\",            //(非必须)在父部门中的次序。从1开始，数字越大排序越靠后\n\t * )\n\t * @return boolean|array\n\t * 成功返回结果\n\t * {\n \t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"created\",  //对返回码的文本描述内容\n \t *   \"id\": 2               //创建的部门id。\n\t * }\n\t */\n\tpublic function createDepartment($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::DEPARTMENT_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\n\t/**\n\t * 更新部门\n\t * @param array $data \t结构体为:\n\t * array(\n\t *     \"id\" => \"1\"               //(必须)部门id\n\t *     \"name\" =>  \"邮箱产品组\",   //(非必须)部门名称\n\t *     \"parentid\" =>  \"1\",         //(非必须)父亲部门id。根部门id为1\n\t *     \"order\" =>  \"1\",            //(非必须)在父部门中的次序。从1开始，数字越大排序越靠后\n\t * )\n\t * @return boolean|array 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"updated\"  //对返回码的文本描述内容\n\t * }\n\t */\n\tpublic function updateDepartment($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::DEPARTMENT_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 删除部门\n\t * @param $id\n\t * @return boolean|array 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"deleted\"  //对返回码的文本描述内容\n\t * }\n\t */\n\tpublic function deleteDepartment($id){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::DEPARTMENT_DELETE_URL.'access_token='.$this->access_token.'&id='.$id);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 移动部门\n\t * @param $data\n\t * array(\n\t *    \"department_id\" => \"5\",\t//所要移动的部门\n\t *    \"to_parentid\" => \"2\",\t\t//想移动到的父部门节点，根部门为1\n\t *    \"to_position\" => \"1\"\t\t//(非必须)想移动到的父部门下的位置，1表示最上方，往后位置为2，3，4，以此类推，默认为1\n\t * )\n\t * @return boolean|array 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"ok\"  //对返回码的文本描述内容\n\t * }\n\t */\n\tpublic function moveDepartment($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::DEPARTMENT_MOVE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取部门列表\n\t * @return boolean|array\t 成功返回结果\n\t * {\n\t *    \"errcode\": 0,\n\t *    \"errmsg\": \"ok\",\n\t *    \"department\": [          //部门列表数据。以部门的order字段从小到大排列\n\t *        {\n\t *            \"id\": 1,\n\t *            \"name\": \"广州研发中心\",\n\t *            \"parentid\": 0,\n\t *            \"order\": 40\n\t *        },\n \t *       {\n\t *          \"id\": 2\n  \t *          \"name\": \"邮箱产品部\",\n  \t *          \"parentid\": 1,\n\t *          \"order\": 40\n \t *       }\n\t *    ]\n\t * }\n\t */\n\tpublic function getDepartment(){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::DEPARTMENT_LIST_URL.'access_token='.$this->access_token);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 创建成员\n\t * @param array $data \t结构体为:\n     * array(\n     *    \"userid\" => \"zhangsan\",\n     *    \"name\" => \"张三\",\n     *    \"department\" => [1, 2],\n     *    \"position\" => \"产品经理\",\n     *    \"mobile\" => \"15913215421\",\n     *    \"gender\" => 1,     //性别。gender=0表示男，=1表示女\n     *    \"tel\" => \"62394\",\n     *    \"email\" => \"zhangsan@gzdev.com\",\n     *    \"weixinid\" => \"zhangsan4dev\"\n     * )\n\t * @return boolean|array\n\t * 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"created\",  //对返回码的文本描述内容\n\t * }\n\t */\n\tpublic function createUser($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::USER_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\n\t/**\n\t * 更新成员\n\t * @param array $data \t结构体为:\n     * array(\n     *    \"userid\" => \"zhangsan\",\n     *    \"name\" => \"张三\",\n     *    \"department\" => [1, 2],\n     *    \"position\" => \"产品经理\",\n     *    \"mobile\" => \"15913215421\",\n     *    \"gender\" => 1,     //性别。gender=0表示男，=1表示女\n     *    \"tel\" => \"62394\",\n     *    \"email\" => \"zhangsan@gzdev.com\",\n     *    \"weixinid\" => \"zhangsan4dev\"\n     * )\n\t * @return boolean|array 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"updated\"  //对返回码的文本描述内容\n\t * }\n\t */\n\tpublic function updateUser($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::USER_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 删除成员\n\t * @param $userid  员工UserID。对应管理端的帐号\n\t * @return boolean|array 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"deleted\"  //对返回码的文本描述内容\n\t * }\n\t */\n\tpublic function deleteUser($userid){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::USER_DELETE_URL.'access_token='.$this->access_token.'&userid='.$userid);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 批量删除成员\n\t * @param array $userid  员工UserID数组。对应管理端的帐号\n\t * {\n\t *     'userid1',\n\t *     'userid2',\n\t *     'userid3',\n\t * }\n\t * @return boolean|array 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"deleted\"  //对返回码的文本描述内容\n\t * }\n\t */\n\tpublic function deleteUsers($userids){\n\t    if (!$userids) return false;\n\t    $data = array('useridlist'=>$userids);\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::USER_BATCHDELETE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取成员信息\n\t * @param $userid  员工UserID。对应管理端的帐号\n\t * @return boolean|array\t 成功返回结果\n\t * {\n\t *    \"errcode\": 0,\n\t *    \"errmsg\": \"ok\",\n\t *    \"userid\": \"zhangsan\",\n\t *    \"name\": \"李四\",\n\t *    \"department\": [1, 2],\n\t *    \"position\": \"后台工程师\",\n\t *    \"mobile\": \"15913215421\",\n\t *    \"gender\": 1,     //性别。gender=0表示男，=1表示女\n\t *    \"tel\": \"62394\",\n\t *    \"email\": \"zhangsan@gzdev.com\",\n\t *    \"weixinid\": \"lisifordev\",        //微信号\n\t *    \"avatar\": \"http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3W..../0\",   //头像url。注：如果要获取小图将url最后的\"/0\"改成\"/64\"即可\n\t *    \"status\": 1      //关注状态: 1=已关注，2=已冻结，4=未关注\n\t *    \"extattr\": {\"attrs\":[{\"name\":\"爱好\",\"value\":\"旅游\"},{\"name\":\"卡号\",\"value\":\"1234567234\"}]}\n\t * }\n\t */\n\tpublic function getUserInfo($userid){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::USER_GET_URL.'access_token='.$this->access_token.'&userid='.$userid);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取部门成员\n\t * @param $department_id   部门id\n\t * @param $fetch_child     1/0：是否递归获取子部门下面的成员\n\t * @param $status          0获取全部员工，1获取已关注成员列表，2获取禁用成员列表，4获取未关注成员列表。status可叠加\n\t * @return boolean|array\t 成功返回结果\n\t * {\n\t *    \"errcode\": 0,\n\t *    \"errmsg\": \"ok\",\n\t *    \"userlist\": [\n\t *            {\n\t *                   \"userid\": \"zhangsan\",\n\t *                   \"name\": \"李四\"\n\t *            }\n\t *      ]\n\t * }\n\t */\n\tpublic function getUserList($department_id,$fetch_child=0,$status=0){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::USER_LIST_URL.'access_token='.$this->access_token\n\t            .'&department_id='.$department_id.'&fetch_child='.$fetch_child.'&status='.$status);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取部门成员详情\n\t * @param $department_id   部门id\n\t * @param $fetch_child     1/0：是否递归获取子部门下面的成员\n\t * @param $status          0获取全部员工，1获取已关注成员列表，2获取禁用成员列表，4获取未关注成员列表。status可叠加\n\t * @return boolean|array\t 成功返回结果\n\t * {\n\t *    \"errcode\": 0,\n\t *    \"errmsg\": \"ok\",\n\t *    \"userlist\": [\n\t *            {\n\t *                   \"userid\": \"zhangsan\",\n\t *                   \"name\": \"李四\",\n\t *                   \"department\": [1, 2],\n\t *                   \"position\": \"后台工程师\",\n\t *                   \"mobile\": \"15913215421\",\n\t *                   \"gender\": 1,     //性别。gender=0表示男，=1表示女\n\t *                   \"tel\": \"62394\",\n\t *                   \"email\": \"zhangsan@gzdev.com\",\n\t *                   \"weixinid\": \"lisifordev\",        //微信号\n\t *                   \"avatar\": \"http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3W..../0\",   //头像url。注：如果要获取小图将url最后的\"/0\"改成\"/64\"即可\n\t *                   \"status\": 1      //关注状态: 1=已关注，2=已冻结，4=未关注\n\t *                   \"extattr\": {\"attrs\":[{\"name\":\"爱好\",\"value\":\"旅游\"},{\"name\":\"卡号\",\"value\":\"1234567234\"}]}\n\t *            }\n\t *      ]\n\t * }\n\t */\n\tpublic function getUserListInfo($department_id,$fetch_child=0,$status=0){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::USER_LIST_INFO_URL.'access_token='.$this->access_token\n\t            .'&department_id='.$department_id.'&fetch_child='.$fetch_child.'&status='.$status);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 根据code获取成员信息\n\t * 通过Oauth2.0或者设置了二次验证时获取的code，用于换取成员的UserId和DeviceId\n\t *\n\t * @param $code        Oauth2.0或者二次验证时返回的code值\n\t * @param $agentid     跳转链接时所在的企业应用ID，未填则默认为当前配置的应用id\n\t * @return boolean|array 成功返回数组\n\t * array(\n\t *     'UserId' => 'USERID',       //员工UserID\n\t *     'DeviceId' => 'DEVICEID'    //手机设备号(由微信在安装时随机生成)\n\t * )\n\t */\n\tpublic function getUserId($code,$agentid=0){\n\t    if (!$agentid) $agentid=$this->agentid;\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::USER_GETINFO_URL.'access_token='.$this->access_token.'&code='.$code.'&agentid='.$agentid);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 邀请成员关注\n\t * 向未关注企业号的成员发送关注邀请。认证号优先判断顺序weixinid>手机号>邮箱绑定>邮件；非认证号只能邮件邀请\n\t *\n\t * @param $userid        用户的userid\n\t * @param $invite_tips   推送到微信上的提示语（只有认证号可以使用）。当使用微信推送时，该字段默认为“请关注XXX企业号”，邮件邀请时，该字段无效。\n\t * @return boolean|array 成功返回数组\n\t * array(\n\t *     'errcode' => 0,\n\t *     'errmsg' => 'ok',\n\t *     'type' => 1         //邀请方式 1.微信邀请 2.邮件邀请\n\t * )\n\t */\n\tpublic function sendInvite($userid,$invite_tips=''){\n\t    $data = array( 'userid' => $userid );\n\t    if (!$invite_tips) {\n\t    \t$data['invite_tips'] = $invite_tips;\n\t    }\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::USER_INVITE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 创建标签\n\t * @param array $data \t结构体为:\n\t * array(\n\t *    \"tagname\" => \"UI\"\n\t * )\n\t * @return boolean|array\n\t * 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"created\",  //对返回码的文本描述内容\n\t *   \"tagid\": \"1\"\n\t * }\n\t */\n\tpublic function createTag($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::TAG_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 更新标签\n\t * @param array $data \t结构体为:\n\t * array(\n\t *    \"tagid\" => \"1\",\n\t *    \"tagname\" => \"UI design\"\n\t * )\n\t * @return boolean|array 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"updated\"  //对返回码的文本描述内容\n\t * }\n\t */\n\tpublic function updateTag($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::TAG_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 删除标签\n\t * @param $tagid  标签TagID\n\t * @return boolean|array 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"deleted\"  //对返回码的文本描述内容\n\t * }\n\t */\n\tpublic function deleteTag($tagid){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::TAG_DELETE_URL.'access_token='.$this->access_token.'&tagid='.$tagid);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取标签成员\n\t * @param $tagid  标签TagID\n\t * @return boolean|array\t 成功返回结果\n\t * {\n\t *    \"errcode\": 0,\n\t *    \"errmsg\": \"ok\",\n\t *    \"userlist\": [\n\t *          {\n\t *              \"userid\": \"zhangsan\",\n\t *              \"name\": \"李四\"\n\t *          }\n\t *      ]\n\t * }\n\t */\n\tpublic function getTag($tagid){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::TAG_GET_URL.'access_token='.$this->access_token.'&tagid='.$tagid);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 增加标签成员\n\t * @param array $data \t结构体为:\n\t * array (\n\t *    \"tagid\" => \"1\",\n\t *    \"userlist\" => array(    //企业员工ID列表\n\t *         \"user1\",\n\t *         \"user2\"\n\t *     )\n\t * )\n\t * @return boolean|array\n\t * 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"ok\",  //对返回码的文本描述内容\n\t *   \"invalidlist\"：\"usr1|usr2|usr\"     //若部分userid非法，则会有此段。不在权限内的员工ID列表，以“|”分隔\n\t * }\n\t */\n\tpublic function addTagUser($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::TAG_ADDUSER_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 删除标签成员\n\t * @param array $data \t结构体为:\n\t * array (\n\t *    \"tagid\" => \"1\",\n\t *    \"userlist\" => array(    //企业员工ID列表\n\t *         \"user1\",\n\t *         \"user2\"\n\t *     )\n\t * )\n\t * @return boolean|array\n\t * 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"deleted\",  //对返回码的文本描述内容\n\t *   \"invalidlist\"：\"usr1|usr2|usr\"     //若部分userid非法，则会有此段。不在权限内的员工ID列表，以“|”分隔\n\t * }\n\t */\n\tpublic function delTagUser($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::TAG_DELUSER_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取标签列表\n\t * @return boolean|array\t 成功返回数组结果，这里附上json样例\n\t * {\n\t *    \"errcode\": 0,\n\t *    \"errmsg\": \"ok\",\n\t *    \"taglist\":[\n\t *       {\"tagid\":1,\"tagname\":\"a\"},\n\t *       {\"tagid\":2,\"tagname\":\"b\"}\n\t *    ]\n\t * }\n\t */\n\tpublic function getTagList(){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::TAG_LIST_URL.'access_token='.$this->access_token);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 主动发送信息接口\n\t * @param array $data \t结构体为:\n\t * array(\n\t *         \"touser\" => \"UserID1|UserID2|UserID3\",\n\t *         \"toparty\" => \"PartyID1|PartyID2 \",\n\t *         \"totag\" => \"TagID1|TagID2 \",\n\t *         \"safe\":\"0\"\t\t\t//是否为保密消息，对于news无效\n\t *         \"agentid\" => \"001\",\t//应用id\n\t *         \"msgtype\" => \"text\",  //根据信息类型，选择下面对应的信息结构体\n\t *\n\t *         \"text\" => array(\n\t *                 \"content\" => \"Holiday Request For Pony(http://xxxxx)\"\n\t *         ),\n\t *\n\t *         \"image\" => array(\n\t *                 \"media_id\" => \"MEDIA_ID\"\n\t *         ),\n\t *\n\t *         \"voice\" => array(\n\t *                 \"media_id\" => \"MEDIA_ID\"\n\t *         ),\n\t *\n\t *         \" video\" => array(\n\t *                 \"media_id\" => \"MEDIA_ID\",\n\t *                 \"title\" => \"Title\",\n\t *                 \"description\" => \"Description\"\n\t *         ),\n\t *\n\t *         \"file\" => array(\n\t *                 \"media_id\" => \"MEDIA_ID\"\n\t *         ),\n\t *\n\t *         \"news\" => array(\t\t\t//不支持保密\n\t *                 \"articles\" => array(    //articles  图文消息，一个图文消息支持1到10个图文\n\t *                     array(\n\t *                         \"title\" => \"Title\",             //标题\n\t *                         \"description\" => \"Description\", //描述\n\t *                         \"url\" => \"URL\",                 //点击后跳转的链接。可根据url里面带的code参数校验员工的真实身份。\n\t *                         \"picurl\" => \"PIC_URL\",          //图文消息的图片链接,支持JPG、PNG格式，较好的效果为大图640*320，\n\t *                                                         //小图80*80。如不填，在客户端不显示图片\n\t *                     ),\n\t *                 )\n\t *         ),\n\t *\n\t *         \"mpnews\" => array(\n\t *                 \"articles\" => array(    //articles  图文消息，一个图文消息支持1到10个图文\n\t *                     array(\n\t *                         \"title\" => \"Title\",             //图文消息的标题\n\t *                         \"thumb_media_id\" => \"id\",       //图文消息缩略图的media_id\n\t *                         \"author\" => \"Author\",           //图文消息的作者(可空)\n\t *                         \"content_source_url\" => \"URL\",  //图文消息点击“阅读原文”之后的页面链接(可空)\n\t *                         \"content\" => \"Content\"          //图文消息的内容，支持html标签\n\t *                         \"digest\" => \"Digest description\",   //图文消息的描述\n\t *                         \"show_cover_pic\" => \"0\"         //是否显示封面，1为显示，0为不显示(可空)\n\t *                     ),\n\t *                 )\n\t *         )\n\t * )\n\t * 请查看官方开发文档中的 发送消息 -> 消息类型及数据格式\n\t *\n\t * @return boolean|array\n\t * 如果对应用或收件人、部门、标签任何一个无权限，则本次发送失败；\n\t * 如果收件人、部门或标签不存在，发送仍然执行，但返回无效的部分。\n\t * {\n\t *    \"errcode\": 0,\n\t *    \"errmsg\": \"ok\",\n\t *    \"invaliduser\": \"UserID1\",\n\t *    \"invalidparty\":\"PartyID1\",\n\t *    \"invalidtag\":\"TagID1\"\n\t * }\n\t */\n\tpublic function sendMessage($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::MASS_SEND_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 二次验证\n\t * 企业在开启二次验证时，必须填写企业二次验证页面的url。\n\t * 当员工绑定通讯录中的帐号后，会收到一条图文消息，\n\t * 引导员工到企业的验证页面验证身份，企业在员工验证成功后，\n\t * 调用如下接口即可让员工关注成功。\n\t *\n\t * @param $userid\n\t * @return boolean|array 成功返回结果\n\t * {\n\t *   \"errcode\": 0,        //返回码\n\t *   \"errmsg\": \"ok\"  //对返回码的文本描述内容\n\t * }\n\t */\n\tpublic function authSucc($userid){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::AUTHSUCC_URL.'access_token='.$this->access_token.'&userid='.$userid);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * oauth 授权跳转接口\n\t * @param string $callback 回调URI\n\t * @param string $state 重定向后会带上state参数，企业可以填写a-zA-Z0-9的参数值\n\t * @return string\n\t */\n\tpublic function getOauthRedirect($callback,$state='STATE',$scope='snsapi_base'){\n\t    return self::OAUTH_PREFIX.self::OAUTH_AUTHORIZE_URL.'appid='.$this->appid.'&redirect_uri='.urlencode($callback).'&response_type=code&scope='.$scope.'&state='.$state.'#wechat_redirect';\n\t}\n\n}\n\n\n\n/**\n * PKCS7Encoder class\n *\n * 提供基于PKCS7算法的加解密接口.\n */\nclass PKCS7Encoder\n{\n    public static $block_size = 32;\n\n    /**\n     * 对需要加密的明文进行填充补位\n     * @param $text 需要进行填充补位操作的明文\n     * @return 补齐明文字符串\n     */\n    function encode($text)\n    {\n        $block_size = PKCS7Encoder::$block_size;\n        $text_length = strlen($text);\n        //计算需要填充的位数\n        $amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size);\n        if ($amount_to_pad == 0) {\n            $amount_to_pad = PKCS7Encoder::block_size;\n        }\n        //获得补位所用的字符\n        $pad_chr = chr($amount_to_pad);\n        $tmp = \"\";\n        for ($index = 0; $index < $amount_to_pad; $index++) {\n            $tmp .= $pad_chr;\n        }\n        return $text . $tmp;\n    }\n\n    /**\n     * 对解密后的明文进行补位删除\n     * @param decrypted 解密后的明文\n     * @return 删除填充补位后的明文\n     */\n    function decode($text)\n    {\n\n        $pad = ord(substr($text, -1));\n        if ($pad < 1 || $pad > PKCS7Encoder::$block_size) {\n            $pad = 0;\n        }\n        return substr($text, 0, (strlen($text) - $pad));\n    }\n\n}\n\n/**\n * Prpcrypt class\n *\n * 提供接收和推送给公众平台消息的加解密接口.\n */\nclass Prpcrypt\n{\n    public $key;\n\n    function __construct($k) {\n        $this->key = base64_decode($k . \"=\");\n    }\n\n    /**\n     * 兼容老版本php构造函数，不能在 __construct() 方法前边，否则报错\n     */\n    function Prpcrypt($k)\n    {\n        $this->key = base64_decode($k . \"=\");\n    }\n\n    /**\n     * 对明文进行加密\n     * @param string $text 需要加密的明文\n     * @return string 加密后的密文\n     */\n    public function encrypt($text, $appid)\n    {\n\n        try {\n            //获得16位随机字符串，填充到明文之前\n            $random = $this->getRandomStr();//\"aaaabbbbccccdddd\";\n            $text = $random . pack(\"N\", strlen($text)) . $text . $appid;\n            // 网络字节序\n            $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);\n            $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');\n            $iv = substr($this->key, 0, 16);\n            //使用自定义的填充方式对明文进行补位填充\n            $pkc_encoder = new PKCS7Encoder;\n            $text = $pkc_encoder->encode($text);\n            mcrypt_generic_init($module, $this->key, $iv);\n            //加密\n            $encrypted = mcrypt_generic($module, $text);\n            mcrypt_generic_deinit($module);\n            mcrypt_module_close($module);\n\n            //\t\t\tprint(base64_encode($encrypted));\n            //使用BASE64对加密后的字符串进行编码\n            return array(ErrorCode::$OK, base64_encode($encrypted));\n        } catch (Exception $e) {\n            //print $e;\n            return array(ErrorCode::$EncryptAESError, null);\n        }\n    }\n\n    /**\n     * 对密文进行解密\n     * @param string $encrypted 需要解密的密文\n     * @return string 解密得到的明文\n     */\n    public function decrypt($encrypted, $appid)\n    {\n\n        try {\n            //使用BASE64对需要解密的字符串进行解码\n            $ciphertext_dec = base64_decode($encrypted);\n            $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');\n            $iv = substr($this->key, 0, 16);\n            mcrypt_generic_init($module, $this->key, $iv);\n            //解密\n            $decrypted = mdecrypt_generic($module, $ciphertext_dec);\n            mcrypt_generic_deinit($module);\n            mcrypt_module_close($module);\n        } catch (Exception $e) {\n            return array(ErrorCode::$DecryptAESError, null);\n        }\n\n\n        try {\n            //去除补位字符\n            $pkc_encoder = new PKCS7Encoder;\n            $result = $pkc_encoder->decode($decrypted);\n            //去除16位随机字符串,网络字节序和AppId\n            if (strlen($result) < 16)\n                return \"\";\n            $content = substr($result, 16, strlen($result));\n            $len_list = unpack(\"N\", substr($content, 0, 4));\n            $xml_len = $len_list[1];\n            $xml_content = substr($content, 4, $xml_len);\n            $from_appid = substr($content, $xml_len + 4);\n        } catch (Exception $e) {\n            //print $e;\n            return array(ErrorCode::$IllegalBuffer, null);\n        }\n        if ($from_appid != $appid)\n            return array(ErrorCode::$ValidateAppidError, null);\n        return array(0, $xml_content);\n\n    }\n\n\n    /**\n     * 随机生成16位字符串\n     * @return string 生成的字符串\n     */\n    function getRandomStr()\n    {\n\n        $str = \"\";\n        $str_pol = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\";\n        $max = strlen($str_pol) - 1;\n        for ($i = 0; $i < 16; $i++) {\n            $str .= $str_pol[mt_rand(0, $max)];\n        }\n        return $str;\n    }\n\n}\n\n/**\n * error code\n * 仅用作类内部使用，不用于官方API接口的errCode码\n */\nclass ErrorCode\n{\n    public static $OK = 0;\n    public static $ValidateSignatureError = 40001;\n    public static $ParseXmlError = 40002;\n    public static $ComputeSignatureError = 40003;\n    public static $IllegalAesKey = 40004;\n    public static $ValidateAppidError = 40005;\n    public static $EncryptAESError = 40006;\n    public static $DecryptAESError = 40007;\n    public static $IllegalBuffer = 40008;\n    public static $EncodeBase64Error = 40009;\n    public static $DecodeBase64Error = 40010;\n    public static $GenReturnXmlError = 40011;\n    public static $errCode=array(\n            '0'=>'无问题',\n            '40001'=>'签名验证错误',\n            '40002'=>'xml解析失败',\n            '40003'=>'sha加密生成签名失败',\n            '40004'=>'encodingAesKey 非法',\n            '40005'=>'appid 校验错误',\n            '40006'=>'aes 加密失败',\n            '40007'=>'aes 解密失败',\n            '40008'=>'解密后得到的buffer非法',\n            '40009'=>'base64加密失败',\n            '40010'=>'base64解密失败',\n            '40011'=>'生成xml失败',\n    );\n    public static function getErrText($err) {\n        if (isset(self::$errCode[$err])) {\n            return self::$errCode[$err];\n        }else {\n            return false;\n        };\n    }\n}\n"
  },
  {
    "path": "test/auth.php",
    "content": "<?php\n/**\n * 微信oAuth认证示例\n */\ninclude(\"../wechat.class.php\");\nclass wxauth {\n\tprivate $options;\n\tpublic $open_id;\n\tpublic $wxuser;\n\t\n\tpublic function __construct($options){\n\t\t$this->options = $options;\n\t\t$this->wxoauth();\n\t\tsession_start();\n\t}\n\t\n\tpublic function wxoauth(){\n\t\t$scope = 'snsapi_base';\n\t\t$code = isset($_GET['code'])?$_GET['code']:'';\n\t\t$token_time = isset($_SESSION['token_time'])?$_SESSION['token_time']:0;\n\t\tif(!$code && isset($_SESSION['open_id']) && isset($_SESSION['user_token']) && $token_time>time()-3600)\n\t\t{\n\t\t\tif (!$this->wxuser) {\n\t\t\t\t$this->wxuser = $_SESSION['wxuser'];\n\t\t\t}\n\t\t\t$this->open_id = $_SESSION['open_id'];\n\t\t\treturn $this->open_id;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$options = array(\n\t\t\t\t\t'token'=>$this->options[\"token\"], //填写你设定的key\n\t\t\t\t\t'appid'=>$this->options[\"appid\"], //填写高级调用功能的app id\n\t\t\t\t\t'appsecret'=>$this->options[\"appsecret\"] //填写高级调用功能的密钥\n\t\t\t);\n\t\t\t$we_obj = new Wechat($options);\n\t\t\tif ($code) {\n\t\t\t\t$json = $we_obj->getOauthAccessToken();\n\t\t\t\tif (!$json) {\n\t\t\t\t\tunset($_SESSION['wx_redirect']);\n\t\t\t\t\tdie('获取用户授权失败，请重新确认');\n\t\t\t\t}\n\t\t\t\t$_SESSION['open_id'] = $this->open_id = $json[\"openid\"];\n\t\t\t\t$access_token = $json['access_token'];\n\t\t\t\t$_SESSION['user_token'] = $access_token;\n\t\t\t\t$_SESSION['token_time'] = time();\n\t\t\t\t$userinfo = $we_obj->getUserInfo($this->open_id);\n\t\t\t\tif ($userinfo && !empty($userinfo['nickname'])) {\n\t\t\t\t\t$this->wxuser = array(\n\t\t\t\t\t\t\t'open_id'=>$this->open_id,\n\t\t\t\t\t\t\t'nickname'=>$userinfo['nickname'],\n\t\t\t\t\t\t\t'sex'=>intval($userinfo['sex']),\n\t\t\t\t\t\t\t'location'=>$userinfo['province'].'-'.$userinfo['city'],\n\t\t\t\t\t\t\t'avatar'=>$userinfo['headimgurl']\n\t\t\t\t\t);\n\t\t\t\t} elseif (strstr($json['scope'],'snsapi_userinfo')!==false) {\n\t\t\t\t\t$userinfo = $we_obj->getOauthUserinfo($access_token,$this->open_id);\n\t\t\t\t\tif ($userinfo && !empty($userinfo['nickname'])) {\n\t\t\t\t\t\t$this->wxuser = array(\n\t\t\t\t\t\t\t\t'open_id'=>$this->open_id,\n\t\t\t\t\t\t\t\t'nickname'=>$userinfo['nickname'],\n\t\t\t\t\t\t\t\t'sex'=>intval($userinfo['sex']),\n\t\t\t\t\t\t\t\t'location'=>$userinfo['province'].'-'.$userinfo['city'],\n\t\t\t\t\t\t\t\t'avatar'=>$userinfo['headimgurl']\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn $this->open_id;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ($this->wxuser) {\n\t\t\t\t\t$_SESSION['wxuser'] = $this->wxuser;\n\t\t\t\t\t$_SESSION['open_id'] =  $json[\"openid\"];\n\t\t\t\t\tunset($_SESSION['wx_redirect']);\n\t\t\t\t\treturn $this->open_id;\n\t\t\t\t}\n\t\t\t\t$scope = 'snsapi_userinfo';\n\t\t\t}\n\t\t\tif ($scope=='snsapi_base') {\n\t\t\t\t$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];\n\t\t\t\t$_SESSION['wx_redirect'] = $url;\n\t\t\t} else {\n\t\t\t\t$url = $_SESSION['wx_redirect'];\n\t\t\t}\n\t\t\tif (!$url) {\n\t\t\t\tunset($_SESSION['wx_redirect']);\n\t\t\t\tdie('获取用户授权失败');\n\t\t\t}\n\t\t\t$oauth_url = $we_obj->getOauthRedirect($url,\"wxbase\",$scope);\n\t\t\theader('Location: ' . $oauth_url);\n\t\t}\n\t}\n}\n$options = array(\n\t\t'token'=>'tokenaccesskey', //填写你设定的key\n\t\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询\n\t\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥\n);\n$auth = new wxauth($options);\nvar_dump($auth->wxuser);\n"
  },
  {
    "path": "test/getQRCode_test.php",
    "content": "<?php\n/**\n * 微信公共接口测试\n *\n */\ninclude(\"../wechat.class.php\");\n\nfunction logdebug($text){\n\tfile_put_contents('../data/log.txt',$text.\"\\n\",FILE_APPEND);\n};\n$options = array(\n\t'token'=>'tokenaccesskey', //填写你设定的key\n\t'debug'=>true,\n\t'logcallback'=>'logdebug'\n);\n$weObj = new Wechat($options);\n\n// check null $scene_id\n$qrcode = $weObj->getQRCode();\nif ($qrcode != false) {\n\techo \"test failed.\\n\";\n\tdie();\n}\n\n// check bad $type\n$qrcode = $weObj->getQRCode(123, -1);\nif ($qrcode != false) {\techo \"test failed.\\n\";\tdie();}\n\n// check bad $type\n$qrcode = $weObj->getQRCode(123, 5);\nif ($qrcode != false) {\techo \"test failed.\\n\";\tdie();}\n\n// check bad $scene_id\n$qrcode = $weObj->getQRCode('ad', 0);\nif ($qrcode != false) {\techo \"test failed.\\n\";\tdie();}\n\n// check bad $scene_id\n$qrcode = $weObj->getQRCode('ad', 1);\nif ($qrcode != false) {\techo \"test failed.\\n\";\tdie();}\n\n// check bad $scene_id\n$qrcode = $weObj->getQRCode(123, 2);\nif ($qrcode != false) {\techo \"test failed.\\n\";\tdie();}\n\necho \"test passed.\\n\";\n"
  },
  {
    "path": "test/jsapi/jsapi-demo-6.1.js",
    "content": "wx.ready(function () {\n\talert(\"启动jsapi!\");\n  // 1 判断当前版本是否支持指定 JS 接口，支持批量判断\n  document.querySelector('#checkJsApi').onclick = function () {\n    wx.checkJsApi({\n      jsApiList: [\n                  'checkJsApi',\n                  'onMenuShareTimeline',\n                  'onMenuShareAppMessage',\n                  'onMenuShareQQ',\n                  'onMenuShareWeibo',\n                  'hideMenuItems',\n                  'showMenuItems',\n                  'hideAllNonBaseMenuItem',\n                  'showAllNonBaseMenuItem',\n                  'translateVoice',\n                  'startRecord',\n                  'stopRecord',\n                  'onRecordEnd',\n                  'playVoice',\n                  'pauseVoice',\n                  'stopVoice',\n                  'uploadVoice',\n                  'downloadVoice',\n                  'chooseImage',\n                  'previewImage',\n                  'uploadImage',\n                  'downloadImage',\n                  'getNetworkType',\n                  'openLocation',\n                  'getLocation',\n                  'hideOptionMenu',\n                  'showOptionMenu',\n                  'closeWindow',\n                  'scanQRCode',\n                  'chooseWXPay',\n                  'openProductSpecificView',\n                  'addCard',\n                  'chooseCard',\n                  'openCard'\n                ],\n      success: function (res) {\n      \talert(\"检测通过：\"  +JSON.stringify(res));\n      },\n      fail: function(res) {\n      \talert(\"检测失败：\"  +JSON.stringify(res));\n      },\n      complete: function(res) {\n      \talert(\"检测结束\");\n      }\n    });\n  };\n\n  // 2. 分享接口\n  // 2.1 监听“分享给朋友”，按钮点击、自定义分享内容及分享结果接口\n  document.querySelector('#onMenuShareAppMessage').onclick = function () {\n    wx.onMenuShareAppMessage({\n      title: 'wechat-php-sdk博客',\n      desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人：dodgepudding 项目地址：https://github.com/dodgepudding/wechat-php-sdk',\n      link: 'http://binsee.github.io/wechat-php-sdk/',\n      imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg',\n      trigger: function (res) {\n      \talert(\"点击分享：\" +JSON.stringify(res));\n          // 用户确认分享后执行的回调函数\n      },\n      success: function (res) {\n      \talert(\"分享成功：\" +JSON.stringify(res));\n          // 用户确认分享后执行的回调函数\n      },\n      cancel: function (res) {\n      \talert(\"取消分享：\" +JSON.stringify(res));\n          // 用户取消分享后执行的回调函数\n      },\n      fail:function (res) {\n      \talert(\"分享失败：\" +JSON.stringify(res));\n      }\n    });\n    alert('已注册获取“发送给朋友”状态事件');\n  };\n\n  // 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口\n  document.querySelector('#onMenuShareTimeline').onclick = function () {\n    wx.onMenuShareTimeline({\n      title: 'wechat-php-sdk博客',\n      desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人：dodgepudding 项目地址：https://github.com/dodgepudding/wechat-php-sdk',\n      link: 'http://binsee.github.io/wechat-php-sdk/',\n      imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg',\n      trigger: function (res) {\n        \talert(\"点击分享：\" +JSON.stringify(res));\n            // 用户确认分享后执行的回调函数\n        },\n        success: function (res) {\n        \talert(\"分享成功：\" +JSON.stringify(res));\n            // 用户确认分享后执行的回调函数\n        },\n        cancel: function (res) {\n        \talert(\"取消分享：\" +JSON.stringify(res));\n            // 用户取消分享后执行的回调函数\n        },\n        fail:function (res) {\n        \talert(\"分享失败：\" +JSON.stringify(res));\n        }\n    });\n    alert('已注册获取“分享到朋友圈”状态事件');\n  };\n\n  // 2.3 监听“分享到QQ”按钮点击、自定义分享内容及分享结果接口\n  document.querySelector('#onMenuShareQQ').onclick = function () {\n    wx.onMenuShareQQ({\n      title: 'wechat-php-sdk博客',\n      desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人：dodgepudding 项目地址：https://github.com/dodgepudding/wechat-php-sdk',\n      link: 'http://binsee.github.io/wechat-php-sdk/',\n      imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg',\n      trigger: function (res) {\n      \talert(\"点击分享：\" +JSON.stringify(res));\n          // 用户确认分享后执行的回调函数\n      },\n      success: function (res) {\n      \talert(\"分享成功：\" +JSON.stringify(res));\n          // 用户确认分享后执行的回调函数\n      },\n      cancel: function (res) {\n      \talert(\"取消分享：\" +JSON.stringify(res));\n          // 用户取消分享后执行的回调函数\n      },\n      fail:function (res) {\n      \talert(\"分享失败：\" +JSON.stringify(res));\n      }\n    });\n    alert('已注册获取“分享到 QQ”状态事件');\n  };\n  \n  // 2.4 监听“分享到微博”按钮点击、自定义分享内容及分享结果接口\n  document.querySelector('#onMenuShareWeibo').onclick = function () {\n    wx.onMenuShareWeibo({\n      title: 'wechat-php-sdk博客',\n      desc: '微信公众平台php开发包,细化各项接口操作,支持链式调用。项目创建人：dodgepudding 项目地址：https://github.com/dodgepudding/wechat-php-sdk',\n      link: 'http://binsee.github.io/wechat-php-sdk/',\n      imgUrl: 'http://binsee.github.io/wechat-php-sdk/img/author.jpg',\n      trigger: function (res) {\n        \talert(\"点击分享：\" +JSON.stringify(res));\n            // 用户确认分享后执行的回调函数\n        },\n        success: function (res) {\n        \talert(\"分享成功：\" +JSON.stringify(res));\n            // 用户确认分享后执行的回调函数\n        },\n        cancel: function (res) {\n        \talert(\"取消分享：\" +JSON.stringify(res));\n            // 用户取消分享后执行的回调函数\n        },\n        fail:function (res) {\n        \talert(\"分享失败：\" +JSON.stringify(res));\n        }\n    });\n    alert('已注册获取“分享到微博”状态事件');\n  };\n\n\n  // 3 智能接口\n  var voice = {\n    localId: '',\n    serverId: ''\n  };\n  // 3.1 识别音频并返回识别结果\n  document.querySelector('#translateVoice').onclick = function () {\n    if (voice.localId == '') {\n      alert('请先使用 startRecord 接口录制一段声音');\n      return;\n    }\n    wx.translateVoice({\n      localId: voice.localId,\n      complete: function (res) {\n        if (res.hasOwnProperty('translateResult')) {\n          alert('识别结果：' + res.translateResult);\n        } else {\n          alert('无法识别');\n        }\n      }\n    });\n  };\n\n  // 4 音频接口\n  // 4.2 开始录音\n  document.querySelector('#startRecord').onclick = function () {\n    wx.startRecord({\n      cancel: function () {\n        alert('用户拒绝授权录音');\n      }\n    });\n  };\n\n  // 4.3 停止录音\n  document.querySelector('#stopRecord').onclick = function () {\n    wx.stopRecord({\n      success: function (res) {\n        voice.localId = res.localId;\n      },\n      fail: function (res) {\n        alert(JSON.stringify(res));\n      }\n    });\n  };\n\n  // 4.4 监听录音自动停止\n  wx.onVoiceRecordEnd({\n    complete: function (res) {\n      voice.localId = res.localId;\n      alert('录音时间已超过一分钟');\n    }\n  });\n\n  // 4.5 播放音频\n  document.querySelector('#playVoice').onclick = function () {\n    if (voice.localId == '') {\n      alert('请先使用 startRecord 接口录制一段声音');\n      return;\n    }\n    wx.playVoice({\n      localId: voice.localId\n    });\n  };\n\n  // 4.6 暂停播放音频\n  document.querySelector('#pauseVoice').onclick = function () {\n    wx.pauseVoice({\n      localId: voice.localId\n    });\n  };\n\n  // 4.7 停止播放音频\n  document.querySelector('#stopVoice').onclick = function () {\n    wx.stopVoice({\n      localId: voice.localId\n    });\n  };\n\n  // 4.8 监听录音播放停止\n  wx.onVoicePlayEnd({\n    complete: function (res) {\n      alert('录音（' + res.localId + '）播放结束');\n    }\n  });\n\n  // 4.8 上传语音\n  document.querySelector('#uploadVoice').onclick = function () {\n    if (voice.localId == '') {\n      alert('请先使用 startRecord 接口录制一段声音');\n      return;\n    }\n    wx.uploadVoice({\n      localId: voice.localId,\n      success: function (res) {\n        alert('上传语音成功，serverId 为' + res.serverId);\n        voice.serverId = res.serverId;\n        alert(\"上传语音信息：\" + JSON.stringify(res));\n      }\n    });\n  };\n\n  // 4.9 下载语音\n  document.querySelector('#downloadVoice').onclick = function () {\n    if (voice.serverId == '') {\n      alert('请先使用 uploadVoice 上传声音');\n      return;\n    }\n    wx.downloadVoice({\n      serverId: voice.serverId,\n      success: function (res) {\n        alert('下载语音成功，localId 为' + res.localId);\n        voice.localId = res.localId;\n        alert(\"下载语音信息：\" + JSON.stringify(res));\n      }\n    });\n  };\n\n  // 5 图片接口\n  // 5.1 拍照、本地选图\n  var images = {\n    localId: [],\n    serverId: []\n  };\n  document.querySelector('#chooseImage').onclick = function () {\n    wx.chooseImage({\n      success: function (res) {\n        images.localId = res.localIds;\n        alert('已选择 ' + res.localIds.length + ' 张图片');\n      }\n    });\n  };\n\n  // 5.2 图片预览\n  document.querySelector('#previewImage').onclick = function () {\n    wx.previewImage({\n      current: 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg',\n      urls: [\n        'http://img3.douban.com/view/photo/photo/public/p2152117150.jpg',\n        'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg',\n        'http://img3.douban.com/view/photo/photo/public/p2152134700.jpg'\n      ]\n    });\n  };\n\n  // 5.3 上传图片\n  document.querySelector('#uploadImage').onclick = function () {\n    if (images.localId.length == 0) {\n      alert('请先使用 chooseImage 接口选择图片');\n      return;\n    }\n    var i = 0, length = images.localId.length;\n    images.serverId = [];\n    function upload() {\n      wx.uploadImage({\n        localId: images.localId[i],\n        success: function (res) {\n          i++;\n            alert('已上传：' + i + '/' + length);\n            alert(\"上传图片信息：\" + JSON.stringify(res));\n          images.serverId.push(res.serverId);\n          if (i < length) {\n            upload();\n          }\n        },\n        fail: function (res) {\n          alert(JSON.stringify(res));\n        }\n      });\n    }\n    upload();\n  };\n\n  // 5.4 下载图片\n  document.querySelector('#downloadImage').onclick = function () {\n    if (images.serverId.length === 0) {\n      alert('请先使用 uploadImage 上传图片');\n      return;\n    }\n    var i = 0, length = images.serverId.length;\n    images.localId = [];\n    function download() {\n      wx.downloadImage({\n        serverId: images.serverId[i],\n        success: function (res) {\n          i++;\n            alert('已下载：' + i + '/' + length);\n            alert(\"下载图片信息：\" + JSON.stringify(res));\n          images.localId.push(res.localId);\n          if (i < length) {\n            download();\n          }\n        }\n      });\n    }\n    download();\n  };\n\n  // 6 设备信息接口\n  // 6.1 获取当前网络状态\n  document.querySelector('#getNetworkType').onclick = function () {\n    wx.getNetworkType({\n      success: function (res) {\n        alert(res.networkType);\n      },\n      fail: function (res) {\n        alert(JSON.stringify(res));\n      }\n    });\n  };\n\n  // 7 地理位置接口\n  // 7.1 查看地理位置\n  document.querySelector('#openLocation').onclick = function () {\n    wx.openLocation({\n      latitude: 23.099994,\n      longitude: 113.324520,\n      name: 'TIT 创意园',\n      address: '广州市海珠区新港中路 397 号',\n      scale: 14,\n      infoUrl: 'http://weixin.qq.com'\n    });\n  };\n\n  // 7.2 获取当前地理位置\n  document.querySelector('#getLocation').onclick = function () {\n    wx.getLocation({\n      success: function (res) {\n        alert(JSON.stringify(res));\n      },\n      cancel: function (res) {\n        alert('用户拒绝授权获取地理位置');\n      }\n    });\n  };\n  \n  // 8 界面操作接口\n  // 8.1 隐藏右上角菜单\n  document.querySelector('#hideOptionMenu').onclick = function () {\n    wx.hideOptionMenu();\n  };\n\n  // 8.2 显示右上角菜单\n  document.querySelector('#showOptionMenu').onclick = function () {\n    wx.showOptionMenu();\n  };\n\n  // 8.3 批量隐藏菜单项\n  document.querySelector('#hideMenuItems').onclick = function () {\n    wx.hideMenuItems({\n      menuList: [\n        'menuItem:readMode', // 阅读模式\n        'menuItem:share:timeline', // 分享到朋友圈\n        'menuItem:copyUrl' // 复制链接\n      ],\n      success: function (res) {\n        alert('已隐藏“阅读模式”，“分享到朋友圈”，“复制链接”等按钮');\n      },\n      fail: function (res) {\n        alert(JSON.stringify(res));\n      }\n    });\n  };\n\n  // 8.4 批量显示菜单项\n  document.querySelector('#showMenuItems').onclick = function () {\n    wx.showMenuItems({\n      menuList: [\n        'menuItem:readMode', // 阅读模式\n        'menuItem:share:timeline', // 分享到朋友圈\n        'menuItem:copyUrl' // 复制链接\n      ],\n      success: function (res) {\n        alert('已显示“阅读模式”，“分享到朋友圈”，“复制链接”等按钮');\n      },\n      fail: function (res) {\n        alert(JSON.stringify(res));\n      }\n    });\n  };\n\n  // 8.5 隐藏所有非基本菜单项\n  document.querySelector('#hideAllNonBaseMenuItem').onclick = function () {\n    wx.hideAllNonBaseMenuItem({\n      success: function () {\n        alert('已隐藏所有非基本菜单项');\n      }\n    });\n  };\n\n  // 8.6 显示所有被隐藏的非基本菜单项\n  document.querySelector('#showAllNonBaseMenuItem').onclick = function () {\n    wx.showAllNonBaseMenuItem({\n      success: function () {\n        alert('已显示所有非基本菜单项');\n      }\n    });\n  };\n\n  // 8.7 关闭当前窗口\n  document.querySelector('#closeWindow').onclick = function () {\n    wx.closeWindow();\n  };\n\n  // 9 微信原生接口\n  // 9.1.1 扫描二维码并返回结果\n  document.querySelector('#scanQRCode0').onclick = function () {\n    wx.scanQRCode({\n      desc: 'scanQRCode desc'\n    });\n  };\n  // 9.1.2 扫描二维码并返回结果\n  document.querySelector('#scanQRCode1').onclick = function () {\n    wx.scanQRCode({\n      needResult: 1,\n      desc: 'scanQRCode desc',\n      success: function (res) {\n        alert(JSON.stringify(res));\n      }\n    });\n  };\n\n  // 10 微信支付接口\n  // 10.1 发起一个支付请求\n  document.querySelector('#chooseWXPay').onclick = function () {\n    wx.chooseWXPay({\n      timestamp: 1414723227,\n      nonceStr: 'noncestr',\n      package: 'addition=action_id%3dgaby1234%26limit_pay%3d&bank_type=WX&body=innertest&fee_type=1&input_charset=GBK&notify_url=http%3A%2F%2F120.204.206.246%2Fcgi-bin%2Fmmsupport-bin%2Fnotifypay&out_trade_no=1414723227818375338&partner=1900000109&spbill_create_ip=127.0.0.1&total_fee=1&sign=432B647FE95C7BF73BCD177CEECBEF8D',\n      paySign: 'bd5b1933cda6e9548862944836a9b52e8c9a2b69'\n    });\n  };\n\n  // 11.3  跳转微信商品页\n  document.querySelector('#openProductSpecificView').onclick = function () {\n    wx.openProductSpecificView({\n      productId: 'pDF3iY0ptap-mIIPYnsM5n8VtCR0'\n    });\n  };\n\n  // 12 微信卡券接口\n  // 12.1 添加卡券\n  document.querySelector('#addCard').onclick = function () {\n    wx.addCard({\n      cardList: [\n        {\n          cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo',\n          cardExt: '{\"code\": \"\", \"openid\": \"\", \"timestamp\": \"1418301401\", \"signature\":\"64e6a7cc85c6e84b726f2d1cbef1b36e9b0f9750\"}'\n        },\n        {\n          cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo',\n          cardExt: '{\"code\": \"\", \"openid\": \"\", \"timestamp\": \"1418301401\", \"signature\":\"64e6a7cc85c6e84b726f2d1cbef1b36e9b0f9750\"}'\n        }\n      ],\n      success: function (res) {\n        alert('已添加卡券：' + JSON.stringify(res.cardList));\n      }\n    });\n  };\n\n  // 12.2 选择卡券\n  document.querySelector('#chooseCard').onclick = function () {\n    wx.chooseCard({\n      cardSign: '97e9c5e58aab3bdf6fd6150e599d7e5806e5cb91',\n      timestamp: 1417504553,\n      nonceStr: 'k0hGdSXKZEj3Min5',\n      success: function (res) {\n        alert('已选择卡券：' + JSON.stringify(res.cardList));\n      }\n    });\n  };\n\n  // 12.3 查看卡券\n  document.querySelector('#openCard').onclick = function () {\n    alert('您没有该公众号的卡券无法打开卡券。');\n    wx.openCard({\n      cardList: [\n      ]\n    });\n  };\n});\n\nwx.error(function (res) {\n  alert(res.errMsg);\n});\n"
  },
  {
    "path": "test/jsapi/jsapi_demo.php",
    "content": "<?php\r\ninclude \"wechat.class.php\";\r\ninclude 'errCode.php';\r\n\r\n$opt = array(\r\n        'appsecret'=>'xxxxxxxxxxxxxxxxxxxxxxxxxx',//填写高级调用功能的密钥\r\n        'appid'=>'wxxxxxxxxxxxxxx'\t//填写高级调用功能的appid\r\n);\r\n\r\n$we = new Wechat($opt);\r\n$auth = $we->checkAuth();\r\n$js_ticket = $we->getJsTicket();\r\nif (!$js_ticket) {\r\n\techo \"获取js_ticket失败！<br>\";\r\n    echo '错误码：'.$we->errCode;\r\n    echo ' 错误原因：'.ErrCode::getErrText($weObj->errCode);\r\n    exit;\r\n}\r\n$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];\r\n$js_sign = $we->getJsSign($url);\r\n?>\r\n<!DOCTYPE html>\r\n<html>\r\n<head>\r\n  <meta charset=\"utf-8\">\r\n  <title>JS-SDK测试页</title>\r\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=0\">\r\n  <link rel=\"stylesheet\" href=\"style.css?ts=1420775603\">\r\n</head>\r\n<body ontouchstart=\"\">\r\n<div class=\"wxapi_container\">\r\n    <div class=\"wxapi_index_container\">\r\n      <ul class=\"label_box lbox_close wxapi_index_list\">\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-basic\">基础接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-share\">分享接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-image\">图像接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-voice\">音频接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-smart\">智能接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-device\">设备信息接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-location\">地理位置接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-webview\">界面操作接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-scan\">微信扫一扫接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-shopping\">微信小店接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-card\">微信卡券接口</a></li>\r\n        <li class=\"label_item wxapi_index_item\"><a class=\"label_inner\" href=\"#menu-pay\">微信支付接口</a></li>\r\n      </ul>\r\n    </div>\r\n    <div class=\"lbox_close wxapi_form\">\r\n      <h3 id=\"menu-basic\">基础接口</h3>\r\n      <span class=\"desc\">判断当前客户端是否支持指定JS接口</span>\r\n      <button class=\"btn btn_primary\" id=\"checkJsApi\">checkJsApi</button>\r\n\r\n      <h3 id=\"menu-share\">分享接口</h3>\r\n      <span class=\"desc\">获取“分享到朋友圈”按钮点击状态及自定义分享内容接口</span>\r\n      <button class=\"btn btn_primary\" id=\"onMenuShareTimeline\">onMenuShareTimeline</button>\r\n      <span class=\"desc\">获取“分享给朋友”按钮点击状态及自定义分享内容接口</span>\r\n      <button class=\"btn btn_primary\" id=\"onMenuShareAppMessage\">onMenuShareAppMessage</button>\r\n      <span class=\"desc\">获取“分享到QQ”按钮点击状态及自定义分享内容接口</span>\r\n      <button class=\"btn btn_primary\" id=\"onMenuShareQQ\">onMenuShareQQ</button>\r\n      <span class=\"desc\">获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口</span>\r\n      <button class=\"btn btn_primary\" id=\"onMenuShareWeibo\">onMenuShareWeibo</button>\r\n\r\n      <h3 id=\"menu-image\">图像接口</h3>\r\n      <span class=\"desc\">拍照或从手机相册中选图接口</span>\r\n      <button class=\"btn btn_primary\" id=\"chooseImage\">chooseImage</button>\r\n      <span class=\"desc\">预览图片接口</span>\r\n      <button class=\"btn btn_primary\" id=\"previewImage\">previewImage</button>\r\n      <span class=\"desc\">上传图片接口</span>\r\n      <button class=\"btn btn_primary\" id=\"uploadImage\">uploadImage</button>\r\n      <span class=\"desc\">下载图片接口</span>\r\n      <button class=\"btn btn_primary\" id=\"downloadImage\">downloadImage</button>\r\n\r\n      <h3 id=\"menu-voice\">音频接口</h3>\r\n      <span class=\"desc\">开始录音接口</span>\r\n      <button class=\"btn btn_primary\" id=\"startRecord\">startRecord</button>\r\n      <span class=\"desc\">停止录音接口</span>\r\n      <button class=\"btn btn_primary\" id=\"stopRecord\">stopRecord</button>\r\n      <span class=\"desc\">播放语音接口</span>\r\n      <button class=\"btn btn_primary\" id=\"playVoice\">playVoice</button>\r\n      <span class=\"desc\">暂停播放接口</span>\r\n      <button class=\"btn btn_primary\" id=\"pauseVoice\">pauseVoice</button>\r\n      <span class=\"desc\">停止播放接口</span>\r\n      <button class=\"btn btn_primary\" id=\"stopVoice\">stopVoice</button>\r\n      <span class=\"desc\">上传语音接口</span>\r\n      <button class=\"btn btn_primary\" id=\"uploadVoice\">uploadVoice</button>\r\n      <span class=\"desc\">下载语音接口</span>\r\n      <button class=\"btn btn_primary\" id=\"downloadVoice\">downloadVoice</button>\r\n\r\n      <h3 id=\"menu-smart\">智能接口</h3>\r\n      <span class=\"desc\">识别音频并返回识别结果接口</span>\r\n      <button class=\"btn btn_primary\" id=\"translateVoice\">translateVoice</button>\r\n\r\n      <h3 id=\"menu-device\">设备信息接口</h3>\r\n      <span class=\"desc\">获取网络状态接口</span>\r\n      <button class=\"btn btn_primary\" id=\"getNetworkType\">getNetworkType</button>\r\n\r\n      <h3 id=\"menu-location\">地理位置接口</h3>\r\n      <span class=\"desc\">使用微信内置地图查看位置接口</span>\r\n      <button class=\"btn btn_primary\" id=\"openLocation\">openLocation</button>\r\n      <span class=\"desc\">获取地理位置接口</span>\r\n      <button class=\"btn btn_primary\" id=\"getLocation\">getLocation</button>\r\n\r\n      <h3 id=\"menu-webview\">界面操作接口</h3>\r\n      <span class=\"desc\">隐藏右上角菜单接口</span>\r\n      <button class=\"btn btn_primary\" id=\"hideOptionMenu\">hideOptionMenu</button>\r\n      <span class=\"desc\">显示右上角菜单接口</span>\r\n      <button class=\"btn btn_primary\" id=\"showOptionMenu\">showOptionMenu</button>\r\n      <span class=\"desc\">关闭当前网页窗口接口</span>\r\n      <button class=\"btn btn_primary\" id=\"closeWindow\">closeWindow</button>\r\n      <span class=\"desc\">批量隐藏功能按钮接口</span>\r\n      <button class=\"btn btn_primary\" id=\"hideMenuItems\">hideMenuItems</button>\r\n      <span class=\"desc\">批量显示功能按钮接口</span>\r\n      <button class=\"btn btn_primary\" id=\"showMenuItems\">showMenuItems</button>\r\n      <span class=\"desc\">隐藏所有非基础按钮接口</span>\r\n      <button class=\"btn btn_primary\" id=\"hideAllNonBaseMenuItem\">hideAllNonBaseMenuItem</button>\r\n      <span class=\"desc\">显示所有功能按钮接口</span>\r\n      <button class=\"btn btn_primary\" id=\"showAllNonBaseMenuItem\">showAllNonBaseMenuItem</button>\r\n\r\n      <h3 id=\"menu-scan\">微信扫一扫</h3>\r\n      <span class=\"desc\">调起微信扫一扫接口</span>\r\n      <button class=\"btn btn_primary\" id=\"scanQRCode0\">scanQRCode(微信处理结果)</button>\r\n      <button class=\"btn btn_primary\" id=\"scanQRCode1\">scanQRCode(直接返回结果)</button>\r\n\r\n      <h3 id=\"menu-shopping\">微信小店接口</h3>\r\n      <span class=\"desc\">跳转微信商品页接口</span>\r\n      <button class=\"btn btn_primary\" id=\"openProductSpecificView\">openProductSpecificView</button>\r\n\r\n      <h3 id=\"menu-card\">微信卡券接口</h3>\r\n      <span class=\"desc\">批量添加卡券接口</span>\r\n      <button class=\"btn btn_primary\" id=\"addCard\">addCard</button>\r\n      <span class=\"desc\">调起适用于门店的卡券列表并获取用户选择列表</span>\r\n      <button class=\"btn btn_primary\" id=\"chooseCard\">chooseCard</button>\r\n      <span class=\"desc\">查看微信卡包中的卡券接口</span>\r\n      <button class=\"btn btn_primary\" id=\"openCard\">openCard</button>\r\n\r\n      <h3 id=\"menu-pay\">微信支付接口</h3>\r\n      <span class=\"desc\">发起一个微信支付请求</span>\r\n      <button class=\"btn btn_primary\" id=\"chooseWXPay\">chooseWXPay</button>\r\n    </div>\r\n  </div>\r\n</body>\r\n<script src=\"http://res.wx.qq.com/open/js/jweixin-1.0.0.js\"> </script>\r\n<script>\r\n  wx.config({\r\n      debug: false,\r\n      appId: '<?php echo $js_sign['appid']; ?>', // 必填，公众号的唯一标识\r\n      timestamp: <?php echo $js_sign['timestamp']; ?>, // 必填，生成签名的时间戳，切记时间戳是整数型，别加引号\r\n      nonceStr: '<?php echo $js_sign['noncestr']; ?>', // 必填，生成签名的随机串\r\n      signature: '<?php echo $js_sign['signature']; ?>', // 必填，签名，见附录1\r\n      jsApiList: [\r\n        'checkJsApi',\r\n        'onMenuShareTimeline',\r\n        'onMenuShareAppMessage',\r\n        'onMenuShareQQ',\r\n        'onMenuShareWeibo',\r\n        'hideMenuItems',\r\n        'showMenuItems',\r\n        'hideAllNonBaseMenuItem',\r\n        'showAllNonBaseMenuItem',\r\n        'translateVoice',\r\n        'startRecord',\r\n        'stopRecord',\r\n        'onRecordEnd',\r\n        'playVoice',\r\n        'pauseVoice',\r\n        'stopVoice',\r\n        'uploadVoice',\r\n        'downloadVoice',\r\n        'chooseImage',\r\n        'previewImage',\r\n        'uploadImage',\r\n        'downloadImage',\r\n        'getNetworkType',\r\n        'openLocation',\r\n        'getLocation',\r\n        'hideOptionMenu',\r\n        'showOptionMenu',\r\n        'closeWindow',\r\n        'scanQRCode',\r\n        'chooseWXPay',\r\n        'openProductSpecificView',\r\n        'addCard',\r\n        'chooseCard',\r\n        'openCard'\r\n      ]\r\n  });\r\n</script>\r\n<script src=\"jsapi-demo-6.1.js?ts=<?php echo $timestamp; ?>\"> </script>\r\n</html>"
  },
  {
    "path": "test/jsapi/style.css",
    "content": "html {\r\n  -ms-text-size-adjust: 100%;\r\n  -webkit-text-size-adjust: 100%;\r\n  -webkit-user-select: none;\r\n  user-select: none;\r\n}\r\nbody {\r\n  line-height: 1.6;\r\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\r\n  background-color: #f1f0f6;\r\n}\r\n* {\r\n  margin: 0;\r\n  padding: 0;\r\n}\r\nbutton {\r\n  font-family: inherit;\r\n  font-size: 100%;\r\n  margin: 0;\r\n  *font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\r\n}\r\nul,\r\nol {\r\n  padding-left: 0;\r\n  list-style-type: none;\r\n}\r\na {\r\n  text-decoration: none;\r\n}\r\n.label_box {\r\n  background-color: #ffffff;\r\n}\r\n.label_item {\r\n  padding-left: 15px;\r\n}\r\n.label_inner {\r\n  padding-top: 10px;\r\n  padding-bottom: 10px;\r\n  min-height: 24px;\r\n  position: relative;\r\n}\r\n.label_inner:before {\r\n  content: \" \";\r\n  position: absolute;\r\n  left: 0;\r\n  top: 0;\r\n  width: 200%;\r\n  height: 1px;\r\n  border-top: 1px solid #ededed;\r\n  -webkit-transform-origin: 0 0;\r\n  transform-origin: 0 0;\r\n  -webkit-transform: scale(0.5);\r\n  transform: scale(0.5);\r\n  top: auto;\r\n  bottom: -2px;\r\n}\r\n.lbox_close {\r\n  position: relative;\r\n}\r\n.lbox_close:before {\r\n  content: \" \";\r\n  position: absolute;\r\n  left: 0;\r\n  top: 0;\r\n  width: 200%;\r\n  height: 1px;\r\n  border-top: 1px solid #ededed;\r\n  -webkit-transform-origin: 0 0;\r\n  transform-origin: 0 0;\r\n  -webkit-transform: scale(0.5);\r\n  transform: scale(0.5);\r\n}\r\n.lbox_close:after {\r\n  content: \" \";\r\n  position: absolute;\r\n  left: 0;\r\n  top: 0;\r\n  width: 200%;\r\n  height: 1px;\r\n  border-top: 1px solid #ededed;\r\n  -webkit-transform-origin: 0 0;\r\n  transform-origin: 0 0;\r\n  -webkit-transform: scale(0.5);\r\n  transform: scale(0.5);\r\n  top: auto;\r\n  bottom: -2px;\r\n}\r\n.lbox_close .label_item:last-child .label_inner:before {\r\n  display: none;\r\n}\r\n.btn {\r\n  display: block;\r\n  margin-left: auto;\r\n  margin-right: auto;\r\n  padding-left: 14px;\r\n  padding-right: 14px;\r\n  font-size: 18px;\r\n  text-align: center;\r\n  text-decoration: none;\r\n  overflow: visible;\r\n  /*.btn_h(@btnHeight);*/\r\n  height: 42px;\r\n  border-radius: 5px;\r\n  -moz-border-radius: 5px;\r\n  -webkit-border-radius: 5px;\r\n  box-sizing: border-box;\r\n  -moz-box-sizing: border-box;\r\n  -webkit-box-sizing: border-box;\r\n  color: #ffffff;\r\n  line-height: 42px;\r\n  -webkit-tap-highlight-color: rgba(255, 255, 255, 0);\r\n}\r\n.btn.btn_inline {\r\n  display: inline-block;\r\n}\r\n.btn_primary {\r\n  background-color: #04be02;\r\n}\r\n.btn_primary:not(.btn_disabled):visited {\r\n  color: #ffffff;\r\n}\r\n.btn_primary:not(.btn_disabled):active {\r\n  color: rgba(255, 255, 255, 0.9);\r\n  background-color: #039702;\r\n}\r\nbutton.btn {\r\n  width: 100%;\r\n  border: 0;\r\n  outline: 0;\r\n  -webkit-appearance: none;\r\n}\r\nbutton.btn:focus {\r\n  outline: 0;\r\n}\r\n.wxapi_container {\r\n  font-size: 16px;\r\n}\r\nh1 {\r\n  font-size: 14px;\r\n  font-weight: 400;\r\n  line-height: 2em;\r\n  padding-left: 15px;\r\n  color: #8d8c92;\r\n}\r\n.desc {\r\n  font-size: 14px;\r\n  font-weight: 400;\r\n  line-height: 2em;\r\n  color: #8d8c92;\r\n}\r\n.wxapi_index_item a {\r\n  display: block;\r\n  color: #3e3e3e;\r\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\r\n}\r\n.wxapi_form {\r\n  background-color: #ffffff;\r\n  padding: 0 15px;\r\n  margin-top: 30px;\r\n  padding-bottom: 15px;\r\n}\r\nh3 {\r\n  padding-top: 16px;\r\n  margin-top: 25px;\r\n  font-size: 16px;\r\n  font-weight: 400;\r\n  color: #3e3e3e;\r\n  position: relative;\r\n}\r\nh3:first-child {\r\n  padding-top: 15px;\r\n}\r\nh3:before {\r\n  content: \" \";\r\n  position: absolute;\r\n  left: 0;\r\n  top: 0;\r\n  width: 200%;\r\n  height: 1px;\r\n  border-top: 1px solid #ededed;\r\n  -webkit-transform-origin: 0 0;\r\n  transform-origin: 0 0;\r\n  -webkit-transform: scale(0.5);\r\n  transform: scale(0.5);\r\n}\r\n.btn {\r\n  margin-bottom: 15px;\r\n}\r\n\r\n"
  },
  {
    "path": "test/merchanttest.php",
    "content": "<?php\ninclude \"../wechat.class.php\";\n\n$options = array(\n\t\t'token'=>'XXXXXX', //填写你设定的key\n\t\t'encodingaeskey'=>'XXXXXX', //填写加密用的EncodingAESKey，如接口为明文模式可忽略\n\t\t'appid'=>'XXXXXX', //填写高级调用功能的app id\n\t\t'appsecret'=>'XXXXXX' //填写高级调用功能的密钥\n\t);\n$weObj = new Wechat($options);\n\n$order = $weObj->getOrderByID(\"13791169361138306965\");\nvar_dump($order);\n\n$order = $weObj->getOrderByFilter();\nvar_dump($order);\n"
  },
  {
    "path": "test/qydemo.php",
    "content": "<?php\ninclude \"../qywechat.class.php\";\n\nfunction logg($text){\n    file_put_contents('./log.txt',$text.\"\\r\\n\\r\\n\",FILE_APPEND);\n};\n\n$options = array(\n        'token'=>'9xxxxxxxxxxxx',\t//填写应用接口的Token\n        'encodingaeskey'=>'d4oxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',//填写加密用的EncodingAESKey\n        'appid'=>'wxa0xxxxxxxxxx',\t//填写高级调用功能的appid\n        'debug'=>true,\n        'logcallback'=>'logg'\n\n);\nlogg(\"GET参数为：\\n\".var_export($_GET,true));\n$weObj = new Wechat($options);\n$ret=$weObj->valid();\nif (!$ret) {\n\tlogg(\"验证失败！\");\n\tvar_dump($ret);\n\texit;\n}\n$f = $weObj->getRev()->getRevFrom();\n$t = $weObj->getRevType();\n$d = $weObj->getRevData();\n$weObj->text(\"你好！来自星星的：\".$f.\"\\n你发送的\".$t.\"类型信息：\\n原始信息如下：\\n\".var_export($d,true))->reply();\nlogg(\"-----------------------------------------\");\n?>"
  },
  {
    "path": "test/test-upload-shake-around-media.php",
    "content": "<?php\n/**\n * 微信摇一摇周边上传素材测试\n * \n */\n\tinclude(\"../wechat.class.php\");\n\n\t$options = array(\n\t\t'appid'           => '', //填写高级调用功能的app id, 请在微信开发模式后台查询\n        'appsecret'       => '', //填写高级调用功能的密钥\n\t);\n\t$weObj = new Wechat($options);\n\t$weObj->uploadShakeAroundMedia(array('media'=>'@'));"
  },
  {
    "path": "test/test1.php",
    "content": "<?php\n/**\n * 微信公共接口测试\n * \n */\n\tinclude(\"../wechat.class.php\");\n\t\n\tfunction logdebug($text){\n\t\tfile_put_contents('../data/log.txt',$text.\"\\n\",FILE_APPEND);\t\t\n\t};\n\t$options = array(\n\t\t'token'=>'tokenaccesskey', //填写你设定的key\n\t\t\t'debug'=>true,\n\t\t\t'logcallback'=>'logdebug'\n\t);\n\t$weObj = new Wechat($options);\n\t$weObj->valid();\n\t$type = $weObj->getRev()->getRevType();\n\tswitch($type) {\n\t\tcase Wechat::MSGTYPE_TEXT:\n\t\t\t\t$weObj->text(\"hello, I'm wechat\")->reply();\n\t\t\t\texit;\n\t\t\t\tbreak;\n\t\tcase Wechat::MSGTYPE_EVENT:\n\t\t\t\tbreak;\n\t\tcase Wechat::MSGTYPE_IMAGE:\n\t\t\t\tbreak;\n\t\tdefault:\n\t\t\t\t$weObj->text(\"help info\")->reply();\n\t}\n"
  },
  {
    "path": "wechat.class.php",
    "content": "<?php\n/**\n *\t微信公众平台PHP-SDK, 官方API部分\n *  @author  dodge <dodgepudding@gmail.com>\n *  @link https://github.com/dodgepudding/wechat-php-sdk\n *  @version 1.2\n *  usage:\n *   $options = array(\n *\t\t\t'token'=>'tokenaccesskey', //填写你设定的key\n *\t\t\t'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey\n *\t\t\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id\n *\t\t\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥\n *\t\t);\n *\t $weObj = new Wechat($options);\n *   $weObj->valid();\n *   $type = $weObj->getRev()->getRevType();\n *   switch($type) {\n *   \t\tcase Wechat::MSGTYPE_TEXT:\n *   \t\t\t$weObj->text(\"hello, I'm wechat\")->reply();\n *   \t\t\texit;\n *   \t\t\tbreak;\n *   \t\tcase Wechat::MSGTYPE_EVENT:\n *   \t\t\t....\n *   \t\t\tbreak;\n *   \t\tcase Wechat::MSGTYPE_IMAGE:\n *   \t\t\t...\n *   \t\t\tbreak;\n *   \t\tdefault:\n *   \t\t\t$weObj->text(\"help info\")->reply();\n *   }\n *\n *   //获取菜单操作:\n *   $menu = $weObj->getMenu();\n *   //设置菜单\n *   $newmenu =  array(\n *   \t\t\"button\"=>\n *   \t\t\tarray(\n *   \t\t\t\tarray('type'=>'click','name'=>'最新消息','key'=>'MENU_KEY_NEWS'),\n *   \t\t\t\tarray('type'=>'view','name'=>'我要搜索','url'=>'http://www.baidu.com'),\n *   \t\t\t\t)\n *  \t\t);\n *   $result = $weObj->createMenu($newmenu);\n */\nclass Wechat\n{\n\tconst MSGTYPE_TEXT = 'text';\n\tconst MSGTYPE_IMAGE = 'image';\n\tconst MSGTYPE_LOCATION = 'location';\n\tconst MSGTYPE_LINK = 'link';\n\tconst MSGTYPE_EVENT = 'event';\n\tconst MSGTYPE_MUSIC = 'music';\n\tconst MSGTYPE_NEWS = 'news';\n\tconst MSGTYPE_VOICE = 'voice';\n\tconst MSGTYPE_VIDEO = 'video';\n\tconst MSGTYPE_SHORTVIDEO = 'shortvideo';\n\tconst EVENT_SUBSCRIBE = 'subscribe';       //订阅\n\tconst EVENT_UNSUBSCRIBE = 'unsubscribe';   //取消订阅\n\tconst EVENT_SCAN = 'SCAN';                 //扫描带参数二维码\n\tconst EVENT_LOCATION = 'LOCATION';         //上报地理位置\n\tconst EVENT_MENU_VIEW = 'VIEW';                     //菜单 - 点击菜单跳转链接\n\tconst EVENT_MENU_CLICK = 'CLICK';                   //菜单 - 点击菜单拉取消息\n\tconst EVENT_MENU_SCAN_PUSH = 'scancode_push';       //菜单 - 扫码推事件(客户端跳URL)\n\tconst EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL)\n\tconst EVENT_MENU_PIC_SYS = 'pic_sysphoto';          //菜单 - 弹出系统拍照发图\n\tconst EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album';  //菜单 - 弹出拍照或者相册发图\n\tconst EVENT_MENU_PIC_WEIXIN = 'pic_weixin';         //菜单 - 弹出微信相册发图器\n\tconst EVENT_MENU_LOCATION = 'location_select';      //菜单 - 弹出地理位置选择器\n\tconst EVENT_SEND_MASS = 'MASSSENDJOBFINISH';        //发送结果 - 高级群发完成\n\tconst EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果\n\tconst EVENT_KF_SEESION_CREATE = 'kfcreatesession';  //多客服 - 接入会话\n\tconst EVENT_KF_SEESION_CLOSE = 'kfclosesession';    //多客服 - 关闭会话\n\tconst EVENT_KF_SEESION_SWITCH = 'kfswitchsession';  //多客服 - 转接会话\n\tconst EVENT_CARD_PASS = 'card_pass_check';          //卡券 - 审核通过\n\tconst EVENT_CARD_NOTPASS = 'card_not_pass_check';   //卡券 - 审核未通过\n\tconst EVENT_CARD_USER_GET = 'user_get_card';        //卡券 - 用户领取卡券\n\tconst EVENT_CARD_USER_DEL = 'user_del_card';        //卡券 - 用户删除卡券\n\tconst EVENT_MERCHANT_ORDER = 'merchant_order';        //微信小店 - 订单付款通知\n\tconst API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin';\n\tconst AUTH_URL = '/token?grant_type=client_credential&';\n\tconst MENU_CREATE_URL = '/menu/create?';\n\tconst MENU_GET_URL = '/menu/get?';\n\tconst MENU_DELETE_URL = '/menu/delete?';\n\tconst MENU_ADDCONDITIONAL_URL = '/menu/addconditional?';\n\tconst MENU_DELCONDITIONAL_URL = '/menu/delconditional?';\n\tconst MENU_TRYMATCH_URL = '/menu/trymatch?';\n\tconst GET_TICKET_URL = '/ticket/getticket?';\n\tconst CALLBACKSERVER_GET_URL = '/getcallbackip?';\n\tconst QRCODE_CREATE_URL='/qrcode/create?';\n\tconst QR_SCENE = 0;\n\tconst QR_LIMIT_SCENE = 1;\n\tconst QRCODE_IMG_URL='https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=';\n\tconst SHORT_URL='/shorturl?';\n\tconst USER_GET_URL='/user/get?';\n\tconst USER_INFO_URL='/user/info?';\n\tconst USERS_INFO_URL='/user/info/batchget?';\n\tconst USER_UPDATEREMARK_URL='/user/info/updateremark?';\n\tconst GROUP_GET_URL='/groups/get?';\n\tconst USER_GROUP_URL='/groups/getid?';\n\tconst GROUP_CREATE_URL='/groups/create?';\n\tconst GROUP_UPDATE_URL='/groups/update?';\n\tconst GROUP_MEMBER_UPDATE_URL='/groups/members/update?';\n\tconst GROUP_MEMBER_BATCHUPDATE_URL='/groups/members/batchupdate?';\n\tconst CUSTOM_SEND_URL='/message/custom/send?';\n\tconst MEDIA_UPLOADNEWS_URL = '/media/uploadnews?';\n\tconst MASS_SEND_URL = '/message/mass/send?';\n\tconst TEMPLATE_SET_INDUSTRY_URL = '/template/api_set_industry?';\n\tconst TEMPLATE_ADD_TPL_URL = '/template/api_add_template?';\n\tconst TEMPLATE_SEND_URL = '/message/template/send?';\n\tconst MASS_SEND_GROUP_URL = '/message/mass/sendall?';\n\tconst MASS_DELETE_URL = '/message/mass/delete?';\n\tconst MASS_PREVIEW_URL = '/message/mass/preview?';\n\tconst MASS_QUERY_URL = '/message/mass/get?';\n\tconst UPLOAD_MEDIA_URL = 'http://file.api.weixin.qq.com/cgi-bin';\n\tconst MEDIA_UPLOAD_URL = '/media/upload?';\n\tconst MEDIA_UPLOADIMG_URL = '/media/uploadimg?';//图片上传接口\n\tconst MEDIA_GET_URL = '/media/get?';\n\tconst MEDIA_VIDEO_UPLOAD = '/media/uploadvideo?';\n    const MEDIA_FOREVER_UPLOAD_URL = '/material/add_material?';\n    const MEDIA_FOREVER_NEWS_UPLOAD_URL = '/material/add_news?';\n    const MEDIA_FOREVER_NEWS_UPDATE_URL = '/material/update_news?';\n    const MEDIA_FOREVER_GET_URL = '/material/get_material?';\n    const MEDIA_FOREVER_DEL_URL = '/material/del_material?';\n    const MEDIA_FOREVER_COUNT_URL = '/material/get_materialcount?';\n    const MEDIA_FOREVER_BATCHGET_URL = '/material/batchget_material?';\n\tconst OAUTH_PREFIX = 'https://open.weixin.qq.com/connect/oauth2';\n\tconst OAUTH_AUTHORIZE_URL = '/authorize?';\n\t///多客服相关地址\n\tconst CUSTOM_SERVICE_GET_RECORD = '/customservice/getrecord?';\n\tconst CUSTOM_SERVICE_GET_KFLIST = '/customservice/getkflist?';\n\tconst CUSTOM_SERVICE_GET_ONLINEKFLIST = '/customservice/getonlinekflist?';\n\tconst API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀\n\tconst OAUTH_TOKEN_URL = '/sns/oauth2/access_token?';\n\tconst OAUTH_REFRESH_URL = '/sns/oauth2/refresh_token?';\n\tconst OAUTH_USERINFO_URL = '/sns/userinfo?';\n\tconst OAUTH_AUTH_URL = '/sns/auth?';\n\t///多客服相关地址\n\tconst CUSTOM_SESSION_CREATE = '/customservice/kfsession/create?';\n\tconst CUSTOM_SESSION_CLOSE = '/customservice/kfsession/close?';\n\tconst CUSTOM_SESSION_SWITCH = '/customservice/kfsession/switch?';\n\tconst CUSTOM_SESSION_GET = '/customservice/kfsession/getsession?';\n\tconst CUSTOM_SESSION_GET_LIST = '/customservice/kfsession/getsessionlist?';\n\tconst CUSTOM_SESSION_GET_WAIT = '/customservice/kfsession/getwaitcase?';\n\tconst CS_KF_ACCOUNT_ADD_URL = '/customservice/kfaccount/add?';\n\tconst CS_KF_ACCOUNT_UPDATE_URL = '/customservice/kfaccount/update?';\n\tconst CS_KF_ACCOUNT_DEL_URL = '/customservice/kfaccount/del?';\n\tconst CS_KF_ACCOUNT_UPLOAD_HEADIMG_URL = '/customservice/kfaccount/uploadheadimg?';\n\t///卡券相关地址\n\tconst CARD_CREATE                     = '/card/create?';\n\tconst CARD_DELETE                     = '/card/delete?';\n\tconst CARD_UPDATE                     = '/card/update?';\n\tconst CARD_GET                        = '/card/get?';\n        const CARD_USER_GETCARDLIST         = '/card/user/getcardlist?';\n        const CARD_BATCHGET                   = '/card/batchget?';\n\tconst CARD_MODIFY_STOCK               = '/card/modifystock?';\n\tconst CARD_LOCATION_BATCHADD          = '/card/location/batchadd?';\n\tconst CARD_LOCATION_BATCHGET          = '/card/location/batchget?';\n\tconst CARD_GETCOLORS                  = '/card/getcolors?';\n\tconst CARD_QRCODE_CREATE              = '/card/qrcode/create?';\n\tconst CARD_CODE_CONSUME               = '/card/code/consume?';\n\tconst CARD_CODE_DECRYPT               = '/card/code/decrypt?';\n\tconst CARD_CODE_GET                   = '/card/code/get?';\n\tconst CARD_CODE_UPDATE                = '/card/code/update?';\n\tconst CARD_CODE_UNAVAILABLE           = '/card/code/unavailable?';\n\tconst CARD_TESTWHILELIST_SET          = '/card/testwhitelist/set?';\n\tconst CARD_MEETINGCARD_UPDATEUSER      = '/card/meetingticket/updateuser?';    //更新会议门票\n\tconst CARD_MEMBERCARD_ACTIVATE        = '/card/membercard/activate?';      //激活会员卡\n\tconst CARD_MEMBERCARD_UPDATEUSER      = '/card/membercard/updateuser?';    //更新会员卡\n\tconst CARD_MOVIETICKET_UPDATEUSER     = '/card/movieticket/updateuser?';   //更新电影票(未加方法)\n\tconst CARD_BOARDINGPASS_CHECKIN       = '/card/boardingpass/checkin?';     //飞机票-在线选座(未加方法)\n\tconst CARD_LUCKYMONEY_UPDATE          = '/card/luckymoney/updateuserbalance?';     //更新红包金额\n\tconst SEMANTIC_API_URL = '/semantic/semproxy/search?'; //语义理解\n\t///数据分析接口\n\tstatic $DATACUBE_URL_ARR = array(        //用户分析\n\t        'user' => array(\n\t                'summary' => '/datacube/getusersummary?',\t\t//获取用户增减数据（getusersummary）\n\t                'cumulate' => '/datacube/getusercumulate?',\t\t//获取累计用户数据（getusercumulate）\n\t        ),\n\t        'article' => array(            //图文分析\n\t                'summary' => '/datacube/getarticlesummary?',\t\t//获取图文群发每日数据（getarticlesummary）\n\t                'total' => '/datacube/getarticletotal?',\t\t//获取图文群发总数据（getarticletotal）\n\t                'read' => '/datacube/getuserread?',\t\t\t//获取图文统计数据（getuserread）\n\t                'readhour' => '/datacube/getuserreadhour?',\t\t//获取图文统计分时数据（getuserreadhour）\n\t                'share' => '/datacube/getusershare?',\t\t\t//获取图文分享转发数据（getusershare）\n\t                'sharehour' => '/datacube/getusersharehour?',\t\t//获取图文分享转发分时数据（getusersharehour）\n\t        ),\n\t        'upstreammsg' => array(        //消息分析\n\t                'summary' => '/datacube/getupstreammsg?',\t\t//获取消息发送概况数据（getupstreammsg）\n\t\t\t\t\t'hour' => '/datacube/getupstreammsghour?',\t//获取消息分送分时数据（getupstreammsghour）\n\t                'week' => '/datacube/getupstreammsgweek?',\t//获取消息发送周数据（getupstreammsgweek）\n\t                'month' => '/datacube/getupstreammsgmonth?',\t//获取消息发送月数据（getupstreammsgmonth）\n\t                'dist' => '/datacube/getupstreammsgdist?',\t//获取消息发送分布数据（getupstreammsgdist）\n\t                'distweek' => '/datacube/getupstreammsgdistweek?',\t//获取消息发送分布周数据（getupstreammsgdistweek）\n\t               \t'distmonth' => '/datacube/getupstreammsgdistmonth?',\t//获取消息发送分布月数据（getupstreammsgdistmonth）\n\t        ),\n\t        'interface' => array(        //接口分析\n\t                'summary' => '/datacube/getinterfacesummary?',\t//获取接口分析数据（getinterfacesummary）\n\t                'summaryhour' => '/datacube/getinterfacesummaryhour?',\t//获取接口分析分时数据（getinterfacesummaryhour）\n\t        )\n\t);\n\t///微信摇一摇周边\n\tconst SHAKEAROUND_DEVICE_APPLYID = '/shakearound/device/applyid?';//申请设备ID\n    const SHAKEAROUND_DEVICE_UPDATE = '/shakearound/device/update?';//编辑设备信息\n\tconst SHAKEAROUND_DEVICE_SEARCH = '/shakearound/device/search?';//查询设备列表\n\tconst SHAKEAROUND_DEVICE_BINDLOCATION = '/shakearound/device/bindlocation?';//配置设备与门店ID的关系\n\tconst SHAKEAROUND_DEVICE_BINDPAGE = '/shakearound/device/bindpage?';//配置设备与页面的绑定关系\n    const SHAKEAROUND_MATERIAL_ADD = '/shakearound/material/add?';//上传摇一摇图片素材\n\tconst SHAKEAROUND_PAGE_ADD = '/shakearound/page/add?';//增加页面\n\tconst SHAKEAROUND_PAGE_UPDATE = '/shakearound/page/update?';//编辑页面\n\tconst SHAKEAROUND_PAGE_SEARCH = '/shakearound/page/search?';//查询页面列表\n\tconst SHAKEAROUND_PAGE_DELETE = '/shakearound/page/delete?';//删除页面\n\tconst SHAKEAROUND_USER_GETSHAKEINFO = '/shakearound/user/getshakeinfo?';//获取摇周边的设备及用户信息\n\tconst SHAKEAROUND_STATISTICS_DEVICE = '/shakearound/statistics/device?';//以设备为维度的数据统计接口\n    const SHAKEAROUND_STATISTICS_PAGE = '/shakearound/statistics/page?';//以页面为维度的数据统计接口\n\t///微信小店相关接口\n\tconst MERCHANT_ORDER_GETBYID = '/merchant/order/getbyid?';//根据订单ID获取订单详情\n\tconst MERCHANT_ORDER_GETBYFILTER = '/merchant/order/getbyfilter?';//根据订单状态/创建时间获取订单详情\n\tconst MERCHANT_ORDER_SETDELIVERY = '/merchant/order/setdelivery?';//设置订单发货信息\n\tconst MERCHANT_ORDER_CLOSE = '/merchant/order/close?';//关闭订单\n\n\tprivate $token;\n\tprivate $encodingAesKey;\n\tprivate $encrypt_type;\n\tprivate $appid;\n\tprivate $appsecret;\n\tprivate $access_token;\n\tprivate $jsapi_ticket;\n\tprivate $api_ticket;\n\tprivate $user_token;\n\tprivate $partnerid;\n\tprivate $partnerkey;\n\tprivate $paysignkey;\n\tprivate $postxml;\n\tprivate $_msg;\n\tprivate $_funcflag = false;\n\tprivate $_receive;\n\tprivate $_text_filter = true;\n\tpublic $debug =  false;\n\tpublic $errCode = 40001;\n\tpublic $errMsg = \"no access\";\n\tpublic $logcallback;\n\n\tpublic function __construct($options)\n\t{\n\t\t$this->token = isset($options['token'])?$options['token']:'';\n\t\t$this->encodingAesKey = isset($options['encodingaeskey'])?$options['encodingaeskey']:'';\n\t\t$this->appid = isset($options['appid'])?$options['appid']:'';\n\t\t$this->appsecret = isset($options['appsecret'])?$options['appsecret']:'';\n\t\t$this->debug = isset($options['debug'])?$options['debug']:false;\n\t\t$this->logcallback = isset($options['logcallback'])?$options['logcallback']:false;\n\t}\n\n\t/**\n\t * For weixin server validation\n\t */\n\tprivate function checkSignature($str='')\n\t{\n        $signature = isset($_GET[\"signature\"])?$_GET[\"signature\"]:'';\n\t    $signature = isset($_GET[\"msg_signature\"])?$_GET[\"msg_signature\"]:$signature; //如果存在加密验证则用加密验证段\n        $timestamp = isset($_GET[\"timestamp\"])?$_GET[\"timestamp\"]:'';\n        $nonce = isset($_GET[\"nonce\"])?$_GET[\"nonce\"]:'';\n\n\t\t$token = $this->token;\n\t\t$tmpArr = array($token, $timestamp, $nonce,$str);\n\t\tsort($tmpArr, SORT_STRING);\n\t\t$tmpStr = implode( $tmpArr );\n\t\t$tmpStr = sha1( $tmpStr );\n\n\t\tif( $tmpStr == $signature ){\n\t\t\treturn true;\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * For weixin server validation\n\t * @param bool $return 是否返回\n\t */\n\tpublic function valid($return=false)\n    {\n        $encryptStr=\"\";\n        if ($_SERVER['REQUEST_METHOD'] == \"POST\") {\n            $postStr = file_get_contents(\"php://input\");\n            $array = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);\n            $this->encrypt_type = isset($_GET[\"encrypt_type\"]) ? $_GET[\"encrypt_type\"]: '';\n            if ($this->encrypt_type == 'aes') { //aes加密\n                $this->log($postStr);\n            \t$encryptStr = $array['Encrypt'];\n            \t$pc = new Prpcrypt($this->encodingAesKey);\n            \t$array = $pc->decrypt($encryptStr,$this->appid);\n            \tif (!isset($array[0]) || ($array[0] != 0)) {\n            \t    if (!$return) {\n            \t        die('decrypt error!');\n            \t    } else {\n            \t        return false;\n            \t    }\n            \t}\n            \t$this->postxml = $array[1];\n            \tif (!$this->appid)\n            \t    $this->appid = $array[2];//为了没有appid的订阅号。\n            } else {\n                $this->postxml = $postStr;\n            }\n        } elseif (isset($_GET[\"echostr\"])) {\n        \t$echoStr = $_GET[\"echostr\"];\n        \tif ($return) {\n        \t\tif ($this->checkSignature())\n        \t\t\treturn $echoStr;\n        \t\telse\n        \t\t\treturn false;\n        \t} else {\n        \t\tif ($this->checkSignature())\n        \t\t\tdie($echoStr);\n        \t\telse\n        \t\t\tdie('no access');\n        \t}\n        }\n\n        if (!$this->checkSignature($encryptStr)) {\n        \tif ($return)\n        \t\treturn false;\n        \telse\n        \t\tdie('no access');\n        }\n        return true;\n    }\n\n\t/**\n\t * 设置发送消息\n\t * @param array $msg 消息数组\n\t * @param bool $append 是否在原消息数组追加\n\t */\n    public function Message($msg = '',$append = false){\n    \t\tif (is_null($msg)) {\n    \t\t\t$this->_msg =array();\n    \t\t}elseif (is_array($msg)) {\n    \t\t\tif ($append)\n    \t\t\t\t$this->_msg = array_merge($this->_msg,$msg);\n    \t\t\telse\n    \t\t\t\t$this->_msg = $msg;\n    \t\t\treturn $this->_msg;\n    \t\t} else {\n    \t\t\treturn $this->_msg;\n    \t\t}\n    }\n\n    /**\n     * 设置消息的星标标志，官方已取消对此功能的支持\n     */\n    public function setFuncFlag($flag) {\n    \t\t$this->_funcflag = $flag;\n    \t\treturn $this;\n    }\n\n    /**\n     * 日志记录，可被重载。\n     * @param mixed $log 输入日志\n     * @return mixed\n     */\n    protected function log($log){\n    \t\tif ($this->debug && function_exists($this->logcallback)) {\n    \t\t\tif (is_array($log)) $log = print_r($log,true);\n    \t\t\treturn call_user_func($this->logcallback,$log);\n    \t\t}\n    }\n\n    /**\n     * 获取微信服务器发来的信息\n     */\n\tpublic function getRev()\n\t{\n\t\tif ($this->_receive) return $this;\n\t\t$postStr = !empty($this->postxml)?$this->postxml:file_get_contents(\"php://input\");\n\t\t//兼顾使用明文又不想调用valid()方法的情况\n\t\t$this->log($postStr);\n\t\tif (!empty($postStr)) {\n\t\t\t$this->_receive = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);\n\t\t}\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 获取微信服务器发来的信息\n\t */\n\tpublic function getRevData()\n\t{\n\t\treturn $this->_receive;\n\t}\n\n\t/**\n\t * 获取消息发送者\n\t */\n\tpublic function getRevFrom() {\n\t\tif (isset($this->_receive['FromUserName']))\n\t\t\treturn $this->_receive['FromUserName'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取消息接受者\n\t */\n\tpublic function getRevTo() {\n\t\tif (isset($this->_receive['ToUserName']))\n\t\t\treturn $this->_receive['ToUserName'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收消息的类型\n\t */\n\tpublic function getRevType() {\n\t\tif (isset($this->_receive['MsgType']))\n\t\t\treturn $this->_receive['MsgType'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取消息ID\n\t */\n\tpublic function getRevID() {\n\t\tif (isset($this->_receive['MsgId']))\n\t\t\treturn $this->_receive['MsgId'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取消息发送时间\n\t */\n\tpublic function getRevCtime() {\n\t\tif (isset($this->_receive['CreateTime']))\n\t\t\treturn $this->_receive['CreateTime'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收消息内容正文\n\t */\n\tpublic function getRevContent(){\n\t\tif (isset($this->_receive['Content']))\n\t\t\treturn $this->_receive['Content'];\n\t\telse if (isset($this->_receive['Recognition'])) //获取语音识别文字内容，需申请开通\n\t\t\treturn $this->_receive['Recognition'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收消息图片\n\t */\n\tpublic function getRevPic(){\n\t\tif (isset($this->_receive['PicUrl']))\n\t\t\treturn array(\n\t\t\t\t'mediaid'=>$this->_receive['MediaId'],\n\t\t\t\t'picurl'=>(string)$this->_receive['PicUrl'],    //防止picurl为空导致解析出错\n\t\t\t);\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收消息链接\n\t */\n\tpublic function getRevLink(){\n\t\tif (isset($this->_receive['Url'])){\n\t\t\treturn array(\n\t\t\t\t'url'=>$this->_receive['Url'],\n\t\t\t\t'title'=>$this->_receive['Title'],\n\t\t\t\t'description'=>$this->_receive['Description']\n\t\t\t);\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收地理位置\n\t */\n\tpublic function getRevGeo(){\n\t\tif (isset($this->_receive['Location_X'])){\n\t\t\treturn array(\n\t\t\t\t'x'=>$this->_receive['Location_X'],\n\t\t\t\t'y'=>$this->_receive['Location_Y'],\n\t\t\t\t'scale'=>$this->_receive['Scale'],\n\t\t\t\t'label'=>$this->_receive['Label']\n\t\t\t);\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取上报地理位置事件\n\t */\n\tpublic function getRevEventGeo(){\n        \tif (isset($this->_receive['Latitude'])){\n        \t\t return array(\n\t\t\t\t'x'=>$this->_receive['Latitude'],\n\t\t\t\t'y'=>$this->_receive['Longitude'],\n\t\t\t\t'precision'=>$this->_receive['Precision'],\n\t\t\t);\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收事件推送\n\t */\n\tpublic function getRevEvent(){\n\t\tif (isset($this->_receive['Event'])){\n\t\t\t$array['event'] = $this->_receive['Event'];\n\t\t}\n\t\tif (isset($this->_receive['EventKey'])){\n\t\t\t$array['key'] = $this->_receive['EventKey'];\n\t\t}\n\t\tif (isset($array) && count($array) > 0) {\n\t\t\treturn $array;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * 获取自定义菜单的扫码推事件信息\n\t *\n\t * 事件类型为以下两种时则调用此方法有效\n\t * Event\t 事件类型，scancode_push\n\t * Event\t 事件类型，scancode_waitmsg\n\t *\n\t * @return: array | false\n\t * array (\n\t *     'ScanType'=>'qrcode',\n\t *     'ScanResult'=>'123123'\n\t * )\n\t */\n\tpublic function getRevScanInfo(){\n\t\tif (isset($this->_receive['ScanCodeInfo'])){\n\t\t    if (!is_array($this->_receive['ScanCodeInfo'])) {\n\t\t        $array=(array)$this->_receive['ScanCodeInfo'];\n\t\t        $this->_receive['ScanCodeInfo']=$array;\n\t\t    }else {\n\t\t        $array=$this->_receive['ScanCodeInfo'];\n\t\t    }\n\t\t}\n\t\tif (isset($array) && count($array) > 0) {\n\t\t\treturn $array;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * 获取自定义菜单的图片发送事件信息\n\t *\n\t * 事件类型为以下三种时则调用此方法有效\n\t * Event\t 事件类型，pic_sysphoto        弹出系统拍照发图的事件推送\n\t * Event\t 事件类型，pic_photo_or_album  弹出拍照或者相册发图的事件推送\n\t * Event\t 事件类型，pic_weixin          弹出微信相册发图器的事件推送\n\t *\n\t * @return: array | false\n\t * array (\n\t *   'Count' => '2',\n\t *   'PicList' =>array (\n\t *         'item' =>array (\n\t *             0 =>array ('PicMd5Sum' => 'aaae42617cf2a14342d96005af53624c'),\n\t *             1 =>array ('PicMd5Sum' => '149bd39e296860a2adc2f1bb81616ff8'),\n\t *         ),\n\t *   ),\n\t * )\n\t *\n\t */\n\tpublic function getRevSendPicsInfo(){\n\t\tif (isset($this->_receive['SendPicsInfo'])){\n\t\t    if (!is_array($this->_receive['SendPicsInfo'])) {\n\t\t        $array=(array)$this->_receive['SendPicsInfo'];\n\t\t        if (isset($array['PicList'])){\n\t\t            $array['PicList']=(array)$array['PicList'];\n\t\t            $item=$array['PicList']['item'];\n\t\t            $array['PicList']['item']=array();\n\t\t            foreach ( $item as $key => $value ){\n\t\t                $array['PicList']['item'][$key]=(array)$value;\n\t\t            }\n\t\t        }\n\t\t        $this->_receive['SendPicsInfo']=$array;\n\t\t    } else {\n\t\t        $array=$this->_receive['SendPicsInfo'];\n\t\t    }\n\t\t}\n\t\tif (isset($array) && count($array) > 0) {\n\t\t\treturn $array;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * 获取自定义菜单的地理位置选择器事件推送\n\t *\n\t * 事件类型为以下时则可以调用此方法有效\n\t * Event\t 事件类型，location_select        弹出地理位置选择器的事件推送\n\t *\n\t * @return: array | false\n\t * array (\n\t *   'Location_X' => '33.731655000061',\n\t *   'Location_Y' => '113.29955200008047',\n\t *   'Scale' => '16',\n\t *   'Label' => '某某市某某区某某路',\n\t *   'Poiname' => '',\n\t * )\n\t *\n\t */\n\tpublic function getRevSendGeoInfo(){\n\t    if (isset($this->_receive['SendLocationInfo'])){\n\t        if (!is_array($this->_receive['SendLocationInfo'])) {\n\t            $array=(array)$this->_receive['SendLocationInfo'];\n\t            if (empty($array['Poiname'])) {\n\t                $array['Poiname']=\"\";\n\t            }\n\t            if (empty($array['Label'])) {\n\t                $array['Label']=\"\";\n\t            }\n\t            $this->_receive['SendLocationInfo']=$array;\n\t        } else {\n\t            $array=$this->_receive['SendLocationInfo'];\n\t        }\n\t    }\n\t    if (isset($array) && count($array) > 0) {\n\t        return $array;\n\t    } else {\n\t        return false;\n\t    }\n\t}\n\n\t/**\n\t * 获取接收语音推送\n\t */\n\tpublic function getRevVoice(){\n\t\tif (isset($this->_receive['MediaId'])){\n\t\t\treturn array(\n\t\t\t\t'mediaid'=>$this->_receive['MediaId'],\n\t\t\t\t'format'=>$this->_receive['Format'],\n\t\t\t);\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收视频推送\n\t */\n\tpublic function getRevVideo(){\n\t\tif (isset($this->_receive['MediaId'])){\n\t\t\treturn array(\n\t\t\t\t\t'mediaid'=>$this->_receive['MediaId'],\n\t\t\t\t\t'thumbmediaid'=>$this->_receive['ThumbMediaId']\n\t\t\t);\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取接收TICKET\n\t */\n\tpublic function getRevTicket(){\n\t\tif (isset($this->_receive['Ticket'])){\n\t\t\treturn $this->_receive['Ticket'];\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t* 获取二维码的场景值\n\t*/\n\tpublic function getRevSceneId (){\n\t\tif (isset($this->_receive['EventKey'])){\n\t\t\treturn str_replace('qrscene_','',$this->_receive['EventKey']);\n\t\t} else{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t* 获取主动推送的消息ID\n\t* 经过验证，这个和普通的消息MsgId不一样\n\t* 当Event为 MASSSENDJOBFINISH 或 TEMPLATESENDJOBFINISH\n\t*/\n\tpublic function getRevTplMsgID(){\n\t\tif (isset($this->_receive['MsgID'])){\n\t\t\treturn $this->_receive['MsgID'];\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t* 获取模板消息发送状态\n\t*/\n\tpublic function getRevStatus(){\n\t\tif (isset($this->_receive['Status'])){\n\t\t\treturn $this->_receive['Status'];\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t* 获取群发或模板消息发送结果\n\t* 当Event为 MASSSENDJOBFINISH 或 TEMPLATESENDJOBFINISH，即高级群发/模板消息\n\t*/\n\tpublic function getRevResult(){\n\t\tif (isset($this->_receive['Status'])) //发送是否成功，具体的返回值请参考 高级群发/模板消息 的事件推送说明\n\t\t\t$array['Status'] = $this->_receive['Status'];\n\t\tif (isset($this->_receive['MsgID'])) //发送的消息id\n\t\t\t$array['MsgID'] = $this->_receive['MsgID'];\n\n\t\t//以下仅当群发消息时才会有的事件内容\n\t\tif (isset($this->_receive['TotalCount']))     //分组或openid列表内粉丝数量\n\t\t\t$array['TotalCount'] = $this->_receive['TotalCount'];\n\t\tif (isset($this->_receive['FilterCount']))    //过滤（过滤是指特定地区、性别的过滤、用户设置拒收的过滤，用户接收已超4条的过滤）后，准备发送的粉丝数\n\t\t\t$array['FilterCount'] = $this->_receive['FilterCount'];\n\t\tif (isset($this->_receive['SentCount']))     //发送成功的粉丝数\n\t\t\t$array['SentCount'] = $this->_receive['SentCount'];\n\t\tif (isset($this->_receive['ErrorCount']))    //发送失败的粉丝数\n\t\t\t$array['ErrorCount'] = $this->_receive['ErrorCount'];\n\t\tif (isset($array) && count($array) > 0) {\n\t\t    return $array;\n\t\t} else {\n\t\t    return false;\n\t\t}\n\t}\n\n\t/**\n\t * 获取多客服会话状态推送事件 - 接入会话\n\t * 当Event为 kfcreatesession 即接入会话\n\t * @return string | boolean  返回分配到的客服\n\t */\n\tpublic function getRevKFCreate(){\n\t\tif (isset($this->_receive['KfAccount'])){\n\t\t\treturn $this->_receive['KfAccount'];\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\t/**\n\t * 获取多客服会话状态推送事件 - 关闭会话\n\t * 当Event为 kfclosesession 即关闭会话\n\t * @return string | boolean  返回分配到的客服\n\t */\n\tpublic function getRevKFClose(){\n\t    if (isset($this->_receive['KfAccount'])){\n\t        return $this->_receive['KfAccount'];\n\t    } else\n\t        return false;\n\t}\n\n\t/**\n\t * 获取多客服会话状态推送事件 - 转接会话\n\t * 当Event为 kfswitchsession 即转接会话\n\t * @return array | boolean  返回分配到的客服\n\t * {\n\t *     'FromKfAccount' => '',      //原接入客服\n\t *     'ToKfAccount' => ''            //转接到客服\n\t * }\n\t */\n\tpublic function getRevKFSwitch(){\n\t    if (isset($this->_receive['FromKfAccount']))     //原接入客服\n\t        $array['FromKfAccount'] = $this->_receive['FromKfAccount'];\n\t    if (isset($this->_receive['ToKfAccount']))    //转接到客服\n\t        $array['ToKfAccount'] = $this->_receive['ToKfAccount'];\n\t    if (isset($array) && count($array) > 0) {\n\t        return $array;\n\t    } else {\n\t        return false;\n\t    }\n\t}\n\n\t/**\n\t * 获取卡券事件推送 - 卡卷审核是否通过\n\t * 当Event为 card_pass_check(审核通过) 或 card_not_pass_check(未通过)\n\t * @return string|boolean  返回卡券ID\n\t */\n\tpublic function getRevCardPass(){\n\t    if (isset($this->_receive['CardId']))\n\t        return $this->_receive['CardId'];\n\t    else\n\t        return false;\n\t}\n\n\t/**\n\t * 获取卡券事件推送 - 领取卡券\n\t * 当Event为 user_get_card(用户领取卡券)\n\t * @return array|boolean\n\t */\n\tpublic function getRevCardGet(){\n\t    if (isset($this->_receive['CardId']))     //卡券 ID\n\t        $array['CardId'] = $this->_receive['CardId'];\n\t    if (isset($this->_receive['IsGiveByFriend']))    //是否为转赠，1 代表是，0 代表否。\n\t        $array['IsGiveByFriend'] = $this->_receive['IsGiveByFriend'];\n\t        $array['OldUserCardCode'] = $this->_receive['OldUserCardCode'];\n\t    if (isset($this->_receive['UserCardCode']) && !empty($this->_receive['UserCardCode'])) //code 序列号。自定义 code 及非自定义 code的卡券被领取后都支持事件推送。\n\t        $array['UserCardCode'] = $this->_receive['UserCardCode'];\n\t    if (isset($array) && count($array) > 0) {\n\t        return $array;\n\t    } else {\n\t        return false;\n\t    }\n\t}\n\n\t/**\n\t * 获取卡券事件推送 - 删除卡券\n\t * 当Event为 user_del_card(用户删除卡券)\n\t * @return array|boolean\n\t */\n\tpublic function getRevCardDel(){\n\t    if (isset($this->_receive['CardId']))     //卡券 ID\n\t        $array['CardId'] = $this->_receive['CardId'];\n\t    if (isset($this->_receive['UserCardCode']) && !empty($this->_receive['UserCardCode'])) //code 序列号。自定义 code 及非自定义 code的卡券被领取后都支持事件推送。\n\t        $array['UserCardCode'] = $this->_receive['UserCardCode'];\n\t    if (isset($array) && count($array) > 0) {\n\t        return $array;\n\t    } else {\n\t        return false;\n\t    }\n\t}\n\n\t/**\n\t * 获取订单ID - 订单付款通知\n\t * 当Event为 merchant_order(订单付款通知)\n\t * @return orderId|boolean\n\t */\n\tpublic function getRevOrderId(){\n\t\tif (isset($this->_receive['OrderId']))     //订单 ID\n\t\t\treturn $this->_receive['OrderId'];\n\t\telse\n\t\t\treturn false;\n\t}\n\n\tpublic static function xmlSafeStr($str)\n\t{\n\t\treturn '<![CDATA['.preg_replace(\"/[\\\\x00-\\\\x08\\\\x0b-\\\\x0c\\\\x0e-\\\\x1f]/\",'',$str).']]>';\n\t}\n\n\t/**\n\t * 数据XML编码\n\t * @param mixed $data 数据\n\t * @return string\n\t */\n\tpublic static function data_to_xml($data) {\n\t    $xml = '';\n\t    foreach ($data as $key => $val) {\n\t        is_numeric($key) && $key = \"item id=\\\"$key\\\"\";\n\t        $xml    .=  \"<$key>\";\n\t        $xml    .=  ( is_array($val) || is_object($val)) ? self::data_to_xml($val)  : self::xmlSafeStr($val);\n\t        list($key, ) = explode(' ', $key);\n\t        $xml    .=  \"</$key>\";\n\t    }\n\t    return $xml;\n\t}\n\n\t/**\n\t * XML编码\n\t * @param mixed $data 数据\n\t * @param string $root 根节点名\n\t * @param string $item 数字索引的子节点名\n\t * @param string $attr 根节点属性\n\t * @param string $id   数字索引子节点key转换的属性名\n\t * @param string $encoding 数据编码\n\t * @return string\n\t*/\n\tpublic function xml_encode($data, $root='xml', $item='item', $attr='', $id='id', $encoding='utf-8') {\n\t    if(is_array($attr)){\n\t        $_attr = array();\n\t        foreach ($attr as $key => $value) {\n\t            $_attr[] = \"{$key}=\\\"{$value}\\\"\";\n\t        }\n\t        $attr = implode(' ', $_attr);\n\t    }\n\t    $attr   = trim($attr);\n\t    $attr   = empty($attr) ? '' : \" {$attr}\";\n\t    $xml   = \"<{$root}{$attr}>\";\n\t    $xml   .= self::data_to_xml($data, $item, $id);\n\t    $xml   .= \"</{$root}>\";\n\t    return $xml;\n\t}\n\n\t/**\n\t * 过滤文字回复\\r\\n换行符\n\t * @param string $text\n\t * @return string|mixed\n\t */\n\tprivate function _auto_text_filter($text) {\n\t\tif (!$this->_text_filter) return $text;\n\t\treturn str_replace(\"\\r\\n\", \"\\n\", $text);\n\t}\n\n\t/**\n\t * 设置回复消息\n\t * Example: $obj->text('hello')->reply();\n\t * @param string $text\n\t */\n\tpublic function text($text='')\n\t{\n\t\t$FuncFlag = $this->_funcflag ? 1 : 0;\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'MsgType'=>self::MSGTYPE_TEXT,\n\t\t\t'Content'=>$this->_auto_text_filter($text),\n\t\t\t'CreateTime'=>time(),\n\t\t\t'FuncFlag'=>$FuncFlag\n\t\t);\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\t/**\n\t * 设置回复消息\n\t * Example: $obj->image('media_id')->reply();\n\t * @param string $mediaid\n\t */\n\tpublic function image($mediaid='')\n\t{\n\t\t$FuncFlag = $this->_funcflag ? 1 : 0;\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'MsgType'=>self::MSGTYPE_IMAGE,\n\t\t\t'Image'=>array('MediaId'=>$mediaid),\n\t\t\t'CreateTime'=>time(),\n\t\t\t'FuncFlag'=>$FuncFlag\n\t\t);\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 设置回复消息\n\t * Example: $obj->voice('media_id')->reply();\n\t * @param string $mediaid\n\t */\n\tpublic function voice($mediaid='')\n\t{\n\t\t$FuncFlag = $this->_funcflag ? 1 : 0;\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'MsgType'=>self::MSGTYPE_VOICE,\n\t\t\t'Voice'=>array('MediaId'=>$mediaid),\n\t\t\t'CreateTime'=>time(),\n\t\t\t'FuncFlag'=>$FuncFlag\n\t\t);\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 设置回复消息\n\t * Example: $obj->video('media_id','title','description')->reply();\n\t * @param string $mediaid\n\t */\n\tpublic function video($mediaid='',$title='',$description='')\n\t{\n\t\t$FuncFlag = $this->_funcflag ? 1 : 0;\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'MsgType'=>self::MSGTYPE_VIDEO,\n\t\t\t'Video'=>array(\n\t\t\t        'MediaId'=>$mediaid,\n\t\t\t        'Title'=>$title,\n\t\t\t        'Description'=>$description\n\t\t\t),\n\t\t\t'CreateTime'=>time(),\n\t\t\t'FuncFlag'=>$FuncFlag\n\t\t);\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 设置回复音乐\n\t * @param string $title\n\t * @param string $desc\n\t * @param string $musicurl\n\t * @param string $hgmusicurl\n\t * @param string $thumbmediaid 音乐图片缩略图的媒体id，非必须\n\t */\n\tpublic function music($title,$desc,$musicurl,$hgmusicurl='',$thumbmediaid='') {\n\t\t$FuncFlag = $this->_funcflag ? 1 : 0;\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'CreateTime'=>time(),\n\t\t\t'MsgType'=>self::MSGTYPE_MUSIC,\n\t\t\t'Music'=>array(\n\t\t\t\t'Title'=>$title,\n\t\t\t\t'Description'=>$desc,\n\t\t\t\t'MusicUrl'=>$musicurl,\n\t\t\t\t'HQMusicUrl'=>$hgmusicurl\n\t\t\t),\n\t\t\t'FuncFlag'=>$FuncFlag\n\t\t);\n\t\tif ($thumbmediaid) {\n\t\t\t$msg['Music']['ThumbMediaId'] = $thumbmediaid;\n\t\t}\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 设置回复图文\n\t * @param array $newsData\n\t * 数组结构:\n\t *  array(\n\t *  \t\"0\"=>array(\n\t *  \t\t'Title'=>'msg title',\n\t *  \t\t'Description'=>'summary text',\n\t *  \t\t'PicUrl'=>'http://www.domain.com/1.jpg',\n\t *  \t\t'Url'=>'http://www.domain.com/1.html'\n\t *  \t),\n\t *  \t\"1\"=>....\n\t *  )\n\t */\n\tpublic function news($newsData=array())\n\t{\n\t\t$FuncFlag = $this->_funcflag ? 1 : 0;\n\t\t$count = count($newsData);\n\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'MsgType'=>self::MSGTYPE_NEWS,\n\t\t\t'CreateTime'=>time(),\n\t\t\t'ArticleCount'=>$count,\n\t\t\t'Articles'=>$newsData,\n\t\t\t'FuncFlag'=>$FuncFlag\n\t\t);\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t *\n\t * 回复微信服务器, 此函数支持链式操作\n\t * Example: $this->text('msg tips')->reply();\n\t * @param string $msg 要发送的信息, 默认取$this->_msg\n\t * @param bool $return 是否返回信息而不抛出到浏览器 默认:否\n\t */\n\tpublic function reply($msg=array(),$return = false)\n\t{\n\t\tif (empty($msg)) {\n\t\t    if (empty($this->_msg))   //防止不先设置回复内容，直接调用reply方法导致异常\n\t\t        return false;\n\t\t\t$msg = $this->_msg;\n\t\t}\n\t\t$xmldata=  $this->xml_encode($msg);\n\t\t$this->log($xmldata);\n\t\tif ($this->encrypt_type == 'aes') { //如果来源消息为加密方式\n\t\t    $pc = new Prpcrypt($this->encodingAesKey);\n\t\t    $array = $pc->encrypt($xmldata, $this->appid);\n\t\t    $ret = $array[0];\n\t\t    if ($ret != 0) {\n\t\t        $this->log('encrypt err!');\n\t\t        return false;\n\t\t    }\n\t\t    $timestamp = time();\n\t\t    $nonce = rand(77,999)*rand(605,888)*rand(11,99);\n\t\t    $encrypt = $array[1];\n\t\t    $tmpArr = array($this->token, $timestamp, $nonce,$encrypt);//比普通公众平台多了一个加密的密文\n\t\t    sort($tmpArr, SORT_STRING);\n\t\t    $signature = implode($tmpArr);\n\t\t    $signature = sha1($signature);\n\t\t    $xmldata = $this->generate($encrypt, $signature, $timestamp, $nonce);\n\t\t    $this->log($xmldata);\n\t\t}\n\t\tif ($return)\n\t\t\treturn $xmldata;\n\t\telse\n\t\t\techo $xmldata;\n\t}\n\n    /**\n     * xml格式加密，仅请求为加密方式时再用\n     */\n\tprivate function generate($encrypt, $signature, $timestamp, $nonce)\n\t{\n\t    //格式化加密信息\n\t    $format = \"<xml>\n<Encrypt><![CDATA[%s]]></Encrypt>\n<MsgSignature><![CDATA[%s]]></MsgSignature>\n<TimeStamp>%s</TimeStamp>\n<Nonce><![CDATA[%s]]></Nonce>\n</xml>\";\n\t    return sprintf($format, $encrypt, $signature, $timestamp, $nonce);\n\t}\n\n\t/**\n\t * GET 请求\n\t * @param string $url\n\t */\n\tprivate function http_get($url){\n\t\t$oCurl = curl_init();\n\t\tif(stripos($url,\"https://\")!==FALSE){\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1\n\t\t}\n\t\tcurl_setopt($oCurl, CURLOPT_URL, $url);\n\t\tcurl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );\n\t\t$sContent = curl_exec($oCurl);\n\t\t$aStatus = curl_getinfo($oCurl);\n\t\tcurl_close($oCurl);\n\t\tif(intval($aStatus[\"http_code\"])==200){\n\t\t\treturn $sContent;\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * POST 请求\n\t * @param string $url\n\t * @param array $param\n\t * @param boolean $post_file 是否文件上传\n\t * @return string content\n\t */\n\tprivate function http_post($url,$param,$post_file=false){\n\t\t$oCurl = curl_init();\n\t\tif(stripos($url,\"https://\")!==FALSE){\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);\n\t\t\tcurl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1\n\t\t}\n\t        if (PHP_VERSION_ID >= 50500 && class_exists('\\CURLFile')) {\n\t            \t$is_curlFile = true;\n\t        } else {\n\t        \t$is_curlFile = false;\n\t            \tif (defined('CURLOPT_SAFE_UPLOAD')) {\n\t                \tcurl_setopt($oCurl, CURLOPT_SAFE_UPLOAD, false);\n\t            \t}\n\t        }\n\t\tif (is_string($param)) {\n\t            \t$strPOST = $param;\n\t        }elseif($post_file) {\n\t            \tif($is_curlFile) {\n\t\t                foreach ($param as $key => $val) {\n\t\t                    \tif (substr($val, 0, 1) == '@') {\n\t\t                        \t$param[$key] = new \\CURLFile(realpath(substr($val,1)));\n\t\t                    \t}\n\t\t                }\n\t            \t}\n\t\t\t$strPOST = $param;\n\t\t} else {\n\t\t\t$aPOST = array();\n\t\t\tforeach($param as $key=>$val){\n\t\t\t\t$aPOST[] = $key.\"=\".urlencode($val);\n\t\t\t}\n\t\t\t$strPOST =  join(\"&\", $aPOST);\n\t\t}\n\t\tcurl_setopt($oCurl, CURLOPT_URL, $url);\n\t\tcurl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );\n\t\tcurl_setopt($oCurl, CURLOPT_POST,true);\n\t\tcurl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);\n\t\t$sContent = curl_exec($oCurl);\n\t\t$aStatus = curl_getinfo($oCurl);\n\t\tcurl_close($oCurl);\n\t\tif(intval($aStatus[\"http_code\"])==200){\n\t\t\treturn $sContent;\n\t\t}else{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * 设置缓存，按需重载\n\t * @param string $cachename\n\t * @param mixed $value\n\t * @param int $expired\n\t * @return boolean\n\t */\n\tprotected function setCache($cachename,$value,$expired){\n\t\t//TODO: set cache implementation\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取缓存，按需重载\n\t * @param string $cachename\n\t * @return mixed\n\t */\n\tprotected function getCache($cachename){\n\t\t//TODO: get cache implementation\n\t\treturn false;\n\t}\n\n\t/**\n\t * 清除缓存，按需重载\n\t * @param string $cachename\n\t * @return boolean\n\t */\n\tprotected function removeCache($cachename){\n\t\t//TODO: remove cache implementation\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取access_token\n\t * @param string $appid 如在类初始化时已提供，则可为空\n\t * @param string $appsecret 如在类初始化时已提供，则可为空\n\t * @param string $token 手动指定access_token，非必要情况不建议用\n\t */\n\tpublic function checkAuth($appid='',$appsecret='',$token=''){\n\t\tif (!$appid || !$appsecret) {\n\t\t\t$appid = $this->appid;\n\t\t\t$appsecret = $this->appsecret;\n\t\t}\n\t\tif ($token) { //手动指定token，优先使用\n\t\t    $this->access_token=$token;\n\t\t    return $this->access_token;\n\t\t}\n\n\t\t$authname = 'wechat_access_token'.$appid;\n\t\tif ($rs = $this->getCache($authname))  {\n\t\t\t$this->access_token = $rs;\n\t\t\treturn $rs;\n\t\t}\n\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::AUTH_URL.'appid='.$appid.'&secret='.$appsecret);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$this->access_token = $json['access_token'];\n\t\t\t$expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;\n\t\t\t$this->setCache($authname,$this->access_token,$expire);\n\t\t\treturn $this->access_token;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 删除验证数据\n\t * @param string $appid\n\t */\n\tpublic function resetAuth($appid=''){\n\t\tif (!$appid) $appid = $this->appid;\n\t\t$this->access_token = '';\n\t\t$authname = 'wechat_access_token'.$appid;\n\t\t$this->removeCache($authname);\n\t\treturn true;\n\t}\n\n\t/**\n\t * 删除JSAPI授权TICKET\n\t * @param string $appid 用于多个appid时使用\n\t */\n\tpublic function resetJsTicket($appid=''){\n\t\tif (!$appid) $appid = $this->appid;\n\t\t$this->jsapi_ticket = '';\n\t\t$authname = 'wechat_jsapi_ticket'.$appid;\n\t\t$this->removeCache($authname);\n\t\treturn true;\n\t}\n\n\t/**\n\t * 获取JSAPI授权TICKET\n\t * @param string $appid 用于多个appid时使用,可空\n\t * @param string $jsapi_ticket 手动指定jsapi_ticket，非必要情况不建议用\n\t */\n\tpublic function getJsTicket($appid='',$jsapi_ticket=''){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\tif (!$appid) $appid = $this->appid;\n\t\tif ($jsapi_ticket) { //手动指定token，优先使用\n\t\t    $this->jsapi_ticket = $jsapi_ticket;\n\t\t    return $this->jsapi_ticket;\n\t\t}\n\t\t$authname = 'wechat_jsapi_ticket'.$appid;\n\t\tif ($rs = $this->getCache($authname))  {\n\t\t\t$this->jsapi_ticket = $rs;\n\t\t\treturn $rs;\n\t\t}\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::GET_TICKET_URL.'access_token='.$this->access_token.'&type=jsapi');\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$this->jsapi_ticket = $json['ticket'];\n\t\t\t$expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;\n\t\t\t$this->setCache($authname,$this->jsapi_ticket,$expire);\n\t\t\treturn $this->jsapi_ticket;\n\t\t}\n\t\treturn false;\n\t}\n\n\n\t/**\n\t * 获取JsApi使用签名\n\t * @param string $url 网页的URL，自动处理#及其后面部分\n\t * @param string $timestamp 当前时间戳 (为空则自动生成)\n\t * @param string $noncestr 随机串 (为空则自动生成)\n\t * @param string $appid 用于多个appid时使用,可空\n\t * @return array|bool 返回签名字串\n\t */\n\tpublic function getJsSign($url, $timestamp=0, $noncestr='', $appid=''){\n\t    if (!$this->jsapi_ticket && !$this->getJsTicket($appid) || !$url) return false;\n\t    if (!$timestamp)\n\t        $timestamp = time();\n\t    if (!$noncestr)\n\t        $noncestr = $this->generateNonceStr();\n\t    $ret = strpos($url,'#');\n\t    if ($ret)\n\t        $url = substr($url,0,$ret);\n\t    $url = trim($url);\n\t    if (empty($url))\n\t        return false;\n\t    $arrdata = array(\"timestamp\" => $timestamp, \"noncestr\" => $noncestr, \"url\" => $url, \"jsapi_ticket\" => $this->jsapi_ticket);\n\t    $sign = $this->getSignature($arrdata);\n\t    if (!$sign)\n\t        return false;\n\t    $signPackage = array(\n\t            \"appId\"     => $this->appid,\n\t            \"nonceStr\"  => $noncestr,\n\t            \"timestamp\" => $timestamp,\n\t            \"url\"       => $url,\n\t            \"signature\" => $sign\n\t    );\n\t    return $signPackage;\n\t}\n\n    /**\n     * 获取卡券签名cardSign\n     * @param string $card_type 卡券的类型，不可为空，官方jssdk文档说这个值可空，但签名验证工具又必填这个值，官方文档到处是坑，\n     * @param string $card_id 卡券的ID，可空\n     * @param string $location_id 卡券的适用门店ID，可空\n     * @param string $timestamp 当前时间戳 (为空则自动生成)\n     * @param string $noncestr 随机串 (为空则自动生成)\n     * @param string $appid 用于多个appid时使用,可空\n     * @return array|bool 返回签名字串\n     */\n    public function getCardSign($card_type='',$card_id='',$code='',$location_id='',$timestamp=0, $noncestr='', $appid=''){\n        if (!$this->api_ticket && !$this->getJsCardTicket($appid)) return false;\n        if (!$timestamp)\n            $timestamp = time();\n        if (!$noncestr)\n            $noncestr = $this->generateNonceStr();\n        $arrdata = array(\"api_ticket\" => $this->api_ticket,\"app_id\" => $this->appid,\"card_id\" => $card_id,\"code\" => $code,\"card_type\" => $card_type,\"location_id\" => $location_id,\"timestamp\" => $timestamp, \"noncestr\" => $noncestr );\n        $sign = $this->getTicketSignature($arrdata);\n        if (!$sign)\n            return false;\n        $signPackage = array(\n            \"cardType\"     => $card_type,\n            \"cardId\"       => $card_id,\n            \"shopId\"       => $location_id,         //location_id就是shopId\n            \"nonceStr\"  => $noncestr,\n            \"timestamp\" => $timestamp,\n            \"cardSign\" => $sign\n        );\n        return $signPackage;\n    }\n\n\t/**\n\t * 微信api不支持中文转义的json结构\n\t * @param array $arr\n\t */\n\tstatic function json_encode($arr) {\n\t\treturn json_encode($arr,JSON_UNESCAPED_UNICODE);\n\t}\n\n\t/**\n\t * 获取签名\n\t * @param array $arrdata 签名数组\n\t * @param string $method 签名方法\n\t * @return boolean|string 签名值\n\t */\n\tpublic function getSignature($arrdata,$method=\"sha1\") {\n\t\tif (!function_exists($method)) return false;\n\t\tksort($arrdata);\n\t\t$paramstring = \"\";\n\t\tforeach($arrdata as $key => $value)\n\t\t{\n\t\t\tif(strlen($paramstring) == 0)\n\t\t\t\t$paramstring .= $key . \"=\" . $value;\n\t\t\telse\n\t\t\t\t$paramstring .= \"&\" . $key . \"=\" . $value;\n\t\t}\n\t\t$Sign = $method($paramstring);\n\t\treturn $Sign;\n\t}\n\n\t/**\n\t * 获取微信卡券api_ticket\n\t * @param string $appid 用于多个appid时使用,可空\n\t * @param string $api_ticket 手动指定api_ticket，非必要情况不建议用\n\t */\n\tpublic function getJsCardTicket($appid='',$api_ticket=''){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\tif (!$appid) $appid = $this->appid;\n\t\tif ($api_ticket) { //手动指定token，优先使用\n\t\t    $this->api_ticket = $api_ticket;\n\t\t    return $this->api_ticket;\n\t\t}\n\t\t$authname = 'wechat_api_ticket_wxcard'.$appid;\n\t\tif ($rs = $this->getCache($authname))  {\n\t\t\t$this->api_ticket = $rs;\n\t\t\treturn $rs;\n\t\t}\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::GET_TICKET_URL.'access_token='.$this->access_token.'&type=wx_card');\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$this->api_ticket = $json['ticket'];\n\t\t\t$expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;\n\t\t\t$this->setCache($authname,$this->api_ticket,$expire);\n\t\t\treturn $this->api_ticket;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取微信卡券签名\n\t * @param array $arrdata 签名数组\n\t * @param string $method 签名方法\n\t * @return boolean|string 签名值\n\t */\n\tpublic function getTicketSignature($arrdata,$method=\"sha1\") {\n\t\tif (!function_exists($method)) return false;\n\t\t$newArray = array();\n\t\tforeach($arrdata as $key => $value)\n\t\t{\n\t\t\tarray_push($newArray,(string)$value);\n\t\t}\n\t\tsort($newArray,SORT_STRING);\n\t\treturn $method(implode($newArray));\n\t}\n\n\t/**\n\t * 生成随机字串\n\t * @param number $length 长度，默认为16，最长为32字节\n\t * @return string\n\t */\n\tpublic function generateNonceStr($length=16){\n\t\t// 密码字符集，可任意添加你需要的字符\n\t\t$chars = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\n\t\t$str = \"\";\n\t\tfor($i = 0; $i < $length; $i++)\n\t\t{\n\t\t\t$str .= $chars[mt_rand(0, strlen($chars) - 1)];\n\t\t}\n\t\treturn $str;\n\t}\n\n\t/**\n\t * 获取微信服务器IP地址列表\n\t * @return array('127.0.0.1','127.0.0.1')\n\t */\n\tpublic function getServerIp(){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::CALLBACKSERVER_GET_URL.'access_token='.$this->access_token);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json['ip_list'];\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 创建菜单(认证后的订阅号可用)\n\t * @param array $data 菜单数组数据\n\t * example:\n     * \tarray (\n     * \t    'button' => array (\n     * \t      0 => array (\n     * \t        'name' => '扫码',\n     * \t        'sub_button' => array (\n     * \t            0 => array (\n     * \t              'type' => 'scancode_waitmsg',\n     * \t              'name' => '扫码带提示',\n     * \t              'key' => 'rselfmenu_0_0',\n     * \t            ),\n     * \t            1 => array (\n     * \t              'type' => 'scancode_push',\n     * \t              'name' => '扫码推事件',\n     * \t              'key' => 'rselfmenu_0_1',\n     * \t            ),\n     * \t        ),\n     * \t      ),\n     * \t      1 => array (\n     * \t        'name' => '发图',\n     * \t        'sub_button' => array (\n     * \t            0 => array (\n     * \t              'type' => 'pic_sysphoto',\n     * \t              'name' => '系统拍照发图',\n     * \t              'key' => 'rselfmenu_1_0',\n     * \t            ),\n     * \t            1 => array (\n     * \t              'type' => 'pic_photo_or_album',\n     * \t              'name' => '拍照或者相册发图',\n     * \t              'key' => 'rselfmenu_1_1',\n     * \t            )\n     * \t        ),\n     * \t      ),\n     * \t      2 => array (\n     * \t        'type' => 'location_select',\n     * \t        'name' => '发送位置',\n     * \t        'key' => 'rselfmenu_2_0'\n     * \t      ),\n     * \t    ),\n     * \t)\n     * type可以选择为以下几种，其中5-8除了收到菜单事件以外，还会单独收到对应类型的信息。\n     * 1、click：点击推事件\n     * 2、view：跳转URL\n     * 3、scancode_push：扫码推事件\n     * 4、scancode_waitmsg：扫码推事件且弹出“消息接收中”提示框\n     * 5、pic_sysphoto：弹出系统拍照发图\n     * 6、pic_photo_or_album：弹出拍照或者相册发图\n     * 7、pic_weixin：弹出微信相册发图器\n     * 8、location_select：弹出地理位置选择器\n\t */\n\tpublic function createMenu($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MENU_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取菜单(认证后的订阅号可用)\n\t * @return array('menu'=>array(....s))\n\t */\n\tpublic function getMenu(){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::MENU_GET_URL.'access_token='.$this->access_token);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 删除菜单(认证后的订阅号可用)\n\t * @return boolean\n\t */\n\tpublic function deleteMenu(){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::MENU_DELETE_URL.'access_token='.$this->access_token);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 创建个性化菜单(认证后的订阅号可用)\n\t * @param array $data\n\t * @return bool\n\t *\n\t */\n\tpublic function addconditionalMenu($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MENU_ADDCONDITIONAL_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 删除个性化菜单(认证后的订阅号可用)\n\t * @param $data {\"menuid\":\"208379533\"}\n\t *\n\t * @return bool\n\t */\n\tpublic function delconditionalMenu($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MENU_DELCONDITIONAL_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 测试个性化菜单匹配结果(认证后的订阅号可用)\n\t * @param $data {\"user_id\":\"weixin\"} user_id可以是粉丝的OpenID，也可以是粉丝的微信号\n\t *\n\t * @return bool|array('button'=>array(....s))\n\t */\n\tpublic function trymatchMenu($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MENU_TRYMATCH_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 上传临时素材，有效期为3天(认证后的订阅号可用)\n\t * 注意：上传大文件时可能需要先调用 set_time_limit(0) 避免超时\n\t * 注意：数组的键值任意，但文件名前必须加@，使用单引号以避免本地路径斜杠被转义\n     * 注意：临时素材的media_id是可复用的！\n\t * @param array $data {\"media\":'@Path\\filename.jpg'}\n\t * @param type 类型：图片:image 语音:voice 视频:video 缩略图:thumb\n\t * @return boolean|array\n\t */\n\tpublic function uploadMedia($data, $type){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t//原先的上传多媒体文件接口使用 self::UPLOAD_MEDIA_URL 前缀\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_UPLOAD_URL.'access_token='.$this->access_token.'&type='.$type,$data,true);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取临时素材(认证后的订阅号可用)\n\t * @param string $media_id 媒体文件id\n\t * @param boolean $is_video 是否为视频文件，默认为否\n\t * @return raw data\n\t */\n\tpublic function getMedia($media_id,$is_video=false){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t//原先的上传多媒体文件接口使用 self::UPLOAD_MEDIA_URL 前缀\n\t\t//如果要获取的素材是视频文件时，不能使用https协议，必须更换成http协议\n\t\t$url_prefix = $is_video?str_replace('https','http',self::API_URL_PREFIX):self::API_URL_PREFIX;\n\t\t$result = $this->http_get($url_prefix.self::MEDIA_GET_URL.'access_token='.$this->access_token.'&media_id='.$media_id);\n\t\tif ($result)\n\t\t{\n            if (is_string($result)) {\n                $json = json_decode($result,true);\n                if (isset($json['errcode'])) {\n                    $this->errCode = $json['errcode'];\n                    $this->errMsg = $json['errmsg'];\n                    return false;\n                }\n            }\n\t\t\treturn $result;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 上传图片，本接口所上传的图片不占用公众号的素材库中图片数量的5000个的限制。图片仅支持jpg/png格式，大小必须在1MB以下。 (认证后的订阅号可用)\n\t * 注意：上传大文件时可能需要先调用 set_time_limit(0) 避免超时\n\t * 注意：数组的键值任意，但文件名前必须加@，使用单引号以避免本地路径斜杠被转义      \n\t * @param array $data {\"media\":'@Path\\filename.jpg'}\n\t * \n\t * @return boolean|array\n\t */\n\tpublic function uploadImg($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t//原先的上传多媒体文件接口使用 self::UPLOAD_MEDIA_URL 前缀\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_UPLOADIMG_URL.'access_token='.$this->access_token,$data,true);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\n    /**\n     * 上传永久素材(认证后的订阅号可用)\n     * 新增的永久素材也可以在公众平台官网素材管理模块中看到\n     * 注意：上传大文件时可能需要先调用 set_time_limit(0) 避免超时\n     * 注意：数组的键值任意，但文件名前必须加@，使用单引号以避免本地路径斜杠被转义\n     * @param array $data {\"media\":'@Path\\filename.jpg'}\n     * @param type 类型：图片:image 语音:voice 视频:video 缩略图:thumb\n     * @param boolean $is_video 是否为视频文件，默认为否\n     * @param array $video_info 视频信息数组，非视频素材不需要提供 array('title'=>'视频标题','introduction'=>'描述')\n     * @return boolean|array\n     */\n    public function uploadForeverMedia($data, $type,$is_video=false,$video_info=array()){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        //#TODO 暂不确定此接口是否需要让视频文件走http协议\n        //如果要获取的素材是视频文件时，不能使用https协议，必须更换成http协议\n        //$url_prefix = $is_video?str_replace('https','http',self::API_URL_PREFIX):self::API_URL_PREFIX;\n        //当上传视频文件时，附加视频文件信息\n        if ($is_video) $data['description'] = self::json_encode($video_info);\n        $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_UPLOAD_URL.'access_token='.$this->access_token.'&type='.$type,$data,true);\n        if ($result)\n        {\n            $json = json_decode($result,true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 上传永久图文素材(认证后的订阅号可用)\n     * 新增的永久素材也可以在公众平台官网素材管理模块中看到\n     * @param array $data 消息结构{\"articles\":[{...}]}\n     * @return boolean|array\n     */\n    public function uploadForeverArticles($data){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_NEWS_UPLOAD_URL.'access_token='.$this->access_token,self::json_encode($data));\n        if ($result)\n        {\n            $json = json_decode($result,true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 修改永久图文素材(认证后的订阅号可用)\n     * 永久素材也可以在公众平台官网素材管理模块中看到\n     * @param string $media_id 图文素材id\n     * @param array $data 消息结构{\"articles\":[{...}]}\n     * @param int $index 更新的文章在图文素材的位置，第一篇为0，仅多图文使用\n     * @return boolean|array\n     */\n    public function updateForeverArticles($media_id,$data,$index=0){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        if (!isset($data['media_id'])) $data['media_id'] = $media_id;\n        if (!isset($data['index'])) $data['index'] = $index;\n        $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_NEWS_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n        if ($result)\n        {\n            $json = json_decode($result,true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 获取永久素材(认证后的订阅号可用)\n     * 返回图文消息数组或二进制数据，失败返回false\n     * @param string $media_id 媒体文件id\n     * @param boolean $is_video 是否为视频文件，默认为否\n     * @return boolean|array|raw data\n     */\n    public function getForeverMedia($media_id,$is_video=false){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $data = array('media_id' => $media_id);\n        //#TODO 暂不确定此接口是否需要让视频文件走http协议\n        //如果要获取的素材是视频文件时，不能使用https协议，必须更换成http协议\n        //$url_prefix = $is_video?str_replace('https','http',self::API_URL_PREFIX):self::API_URL_PREFIX;\n        $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_GET_URL.'access_token='.$this->access_token,self::json_encode($data));\n        if ($result)\n        {\n            if (is_string($result)) {\n                $json = json_decode($result,true);\n                if ($json) {\n                    if (isset($json['errcode'])) {\n                        $this->errCode = $json['errcode'];\n                        $this->errMsg = $json['errmsg'];\n                        return false;\n                    }\n                    return $json;\n                } else {\n                    return $result;\n                }\n            }\n            return $result;\n        }\n        return false;\n    }\n\n    /**\n     * 删除永久素材(认证后的订阅号可用)\n     * @param string $media_id 媒体文件id\n     * @return boolean\n     */\n    public function delForeverMedia($media_id){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $data = array('media_id' => $media_id);\n        $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_DEL_URL.'access_token='.$this->access_token,self::json_encode($data));\n        if ($result)\n        {\n            $json = json_decode($result,true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 获取永久素材列表(认证后的订阅号可用)\n     * @param string $type 素材的类型,图片（image）、视频（video）、语音 （voice）、图文（news）\n     * @param int $offset 全部素材的偏移位置，0表示从第一个素材\n     * @param int $count 返回素材的数量，取值在1到20之间\n     * @return boolean|array\n     * 返回数组格式:\n     * array(\n     *  'total_count'=>0, //该类型的素材的总数\n     *  'item_count'=>0,  //本次调用获取的素材的数量\n     *  'item'=>array()   //素材列表数组，内容定义请参考官方文档\n     * )\n     */\n    public function getForeverList($type,$offset,$count){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $data = array(\n            'type' => $type,\n            'offset' => $offset,\n            'count' => $count,\n        );\n        $result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_FOREVER_BATCHGET_URL.'access_token='.$this->access_token,self::json_encode($data));\n        if ($result)\n        {\n            $json = json_decode($result,true);\n            if (isset($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 获取永久素材总数(认证后的订阅号可用)\n     * @return boolean|array\n     * 返回数组格式:\n     * array(\n     *  'voice_count'=>0, //语音总数量\n     *  'video_count'=>0, //视频总数量\n     *  'image_count'=>0, //图片总数量\n     *  'news_count'=>0   //图文总数量\n     * )\n     */\n    public function getForeverCount(){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_get(self::API_URL_PREFIX.self::MEDIA_FOREVER_COUNT_URL.'access_token='.$this->access_token);\n        if ($result)\n        {\n            $json = json_decode($result,true);\n            if (isset($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n\t/**\n\t * 上传图文消息素材，用于群发(认证后的订阅号可用)\n\t * @param array $data 消息结构{\"articles\":[{...}]}\n\t * @return boolean|array\n\t */\n\tpublic function uploadArticles($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_UPLOADNEWS_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 上传视频素材(认证后的订阅号可用)\n\t * @param array $data 消息结构\n\t * {\n\t *     \"media_id\"=>\"\",     //通过上传媒体接口得到的MediaId\n\t *     \"title\"=>\"TITLE\",    //视频标题\n\t *     \"description\"=>\"Description\"        //视频描述\n\t * }\n\t * @return boolean|array\n\t * {\n\t *     \"type\":\"video\",\n\t *     \"media_id\":\"mediaid\",\n\t *     \"created_at\":1398848981\n\t *  }\n\t */\n\tpublic function uploadMpVideo($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::UPLOAD_MEDIA_URL.self::MEDIA_VIDEO_UPLOAD.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 高级群发消息, 根据OpenID列表群发图文消息(订阅号不可用)\n\t * \t注意：视频需要在调用uploadMedia()方法后，再使用 uploadMpVideo() 方法生成，\n\t *             然后获得的 mediaid 才能用于群发，且消息类型为 mpvideo 类型。\n\t * @param array $data 消息结构\n\t * {\n\t *     \"touser\"=>array(\n\t *         \"OPENID1\",\n\t *         \"OPENID2\"\n\t *     ),\n\t *      \"msgtype\"=>\"mpvideo\",\n\t *      // 在下面5种类型中选择对应的参数内容\n\t *      // mpnews | voice | image | mpvideo => array( \"media_id\"=>\"MediaId\")\n\t *      // text => array ( \"content\" => \"hello\")\n\t * }\n\t * @return boolean|array\n\t */\n\tpublic function sendMassMessage($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MASS_SEND_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 高级群发消息, 根据群组id群发图文消息(认证后的订阅号可用)\n\t * \t注意：视频需要在调用uploadMedia()方法后，再使用 uploadMpVideo() 方法生成，\n\t *             然后获得的 mediaid 才能用于群发，且消息类型为 mpvideo 类型。\n\t * @param array $data 消息结构\n\t * {\n\t *     \"filter\"=>array(\n\t *         \"is_to_all\"=>False,     //是否群发给所有用户.True不用分组id，False需填写分组id\n\t *         \"group_id\"=>\"2\"     //群发的分组id\n\t *     ),\n\t *      \"msgtype\"=>\"mpvideo\",\n\t *      // 在下面5种类型中选择对应的参数内容\n\t *      // mpnews | voice | image | mpvideo => array( \"media_id\"=>\"MediaId\")\n\t *      // text => array ( \"content\" => \"hello\")\n\t * }\n\t * @return boolean|array\n\t */\n\tpublic function sendGroupMassMessage($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MASS_SEND_GROUP_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 高级群发消息, 删除群发图文消息(认证后的订阅号可用)\n\t * @param int $msg_id 消息id\n\t * @return boolean|array\n\t */\n\tpublic function deleteMassMessage($msg_id){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MASS_DELETE_URL.'access_token='.$this->access_token,self::json_encode(array('msg_id'=>$msg_id)));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 高级群发消息, 预览群发消息(认证后的订阅号可用)\n\t * \t注意：视频需要在调用uploadMedia()方法后，再使用 uploadMpVideo() 方法生成，\n\t *             然后获得的 mediaid 才能用于群发，且消息类型为 mpvideo 类型。\n\t * @param array $data 消息结构\n\t * {\n\t *     \"touser\"=>\"OPENID\",\n\t *      \"msgtype\"=>\"mpvideo\",\n\t *      // 在下面5种类型中选择对应的参数内容\n\t *      // mpnews | voice | image | mpvideo => array( \"media_id\"=>\"MediaId\")\n\t *      // text => array ( \"content\" => \"hello\")\n\t * }\n\t * @return boolean|array\n\t */\n\tpublic function previewMassMessage($data){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::MASS_PREVIEW_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 高级群发消息, 查询群发消息发送状态(认证后的订阅号可用)\n\t * @param int $msg_id 消息id\n\t * @return boolean|array\n\t * {\n\t *     \"msg_id\":201053012,     //群发消息后返回的消息id\n\t *     \"msg_status\":\"SEND_SUCCESS\" //消息发送后的状态，SENDING表示正在发送 SEND_SUCCESS表示发送成功\n\t * }\n\t */\n\tpublic function queryMassMessage($msg_id){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::MASS_QUERY_URL.'access_token='.$this->access_token,self::json_encode(array('msg_id'=>$msg_id)));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 创建二维码ticket\n\t * @param int|string $scene_id 自定义追踪id,临时二维码只能用数值型\n\t * @param int $type 0:临时二维码；1:数值型永久二维码(此时expire参数无效)；2:字符串型永久二维码(此时expire参数无效)\n\t * @param int $expire 临时二维码有效期，最大为604800秒\n\t * @return array('ticket'=>'qrcode字串','expire_seconds'=>604800,'url'=>'二维码图片解析后的地址')\n\t */\n\tpublic function getQRCode($scene_id,$type=0,$expire=604800){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\tif (!isset($scene_id)) return false;\n\t\tswitch ($type) {\n\t\t\tcase '0':\n\t\t\t\tif (!is_numeric($scene_id))\n\t\t\t\t\treturn false;\n\t\t\t\t$action_name = 'QR_SCENE';\n\t\t\t\t$action_info = array('scene'=>(array('scene_id'=>$scene_id)));\n\t\t\t\tbreak;\n\n\t\t\tcase '1':\n\t\t\t\tif (!is_numeric($scene_id))\n\t\t\t\t\treturn false;\n\t\t\t\t$action_name = 'QR_LIMIT_SCENE';\n\t\t\t\t$action_info = array('scene'=>(array('scene_id'=>$scene_id)));\n\t\t\t\tbreak;\n\n\t\t\tcase '2':\n\t\t\t\tif (!is_string($scene_id))\n\t\t\t\t\treturn false;\n\t\t\t\t$action_name = 'QR_LIMIT_STR_SCENE';\n\t\t\t\t$action_info = array('scene'=>(array('scene_str'=>$scene_id)));\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\n\t\t$data = array(\n\t\t\t'action_name'    => $action_name,\n\t\t\t'expire_seconds' => $expire,\n\t\t\t'action_info'    => $action_info\n\t\t);\n\t\tif ($type) {\n\t\t\tunset($data['expire_seconds']);\n\t\t}\n\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::QRCODE_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result) {\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取二维码图片\n\t * @param string $ticket 传入由getQRCode方法生成的ticket参数\n\t * @return string url 返回http地址\n\t */\n\tpublic function getQRUrl($ticket) {\n\t\treturn self::QRCODE_IMG_URL.urlencode($ticket);\n\t}\n\n\t/**\n\t * 长链接转短链接接口\n\t * @param string $long_url 传入要转换的长url\n\t * @return boolean|string url 成功则返回转换后的短url\n\t */\n\tpublic function getShortUrl($long_url){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $data = array(\n            'action'=>'long2short',\n            'long_url'=>$long_url\n\t    );\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::SHORT_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json['short_url'];\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取统计数据\n\t * @param string $type  数据分类(user|article|upstreammsg|interface)分别为(用户分析|图文分析|消息分析|接口分析)\n\t * @param string $subtype   数据子分类，参考 DATACUBE_URL_ARR 常量定义部分 或者README.md说明文档\n\t * @param string $begin_date 开始时间\n\t * @param string $end_date   结束时间\n\t * @return boolean|array 成功返回查询结果数组，其定义请看官方文档\n\t */\n\tpublic function getDatacube($type,$subtype,$begin_date,$end_date=''){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t\tif (!isset(self::$DATACUBE_URL_ARR[$type]) || !isset(self::$DATACUBE_URL_ARR[$type][$subtype]))\n\t\t\treturn false;\n\t    $data = array(\n            'begin_date'=>$begin_date,\n            'end_date'=>$end_date?$end_date:$begin_date\n\t    );\n\t    $result = $this->http_post(self::API_BASE_URL_PREFIX.self::$DATACUBE_URL_ARR[$type][$subtype].'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return isset($json['list'])?$json['list']:$json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 批量获取关注用户列表\n\t * @param unknown $next_openid\n\t */\n\tpublic function getUserList($next_openid=''){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::USER_GET_URL.'access_token='.$this->access_token.'&next_openid='.$next_openid);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取关注者详细信息\n\t * @param string $openid\n\t * @param string $lang 返回国家地区语言版本，zh_CN 简体，zh_TW 繁体，en 英语\n\t * @return array {subscribe,openid,nickname,sex,city,province,country,language,headimgurl,subscribe_time,[unionid]}\n\t * 注意：unionid字段 只有在用户将公众号绑定到微信开放平台账号后，才会出现。建议调用前用isset()检测一下\n\t */\n\tpublic function getUserInfo($openid, $lang = 'zh_CN'){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::USER_INFO_URL.'access_token='.$this->access_token.'&openid='.$openid.'&lang='.$lang);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\t/**\n\t * 批量获取关注者详细信息\n\t * @param array $openids user_list{{'openid:xxxxxx'},{},{}}\n\t * @return array user_info_list{subscribe,openid,nickname,sex,city,province,country,language,headimgurl,subscribe_time,[unionid]}{}{}...\n\t * 注意：unionid字段 只有在用户将公众号绑定到微信开放平台账号后，才会出现。建议调用前用isset()检测一下\n\t */\n\tpublic function getUsersInfo($openids){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::USERS_INFO_URL.'access_token='.$this->access_token,json_encode($openids));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 设置用户备注名\n\t * @param string $openid\n\t * @param string $remark 备注名\n\t * @return boolean|array\n\t */\n\tpublic function updateUserRemark($openid,$remark){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $data = array(\n\t\t\t'openid'=>$openid,\n\t\t\t'remark'=>$remark\n\t    );\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::USER_UPDATEREMARK_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取用户分组列表\n\t * @return boolean|array\n\t */\n\tpublic function getGroup(){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::GROUP_GET_URL.'access_token='.$this->access_token);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (isset($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取用户所在分组\n\t * @param string $openid\n\t * @return boolean|int 成功则返回用户分组id\n\t */\n\tpublic function getUserGroup($openid){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $data = array(\n\t            'openid'=>$openid\n\t    );\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::USER_GROUP_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        } else\n                if (isset($json['groupid'])) return $json['groupid'];\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 新增自定分组\n\t * @param string $name 分组名称\n\t * @return boolean|array\n\t */\n\tpublic function createGroup($name){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$data = array(\n\t\t\t\t'group'=>array('name'=>$name)\n\t\t);\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::GROUP_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 更改分组名称\n\t * @param int $groupid 分组id\n\t * @param string $name 分组名称\n\t * @return boolean|array\n\t */\n\tpublic function updateGroup($groupid,$name){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$data = array(\n\t\t\t\t'group'=>array('id'=>$groupid,'name'=>$name)\n\t\t);\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::GROUP_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 移动用户分组\n\t * @param int $groupid 分组id\n\t * @param string $openid 用户openid\n\t * @return boolean|array\n\t */\n\tpublic function updateGroupMembers($groupid,$openid){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$data = array(\n\t\t\t\t'openid'=>$openid,\n\t\t\t\t'to_groupid'=>$groupid\n\t\t);\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::GROUP_MEMBER_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 批量移动用户分组\n\t * @param int $groupid 分组id\n\t * @param string $openid_list 用户openid数组,一次不能超过50个\n\t * @return boolean|array\n\t */\n\tpublic function batchUpdateGroupMembers($groupid,$openid_list){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$data = array(\n\t\t\t\t'openid_list'=>$openid_list,\n\t\t\t\t'to_groupid'=>$groupid\n\t\t);\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::GROUP_MEMBER_BATCHUPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 发送客服消息\n\t * @param array $data 消息结构{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{...}}\n\t * @return boolean|array\n\t */\n\tpublic function sendCustomMessage($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::CUSTOM_SEND_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * oauth 授权跳转接口\n\t * @param string $callback 回调URI\n\t * @return string\n\t */\n\tpublic function getOauthRedirect($callback,$state='',$scope='snsapi_userinfo'){\n\t\treturn self::OAUTH_PREFIX.self::OAUTH_AUTHORIZE_URL.'appid='.$this->appid.'&redirect_uri='.urlencode($callback).'&response_type=code&scope='.$scope.'&state='.$state.'#wechat_redirect';\n\t}\n\n\t/**\n\t * 通过code获取Access Token\n\t * @return array {access_token,expires_in,refresh_token,openid,scope}\n\t */\n\tpublic function getOauthAccessToken(){\n\t\t$code = isset($_GET['code'])?$_GET['code']:'';\n\t\tif (!$code) return false;\n\t\t$result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_TOKEN_URL.'appid='.$this->appid.'&secret='.$this->appsecret.'&code='.$code.'&grant_type=authorization_code');\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$this->user_token = $json['access_token'];\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 刷新access token并续期\n\t * @param string $refresh_token\n\t * @return boolean|mixed\n\t */\n\tpublic function getOauthRefreshToken($refresh_token){\n\t\t$result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_REFRESH_URL.'appid='.$this->appid.'&grant_type=refresh_token&refresh_token='.$refresh_token);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t$this->user_token = $json['access_token'];\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取授权后的用户资料\n\t * @param string $access_token\n\t * @param string $openid\n\t * @return array {openid,nickname,sex,province,city,country,headimgurl,privilege,[unionid]}\n\t * 注意：unionid字段 只有在用户将公众号绑定到微信开放平台账号后，才会出现。建议调用前用isset()检测一下\n\t */\n\tpublic function getOauthUserinfo($access_token,$openid){\n\t\t$result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_USERINFO_URL.'access_token='.$access_token.'&openid='.$openid);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 检验授权凭证是否有效\n\t * @param string $access_token\n\t * @param string $openid\n\t * @return boolean 是否有效\n\t */\n\tpublic function getOauthAuth($access_token,$openid){\n\t    $result = $this->http_get(self::API_BASE_URL_PREFIX.self::OAUTH_AUTH_URL.'access_token='.$access_token.'&openid='.$openid);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        } else\n\t          if ($json['errcode']==0) return true;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 模板消息 设置所属行业\n\t * @param int $id1  公众号模板消息所属行业编号，参看官方开发文档 行业代码\n\t * @param int $id2  同$id1。但如果只有一个行业，此参数可省略\n\t * @return boolean|array\n\t */\n\tpublic function setTMIndustry($id1,$id2=''){\n\t    if ($id1) $data['industry_id1'] = $id1;\n\t    if ($id2) $data['industry_id2'] = $id2;\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::TEMPLATE_SET_INDUSTRY_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if($result){\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 模板消息 添加消息模板\n\t * 成功返回消息模板的调用id\n\t * @param string $tpl_id 模板库中模板的编号，有“TM**”和“OPENTMTM**”等形式\n\t * @return boolean|string\n\t */\n\tpublic function addTemplateMessage($tpl_id){\n\t    $data = array ('template_id_short' =>$tpl_id);\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_URL_PREFIX.self::TEMPLATE_ADD_TPL_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if($result){\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json['template_id'];\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 发送模板消息\n\t * @param array $data 消息结构\n\t * ｛\n\t\t\t\"touser\":\"OPENID\",\n\t\t\t\"template_id\":\"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY\",\n\t\t\t\"url\":\"http://weixin.qq.com/download\",\n\t\t\t\"topcolor\":\"#FF0000\",\n\t\t\t\"data\":{\n\t\t\t\t\"参数名1\": {\n\t\t\t\t\t\"value\":\"参数\",\n\t\t\t\t\t\"color\":\"#173177\"\t //参数颜色\n\t\t\t\t\t},\n\t\t\t\t\"Date\":{\n\t\t\t\t\t\"value\":\"06月07日 19时24分\",\n\t\t\t\t\t\"color\":\"#173177\"\n\t\t\t\t\t},\n\t\t\t\t\"CardNumber\":{\n\t\t\t\t\t\"value\":\"0426\",\n\t\t\t\t\t\"color\":\"#173177\"\n\t\t\t\t\t},\n\t\t\t\t\"Type\":{\n\t\t\t\t\t\"value\":\"消费\",\n\t\t\t\t\t\"color\":\"#173177\"\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\t * @return boolean|array\n\t */\n\tpublic function sendTemplateMessage($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::TEMPLATE_SEND_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif($result){\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取多客服会话记录\n\t * @param array $data 数据结构{\"starttime\":123456789,\"endtime\":987654321,\"openid\":\"OPENID\",\"pagesize\":10,\"pageindex\":1,}\n\t * @return boolean|array\n\t */\n\tpublic function getCustomServiceMessage($data){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_URL_PREFIX.self::CUSTOM_SERVICE_GET_RECORD.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 转发多客服消息\n\t * Example: $obj->transfer_customer_service($customer_account)->reply();\n\t * @param string $customer_account 转发到指定客服帐号：test1@test\n\t */\n\tpublic function transfer_customer_service($customer_account = '')\n\t{\n\t\t$msg = array(\n\t\t\t'ToUserName' => $this->getRevFrom(),\n\t\t\t'FromUserName'=>$this->getRevTo(),\n\t\t\t'CreateTime'=>time(),\n\t\t\t'MsgType'=>'transfer_customer_service',\n\t\t);\n\t\tif ($customer_account) {\n\t\t\t$msg['TransInfo'] = array('KfAccount'=>$customer_account);\n\t\t}\n\t\t$this->Message($msg);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * 获取多客服客服基本信息\n\t *\n\t * @return boolean|array\n\t */\n\tpublic function getCustomServiceKFlist(){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_get(self::API_URL_PREFIX.self::CUSTOM_SERVICE_GET_KFLIST.'access_token='.$this->access_token);\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取多客服在线客服接待信息\n\t *\n\t * @return boolean|array {\n\t \"kf_online_list\": [\n\t {\n\t \"kf_account\": \"test1@test\",\t//客服账号@微信别名\n\t \"status\": 1,\t\t\t//客服在线状态 1：pc在线，2：手机在线,若pc和手机同时在线则为 1+2=3\n\t \"kf_id\": \"1001\",\t\t//客服工号\n\t \"auto_accept\": 0,\t\t//客服设置的最大自动接入数\n\t \"accepted_case\": 1\t\t//客服当前正在接待的会话数\n\t }\n\t ]\n\t }\n\t */\n\tpublic function getCustomServiceOnlineKFlist(){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_URL_PREFIX.self::CUSTOM_SERVICE_GET_ONLINEKFLIST.'access_token='.$this->access_token);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 创建指定多客服会话\n\t * @tutorial 当用户已被其他客服接待或指定客服不在线则会失败\n\t * @param string $openid           //用户openid\n\t * @param string $kf_account     //客服账号\n\t * @param string $text                 //附加信息，文本会展示在客服人员的多客服客户端，可为空\n\t * @return boolean | array            //成功返回json数组\n\t * {\n\t *   \"errcode\": 0,\n\t *   \"errmsg\": \"ok\",\n\t * }\n\t */\n\tpublic function createKFSession($openid,$kf_account,$text=''){\n\t    $data=array(\n\t    \t\"openid\" =>$openid,\n\t        \"kf_account\" => $kf_account\n\t    );\n\t    if ($text) $data[\"text\"] = $text;\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CUSTOM_SESSION_CREATE.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 关闭指定多客服会话\n\t * @tutorial 当用户被其他客服接待时则会失败\n\t * @param string $openid           //用户openid\n\t * @param string $kf_account     //客服账号\n\t * @param string $text                 //附加信息，文本会展示在客服人员的多客服客户端，可为空\n\t * @return boolean | array            //成功返回json数组\n\t * {\n\t *   \"errcode\": 0,\n\t *   \"errmsg\": \"ok\",\n\t * }\n\t */\n\tpublic function closeKFSession($openid,$kf_account,$text=''){\n\t    $data=array(\n\t    \t\"openid\" =>$openid,\n\t        \"kf_account\" => $kf_account\n\t    );\n\t    if ($text) $data[\"text\"] = $text;\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CUSTOM_SESSION_CLOSE .'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取用户会话状态\n\t * @param string $openid           //用户openid\n\t * @return boolean | array            //成功返回json数组\n\t * {\n\t *     \"errcode\" : 0,\n\t *     \"errmsg\" : \"ok\",\n\t *     \"kf_account\" : \"test1@test\",    //正在接待的客服\n\t *     \"createtime\": 123456789,        //会话接入时间\n\t *  }\n\t */\n\tpublic function getKFSession($openid){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_BASE_URL_PREFIX.self::CUSTOM_SESSION_GET .'access_token='.$this->access_token.'&openid='.$openid);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取指定客服的会话列表\n\t * @param string $openid           //用户openid\n\t * @return boolean | array            //成功返回json数组\n\t *  array(\n\t *     'sessionlist' => array (\n\t *         array (\n\t *             'openid'=>'OPENID',             //客户 openid\n\t *             'createtime'=>123456789,  //会话创建时间，UNIX 时间戳\n\t *         ),\n\t *         array (\n\t *             'openid'=>'OPENID',             //客户 openid\n\t *             'createtime'=>123456789,  //会话创建时间，UNIX 时间戳\n\t *         ),\n\t *     )\n\t *  )\n\t */\n\tpublic function getKFSessionlist($kf_account){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_BASE_URL_PREFIX.self::CUSTOM_SESSION_GET_LIST .'access_token='.$this->access_token.'&kf_account='.$kf_account);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 获取未接入会话列表\n\t * @param string $openid           //用户openid\n\t * @return boolean | array            //成功返回json数组\n\t *  array (\n\t *     'count' => 150 ,                            //未接入会话数量\n\t *     'waitcaselist' => array (\n\t *         array (\n\t *             'openid'=>'OPENID',             //客户 openid\n\t *             'kf_account ' =>'',                   //指定接待的客服，为空则未指定\n\t *             'createtime'=>123456789,  //会话创建时间，UNIX 时间戳\n\t *         ),\n\t *         array (\n\t *             'openid'=>'OPENID',             //客户 openid\n\t *             'kf_account ' =>'',                   //指定接待的客服，为空则未指定\n\t *             'createtime'=>123456789,  //会话创建时间，UNIX 时间戳\n\t *         )\n\t *     )\n\t *  )\n\t */\n\tpublic function getKFSessionWait(){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_BASE_URL_PREFIX.self::CUSTOM_SESSION_GET_WAIT .'access_token='.$this->access_token);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 添加客服账号\n\t *\n\t * @param string $account      //完整客服账号，格式为：账号前缀@公众号微信号，账号前缀最多10个字符，必须是英文或者数字字符\n\t * @param string $nickname     //客服昵称，最长6个汉字或12个英文字符\n\t * @param string $password     //客服账号明文登录密码，会自动加密\n\t * @return boolean|array\n\t * 成功返回结果\n\t * {\n\t *   \"errcode\": 0,\n\t *   \"errmsg\": \"ok\",\n\t * }\n\t */\n\tpublic function addKFAccount($account,$nickname,$password){\n\t    $data=array(\n\t    \t\"kf_account\" =>$account,\n\t        \"nickname\" => $nickname,\n\t        \"password\" => md5($password)\n\t    );\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\t$result = $this->http_post(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_ADD_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (!$json || !empty($json['errcode'])) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 修改客服账号信息\n\t *\n\t * @param string $account      //完整客服账号，格式为：账号前缀@公众号微信号，账号前缀最多10个字符，必须是英文或者数字字符\n\t * @param string $nickname     //客服昵称，最长6个汉字或12个英文字符\n\t * @param string $password     //客服账号明文登录密码，会自动加密\n\t * @return boolean|array\n\t * 成功返回结果\n\t * {\n\t *   \"errcode\": 0,\n\t *   \"errmsg\": \"ok\",\n\t * }\n\t */\n\tpublic function updateKFAccount($account,$nickname,$password){\n\t    $data=array(\n\t            \"kf_account\" =>$account,\n\t            \"nickname\" => $nickname,\n\t            \"password\" => md5($password)\n\t    );\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 删除客服账号\n\t *\n\t * @param string $account      //完整客服账号，格式为：账号前缀@公众号微信号，账号前缀最多10个字符，必须是英文或者数字字符\n\t * @return boolean|array\n\t * 成功返回结果\n\t * {\n\t *   \"errcode\": 0,\n\t *   \"errmsg\": \"ok\",\n\t * }\n\t */\n\tpublic function deleteKFAccount($account){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_get(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_DEL_URL.'access_token='.$this->access_token.'&kf_account='.$account);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 上传客服头像\n\t *\n\t * @param string $account //完整客服账号，格式为：账号前缀@公众号微信号，账号前缀最多10个字符，必须是英文或者数字字符\n\t * @param string $imgfile //头像文件完整路径,如：'D:\\user.jpg'。头像文件必须JPG格式，像素建议640*640\n\t * @return boolean|array\n\t * 成功返回结果\n\t * {\n\t *   \"errcode\": 0,\n\t *   \"errmsg\": \"ok\",\n\t * }\n\t */\n\tpublic function setKFHeadImg($account,$imgfile){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $result = $this->http_post(self::API_BASE_URL_PREFIX.self::CS_KF_ACCOUNT_UPLOAD_HEADIMG_URL.'access_token='.$this->access_token.'&kf_account='.$account,array('media'=>'@'.$imgfile),true);\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n\t/**\n\t * 语义理解接口\n\t * @param String $uid      用户唯一id（非开发者id），用户区分公众号下的不同用户（建议填入用户openid）\n\t * @param String $query    输入文本串\n\t * @param String $category 需要使用的服务类型，多个用“，”隔开，不能为空\n\t * @param Float $latitude  纬度坐标，与经度同时传入；与城市二选一传入\n\t * @param Float $longitude 经度坐标，与纬度同时传入；与城市二选一传入\n\t * @param String $city     城市名称，与经纬度二选一传入\n\t * @param String $region   区域名称，在城市存在的情况下可省略；与经纬度二选一传入\n\t * @return boolean|array\n\t */\n\tpublic function querySemantic($uid,$query,$category,$latitude=0,$longitude=0,$city=\"\",$region=\"\"){\n\t    if (!$this->access_token && !$this->checkAuth()) return false;\n\t    $data=array(\n\t            'query' => $query,\n\t            'category' => $category,\n\t            'appid' => $this->appid,\n\t            'uid' => ''\n\t    );\n\t    //地理坐标或城市名称二选一\n\t    if ($latitude) {\n\t        $data['latitude'] = $latitude;\n\t        $data['longitude'] = $longitude;\n\t    } elseif ($city) {\n\t        $data['city'] = $city;\n\t    } elseif ($region) {\n\t        $data['region'] = $region;\n\t    }\n\t    $result = $this->http_post(self::API_BASE_URL_PREFIX.self::SEMANTIC_API_URL.'access_token='.$this->access_token,self::json_encode($data));\n\t    if ($result)\n\t    {\n\t        $json = json_decode($result,true);\n\t        if (!$json || !empty($json['errcode'])) {\n\t            $this->errCode = $json['errcode'];\n\t            $this->errMsg = $json['errmsg'];\n\t            return false;\n\t        }\n\t        return $json;\n\t    }\n\t    return false;\n\t}\n\n    /**\n     * 创建卡券\n     * @param Array $data      卡券数据\n     * @return array|boolean 返回数组中card_id为卡券ID\n     */\n    public function createCard($data) {\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CREATE . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 更改卡券信息\n     * 调用该接口更新信息后会重新送审，卡券状态变更为待审核。已被用户领取的卡券会实时更新票面信息。\n     * @param string $data\n     * @return boolean\n     */\n    public function updateCard($data) {\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 删除卡券\n     * 允许商户删除任意一类卡券。删除卡券后，该卡券对应已生成的领取用二维码、添加到卡包 JS API 均会失效。\n     * 注意：删除卡券不能删除已被用户领取，保存在微信客户端中的卡券，已领取的卡券依旧有效。\n     * @param string $card_id 卡券ID\n     * @return boolean\n     */\n    public function delCard($card_id) {\n        $data = array(\n            'card_id' => $card_id,\n        );\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_DELETE . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 查询卡券详情\n     * @param string $card_id\n     * @return boolean|array    返回数组信息比较复杂，请参看卡券接口文档\n     */\n    public function getCardInfo($card_id) {\n        $data = array(\n            'card_id' => $card_id,\n        );\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_GET . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 获取用户已领取卡券接口\n     * @param string $openid\n     * @param string $card_id\n     * @return boolean|array    返回数组信息比较复杂，请参看卡券接口文档\n     * 成功返回结果\n     *  {\n     * \"errcode\":0,\n     * \"errmsg\":\"ok\",\n     * \"card_list\": [\n     * {\"code\": \"xxx1434079154\", \"card_id\": \"xxxxxxxxxx\"},\n     * {\"code\": \"xxx1434079155\", \"card_id\": \"xxxxxxxxxx\"}\n     * ]\n     * }\n     */\n    public function getUserCardList($openid,$card_id) {\n        $data = array(\n            'openid' => $openid,\n            'card_id' => $card_id\n        );\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_USER_GETCARDLIST . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 获取颜色列表\n\t * 获得卡券的最新颜色列表，用于创建卡券\n\t * @return boolean|array   返回数组请参看 微信卡券接口文档 的json格式\n     */\n    public function getCardColors() {\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_get(self::API_BASE_URL_PREFIX . self::CARD_GETCOLORS . 'access_token=' . $this->access_token);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 拉取门店列表\n\t * 获取在公众平台上申请创建的门店列表\n\t * @param int $offset  开始拉取的偏移，默认为0从头开始\n\t * @param int $count   拉取的数量，默认为0拉取全部\n\t * @return boolean|array   返回数组请参看 微信卡券接口文档 的json格式\n     */\n    public function getCardLocations($offset=0,$count=0) {\n\t    $data=array(\n\t    \t'offset'=>$offset,\n\t        'count'=>$count\n\t    );\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_LOCATION_BATCHGET . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 批量导入门店信息\n\t * @tutorial 返回插入的门店id列表，以逗号分隔。如果有插入失败的，则为-1，请自行核查是哪个插入失败\n\t * @param array $data    数组形式的json数据，由于内容较多，具体内容格式请查看 微信卡券接口文档\n\t * @return boolean|string 成功返回插入的门店id列表\n     */\n    public function addCardLocations($data) {\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_LOCATION_BATCHADD . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 生成卡券二维码\n\t * 成功则直接返回ticket值，可以用 getQRUrl($ticket) 换取二维码url\n\t *\n\t * @param string $cardid 卡券ID 必须\n\t * @param string $code 指定卡券 code 码，只能被领一次。use_custom_code 字段为 true 的卡券必须填写，非自定义 code 不必填写。\n\t * @param string $openid 指定领取者的 openid，只有该用户能领取。bind_openid 字段为 true 的卡券必须填写，非自定义 openid 不必填写。\n\t * @param int $expire_seconds 指定二维码的有效时间，范围是 60 ~ 1800 秒。不填默认为永久有效。\n\t * @param boolean $is_unique_code 指定下发二维码，生成的二维码随机分配一个 code，领取后不可再次扫描。填写 true 或 false。默认 false。\n\t * @param string $balance 红包余额，以分为单位。红包类型必填（LUCKY_MONEY），其他卡券类型不填。\n     * @return boolean|string\n     */\n    public function createCardQrcode($card_id,$code='',$openid='',$expire_seconds=0,$is_unique_code=false,$balance='') {\n        $card = array(\n            'card_id' => $card_id\n        );\n        $data = array(\n            'action_name' => \"QR_CARD\"\n        );\n        if ($code)\n            $card['code'] = $code;\n        if ($openid)\n            $card['openid'] = $openid;\n        if ($is_unique_code)\n            $card['is_unique_code'] = $is_unique_code;\n        if ($balance)\n            $card['balance'] = $balance;\n        if ($expire_seconds)\n            $data['expire_seconds'] = $expire_seconds;\n        $data['action_info'] = array('card' => $card);\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_QRCODE_CREATE . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 消耗 code\n     * 自定义 code（use_custom_code 为 true）的优惠券，在 code 被核销时，必须调用此接口。\n     *\n     * @param string $code 要消耗的序列号\n     * @param string $card_id 要消耗序列号所述的 card_id，创建卡券时use_custom_code 填写 true 时必填。\n     * @return boolean|array\n     * {\n     *  \"errcode\":0,\n     *  \"errmsg\":\"ok\",\n     *  \"card\":{\"card_id\":\"pFS7Fjg8kV1IdDz01r4SQwMkuCKc\"},\n     *  \"openid\":\"oFS7Fjl0WsZ9AMZqrI80nbIq8xrA\"\n     * }\n     */\n    public function consumeCardCode($code,$card_id='') {\n        $data = array('code' => $code);\n        if ($card_id)\n            $data['card_id'] = $card_id;\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_CONSUME . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * code 解码\n     * @param string $encrypt_code 通过 choose_card_info 获取的加密字符串\n     * @return boolean|array\n     * {\n     *  \"errcode\":0,\n     *  \"errmsg\":\"ok\",\n     *  \"code\":\"751234212312\"\n     *  }\n     */\n    public function decryptCardCode($encrypt_code) {\n        $data = array(\n            'encrypt_code' => $encrypt_code,\n        );\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_DECRYPT . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 查询 code 的有效性（非自定义 code）\n     * @param string $code\n     * @return boolean|array\n     * {\n     *  \"errcode\":0,\n     *  \"errmsg\":\"ok\",\n     *  \"openid\":\"oFS7Fjl0WsZ9AMZqrI80nbIq8xrA\",    //用户 openid\n     *  \"card\":{\n     *      \"card_id\":\"pFS7Fjg8kV1IdDz01r4SQwMkuCKc\",\n     *      \"begin_time\": 1404205036,               //起始使用时间\n     *      \"end_time\": 1404205036,                 //结束时间\n     *  }\n     * }\n     */\n    public function checkCardCode($code) {\n        $data = array(\n            'code' => $code,\n        );\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_GET . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 批量查询卡列表\n\t * @param $offset  开始拉取的偏移，默认为0从头开始\n\t * @param $count   需要查询的卡片的数量（数量最大50,默认50）\n     * @return boolean|array\n     * {\n     *  \"errcode\":0,\n     *  \"errmsg\":\"ok\",\n     *  \"card_id_list\":[\"ph_gmt7cUVrlRk8swPwx7aDyF-pg\"],    //卡 id 列表\n     *  \"total_num\":1                                       //该商户名下 card_id 总数\n     * }\n     */\n    public function getCardIdList($offset=0,$count=50) {\n        if ($count>50)\n            $count = 50;\n        $data = array(\n            'offset' => $offset,\n            'count'  => $count,\n        );\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_BATCHGET . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 更改 code\n     * 为确保转赠后的安全性，微信允许自定义code的商户对已下发的code进行更改。\n     * 注：为避免用户疑惑，建议仅在发生转赠行为后（发生转赠后，微信会通过事件推送的方式告知商户被转赠的卡券code）对用户的code进行更改。\n     * @param string $code      卡券的 code 编码\n     * @param string $card_id   卡券 ID\n     * @param string $new_code  新的卡券 code 编码\n     * @return boolean\n     */\n    public function updateCardCode($code,$card_id,$new_code) {\n        $data = array(\n            'code' => $code,\n            'card_id' => $card_id,\n            'new_code' => $new_code,\n        );\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 设置卡券失效\n     * 设置卡券失效的操作不可逆\n     * @param string $code 需要设置为失效的 code\n     * @param string $card_id 自定义 code 的卡券必填。非自定义 code 的卡券不填。\n     * @return boolean\n     */\n    public function unavailableCardCode($code,$card_id='') {\n        $data = array(\n            'code' => $code,\n        );\n        if ($card_id)\n            $data['card_id'] = $card_id;\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_CODE_UNAVAILABLE . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 库存修改\n     * @param string $data\n     * @return boolean\n     */\n    public function modifyCardStock($data) {\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MODIFY_STOCK . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 更新门票\n     * @param string $data\n     * @return boolean\n     */\n    public function updateMeetingCard($data) {\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MEETINGCARD_UPDATEUSER . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 激活/绑定会员卡\n     * @param string $data 具体结构请参看卡券开发文档(6.1.1 激活/绑定会员卡)章节\n     * @return boolean\n     */\n    public function activateMemberCard($data) {\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MEMBERCARD_ACTIVATE . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 会员卡交易\n     * 会员卡交易后每次积分及余额变更需通过接口通知微信，便于后续消息通知及其他扩展功能。\n     * @param string $data 具体结构请参看卡券开发文档(6.1.2 会员卡交易)章节\n     * @return boolean|array\n     */\n    public function updateMemberCard($data) {\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_MEMBERCARD_UPDATEUSER . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 更新红包金额\n     * @param string $code      红包的序列号\n     * @param $balance          红包余额\n     * @param string $card_id   自定义 code 的卡券必填。非自定义 code 可不填。\n     * @return boolean|array\n     */\n    public function updateLuckyMoney($code,$balance,$card_id='') {\n        $data = array(\n                'code' => $code,\n                'balance' => $balance\n        );\n        if ($card_id)\n            $data['card_id'] = $card_id;\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_LUCKYMONEY_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 设置卡券测试白名单\n     * @param string $openid    测试的 openid 列表\n     * @param string $user      测试的微信号列表\n     * @return boolean\n     */\n    public function setCardTestWhiteList($openid=array(),$user=array()) {\n        $data = array();\n        if (count($openid) > 0)\n            $data['openid'] = $openid;\n        if (count($user) > 0)\n            $data['username'] = $user;\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_TESTWHILELIST_SET . 'access_token=' . $this->access_token, self::json_encode($data));\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 申请设备ID\n     * [applyShakeAroundDevice 申请配置设备所需的UUID、Major、Minor。\n     * 若激活率小于50%，不能新增设备。单次新增设备超过500 个，需走人工审核流程。\n     * 审核通过后，可用迒回的批次ID 用“查询设备列表”接口拉取本次申请的设备ID]\n     * @param array $data\n     * array(\n     *      \"quantity\" => 3,         //申请的设备ID 的数量，单次新增设备超过500 个,需走人工审核流程(必填)\n     *      \"apply_reason\" => \"测试\",//申请理由(必填)\n     *      \"comment\" => \"测试专用\", //备注(非必填)\n     *      \"poi_id\" => 1234         //设备关联的门店ID(非必填)\n     * )\n     * @return boolean|mixed\n     * {\n        \"data\": {\n            \"apply_id\": 123,\n            \"device_identifiers\":[\n            {\n            \"device_id\":10100,\n            \"uuid\":\"FDA50693-A4E2-4FB1-AFCF-C6EB07647825\",\n            \"major\":10001,\n            \"minor\":10002\n            }\n            ]\n        },\n        \"errcode\": 0,\n        \"errmsg\": \"success.\"\n        }\n\n        apply_id:申请的批次ID，可用在“查询设备列表”接口按批次查询本次申请成功的设备ID\n        device_identifiers:指定的设备ID 列表\n        device_id:设备编号\n        uuid、major、minor\n        audit_status:审核状态。0：审核未通过、1：审核中、2：审核已通过；审核会在三个工作日内完成\n        audit_comment:审核备注，包括审核不通过的原因\n     * @access public\n     * @author polo<gao.bo168@gmail.com>\n     * @version 2015-3-25 下午1:24:06\n     * @copyright Show More\n     */\n    public function applyShakeAroundDevice($data){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_DEVICE_APPLYID . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 编辑设备信息\n     * [updateShakeAroundDevice 编辑设备的备注信息。可用设备ID或完整的UUID、Major、Minor指定设备，二者选其一。]\n     * @param array $data\n     * array(\n     *      \"device_identifier\" => array(\n     *          \t\t\"device_id\" => 10011,   //当提供了device_id则不需要使用uuid、major、minor，反之亦然\n     *          \t\t\"uuid\" => \"FDA50693-A4E2-4FB1-AFCF-C6EB07647825\",\n     *          \t\t\"major\" => 1002,\n     *          \t\t\"minor\" => 1223\n     *      ),\n     *      \"comment\" => \"测试专用\", //备注(非必填)\n     * )\n     * {\n        \"data\": {\n        },\n        \"errcode\": 0,\n        \"errmsg\": \"success.\"\n       }\n     * @return boolean\n     * @author binsee<binsee@163.com>\n     * @version 2015-4-20 23:45:00\n     */\n    public function updateShakeAroundDevice($data){\n    \tif (!$this->access_token && !$this->checkAuth()) return false;\n    \t$result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_DEVICE_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data));\n    \t$this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 查询设备列表\n     * [searchShakeAroundDevice 查询已有的设备ID、UUID、Major、Minor、激活状态、备注信息、关联门店、关联页面等信息。\n     * 可指定设备ID 或完整的UUID、Major、Minor 查询，也可批量拉取设备信息列表。]\n     * @param array $data\n     * $data 三种格式:\n     * ①查询指定设备时：$data = array(\n     *                              \"device_identifiers\" => array(\n     *                                                          array(\n     *                                                              \"device_id\" => 10100,\n     *                                                              \"uuid\" => \"FDA50693-A4E2-4FB1-AFCF-C6EB07647825\",\n     *                                                              \"major\" => 10001,\n     *                                                              \"minor\" => 10002\n     *                                                          )\n     *                                                      )\n     *                              );\n     * device_identifiers:指定的设备\n     * device_id:设备编号，若填了UUID、major、minor，则可不填设备编号，若二者都填，则以设备编号为优先\n     * uuid、major、minor:三个信息需填写完整，若填了设备编号，则可不填此信息\n     * +-------------------------------------------------------------------------------------------------------------\n     * ②需要分页查询或者指定范围内的设备时: $data = array(\n     *                                                  \"begin\" => 0,\n     *                                                  \"count\" => 3\n     *                                               );\n     * begin:设备列表的起始索引值\n     * count:待查询的设备个数\n     * +-------------------------------------------------------------------------------------------------------------\n     * ③当需要根据批次ID 查询时: $data = array(\n     *                                      \"apply_id\" => 1231,\n     *                                      \"begin\" => 0,\n     *                                      \"count\" => 3\n     *                                    );\n     * apply_id:批次ID\n     * +-------------------------------------------------------------------------------------------------------------\n     * @return boolean|mixed\n     *正确迒回JSON 数据示例：\n     *字段说明\n        {\n            \"data\": {\n                \"devices\": [          //指定的设备信息列表\n                    {\n                        \"comment\": \"\", //设备的备注信息\n                        \"device_id\": 10097, //设备编号\n                        \"major\": 10001,\n                        \"minor\": 12102,\n                        \"page_ids\": \"15369\", //与此设备关联的页面ID 列表，用逗号隔开\n                        \"status\": 1, //激活状态，0：未激活，1：已激活（但不活跃），2：活跃\n                        \"poi_id\": 0, //门店ID\n                        \"uuid\": \"FDA50693-A4E2-4FB1-AFCF-C6EB07647825\"\n                    },\n                    {\n                        \"comment\": \"\", //设备的备注信息\n                        \"device_id\": 10098, //设备编号\n                        \"major\": 10001,\n                        \"minor\": 12103,\n                        \"page_ids\": \"15368\", //与此设备关联的页面ID 列表，用逗号隔开\n                        \"status\": 1, //激活状态，0：未激活，1：已激活（但不活跃），2：活跃\n                        \"poi_id\": 0, //门店ID\n                        \"uuid\": \"FDA50693-A4E2-4FB1-AFCF-C6EB07647825\"\n                    }\n                ],\n                \"total_count\": 151 //商户名下的设备总量\n            },\n            \"errcode\": 0,\n            \"errmsg\": \"success.\"\n        }\n     * @access public\n     * @author polo<gao.bo168@gmail.com>\n     * @version 2015-3-25 下午1:45:42\n     * @copyright Show More\n     */\n    public function searchShakeAroundDevice($data){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_DEVICE_SEARCH . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * [bindLocationShakeAroundDevice 配置设备与门店的关联关系]\n     * @param string $device_id 设备编号，若填了UUID、major、minor，则可不填设备编号，若二者都填，则以设备编号为优先\n     * @param int $poi_id 待关联的门店ID\n     * @param string $uuid UUID、major、minor，三个信息需填写完整，若填了设备编号，则可不填此信息\n     * @param int $major\n     * @param int $minor\n     * @return boolean|mixed\n     * 正确返回JSON 数据示例:\n     * {\n        \"data\": {\n        },\n        \"errcode\": 0,\n        \"errmsg\": \"success.\"\n       }\n     * @access public\n     * @author polo<gao.bo168@gmail.com>\n     * @version 2015-4-21 00:14:00\n     * @copyright Show More\n     */\n    public function bindLocationShakeAroundDevice($device_id,$poi_id,$uuid='',$major=0,$minor=0){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        if(!$device_id){\n            if(!$uuid || !$major || !$minor){\n                return false;\n            }\n            $device_identifier = array(\n                'uuid' => $uuid,\n                'major' => $major,\n                'minor' => $minor\n            );\n        }else{\n            $device_identifier = array(\n                'device_id' => $device_id\n            );\n        }\n        $data = array(\n            'device_identifier' => $device_identifier,\n            'poi_id' => $poi_id\n        );\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_DEVICE_BINDLOCATION . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json; //这个可以更改为返回true\n        }\n        return false;\n    }\n\n    /**\n     * [bindPageShakeAroundDevice 配置设备与页面的关联关系。\n     * 支持建立或解除关联关系，也支持新增页面或覆盖页面等操作。\n     * 配置完成后，在此设备的信号范围内，即可摇出关联的页面信息。\n     * 若设备配置多个页面，则随机出现页面信息]\n     * @param string $device_id 设备编号，若填了UUID、major、minor，则可不填设备编号，若二者都填，则以设备编号为优先\n     * @param array $page_ids 待关联的页面列表\n     * @param number $bind 关联操作标志位， 0 为解除关联关系，1 为建立关联关系\n     * @param number $append 新增操作标志位， 0 为覆盖，1 为新增\n     * @param string $uuid UUID、major、minor，三个信息需填写完整，若填了设备编号，则可不填此信息\n     * @param int $major\n     * @param int $minor\n     * @return boolean|mixed\n     * 正确返回JSON 数据示例:\n     * {\n        \"data\": {\n        },\n        \"errcode\": 0,\n        \"errmsg\": \"success.\"\n       }\n     * @access public\n     * @author polo<gao.bo168@gmail.com>\n     * @version 2015-4-21 00:31:00\n     * @copyright Show More\n     */\n    public function bindPageShakeAroundDevice($device_id,$page_ids=array(),$bind=1,$append=1,$uuid='',$major=0,$minor=0){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        if(!$device_id){\n            if(!$uuid || !$major || !$minor){\n                return false;\n            }\n            $device_identifier = array(\n                'uuid' => $uuid,\n                'major' => $major,\n                'minor' => $minor\n            );\n        }else{\n            $device_identifier = array(\n                'device_id' => $device_id\n            );\n        }\n        $data = array(\n            'device_identifier' => $device_identifier,\n            'page_ids' => $page_ids,\n            'bind' => $bind,\n            'append' => $append\n        );\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_DEVICE_BINDPAGE . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * 上传在摇一摇页面展示的图片素材\n     * 注意：数组的键值任意，但文件名前必须加@，使用单引号以避免本地路径斜杠被转义\n     * @param array $data {\"media\":'@Path\\filename.jpg'} 格式限定为：jpg,jpeg,png,gif，图片大小建议120px*120 px，限制不超过200 px *200 px，图片需为正方形。\n     * @return boolean|array\n     * {\n        \"data\": {\n            \"pic_url\":\"http://shp.qpic.cn/wechat_shakearound_pic/0/1428377032e9dd2797018cad79186e03e8c5aec8dc/120\"\n        },\n            \"errcode\": 0,\n            \"errmsg\": \"success.\"\n        }\n       }\n     * @author binsee<binsee@163.com>\n     * @version 2015-4-21 00:51:00\n     */\n    public function uploadShakeAroundMedia($data){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $result = $this->http_post(self::API_BASE_URL_PREFIX.self::SHAKEAROUND_MATERIAL_ADD.'access_token='.$this->access_token,$data,true);\n        if ($result)\n        {\n            $json = json_decode($result,true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * [addShakeAroundPage 增加摇一摇出来的页面信息，包括在摇一摇页面出现的主标题、副标题、图片和点击进去的超链接。]\n     * @param string $title 在摇一摇页面展示的主标题，不超过6 个字\n     * @param string $description 在摇一摇页面展示的副标题，不超过7 个字\n     * @param sting $icon_url 在摇一摇页面展示的图片， 格式限定为：jpg,jpeg,png,gif; 建议120*120 ， 限制不超过200*200\n     * @param string $page_url 跳转链接\n     * @param string $comment 页面的备注信息，不超过15 个字,可不填\n     * @return boolean|mixed\n     * 正确返回JSON 数据示例:\n     * {\n        \"data\": {\n            \"page_id\": 28840 //新增页面的页面id\n        }\n        \"errcode\": 0,\n        \"errmsg\": \"success.\"\n       }\n     * @access public\n     * @author polo<gao.bo168@gmail.com>\n     * @version 2015-3-25 下午2:57:09\n     * @copyright Show More\n     */\n    public function addShakeAroundPage($title,$description,$icon_url,$page_url,$comment=''){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $data = array(\n            \"title\" => $title,\n            \"description\" => $description,\n            \"icon_url\" => $icon_url,\n            \"page_url\" => $page_url,\n            \"comment\" => $comment\n        );\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_PAGE_ADD . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * [updateShakeAroundPage 编辑摇一摇出来的页面信息，包括在摇一摇页面出现的主标题、副标题、图片和点击进去的超链接。]\n     * @param int $page_id\n     * @param string $title 在摇一摇页面展示的主标题，不超过6 个字\n     * @param string $description 在摇一摇页面展示的副标题，不超过7 个字\n     * @param sting $icon_url 在摇一摇页面展示的图片， 格式限定为：jpg,jpeg,png,gif; 建议120*120 ， 限制不超过200*200\n     * @param string $page_url 跳转链接\n     * @param string $comment 页面的备注信息，不超过15 个字,可不填\n     * @return boolean|mixed\n     * 正确返回JSON 数据示例:\n     * {\n        \"data\": {\n            \"page_id\": 28840 //编辑页面的页面ID\n        }\n        \"errcode\": 0,\n        \"errmsg\": \"success.\"\n       }\n     * @access public\n     * @author polo<gao.bo168@gmail.com>\n     * @version 2015-3-25 下午3:02:51\n     * @copyright Show More\n     */\n    public function updateShakeAroundPage($page_id,$title,$description,$icon_url,$page_url,$comment=''){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $data = array(\n            \"page_id\" => $page_id,\n            \"title\" => $title,\n            \"description\" => $description,\n            \"icon_url\" => $icon_url,\n            \"page_url\" => $page_url,\n            \"comment\" => $comment\n        );\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_PAGE_UPDATE . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * [searchShakeAroundPage 查询已有的页面，包括在摇一摇页面出现的主标题、副标题、图片和点击进去的超链接。\n     * 提供两种查询方式，①可指定页面ID 查询，②也可批量拉取页面列表。]\n     * @param array $page_ids\n     * @param int $begin\n     * @param int $count\n     * ①需要查询指定页面时:\n     * {\n        \"page_ids\":[12345, 23456, 34567]\n       }\n     * +-------------------------------------------------------------------------------------------------------------\n     * ②需要分页查询或者指定范围内的页面时:\n     * {\n        \"begin\": 0,\n        \"count\": 3\n       }\n     * +-------------------------------------------------------------------------------------------------------------\n     * @return boolean|mixed\n     * 正确返回JSON 数据示例:\n        {\n            \"data\": {\n                \"pages\": [\n                    {\n                        \"comment\": \"just for test\",\n                        \"description\": \"test\",\n                        \"icon_url\": \"https://www.baidu.com/img/bd_logo1.png\",\n                        \"page_id\": 28840,\n                        \"page_url\": \"http://xw.qq.com/testapi1\",\n                        \"title\": \"测试1\"\n                    },\n                    {\n                        \"comment\": \"just for test\",\n                        \"description\": \"test\",\n                        \"icon_url\": \"https://www.baidu.com/img/bd_logo1.png\",\n                        \"page_id\": 28842,\n                        \"page_url\": \"http://xw.qq.com/testapi2\",\n                        \"title\": \"测试2\"\n                    }\n                    ],\n                \"total_count\": 2\n            },\n            \"errcode\": 0,\n            \"errmsg\": \"success.\"\n        }\n     *字段说明:\n     *total_count 商户名下的页面总数\n     *page_id 摇周边页面唯一ID\n     *title 在摇一摇页面展示的主标题\n     *description 在摇一摇页面展示的副标题\n     *icon_url 在摇一摇页面展示的图片\n     *page_url 跳转链接\n     *comment 页面的备注信息\n     * @access public\n     * @author polo<gao.bo168@gmail.com>\n     * @version 2015-3-25 下午3:12:17\n     * @copyright Show More\n     */\n    public function searchShakeAroundPage($page_ids=array(),$begin=0,$count=1){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        if(!empty($page_ids)){\n            $data = array(\n                'page_ids' => $page_ids\n            );\n        }else{\n            $data = array(\n                'begin' => $begin,\n                'count' => $count\n            );\n        }\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_PAGE_SEARCH . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * [deleteShakeAroundPage 删除已有的页面，包括在摇一摇页面出现的主标题、副标题、图片和点击进去的超链接。\n     * 只有页面与设备没有关联关系时，才可被删除。]\n     * @param array $page_ids\n     * {\n        \"page_ids\":[12345,23456,34567]\n       }\n     * @return boolean|mixed\n     * 正确返回JSON 数据示例:\n     * {\n        \"data\": {\n        },\n        \"errcode\": 0,\n        \"errmsg\": \"success.\"\n       }\n     * @access public\n     * @author polo<gao.bo168@gmail.com>\n     * @version 2015-3-25 下午3:23:00\n     * @copyright Show More\n     */\n    public function deleteShakeAroundPage($page_ids=array()){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $data = array(\n            'page_ids' => $page_ids\n        );\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_PAGE_DELETE . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * [getShakeInfoShakeAroundUser 获取设备信息，包括UUID、major、minor，以及距离、openID 等信息。]\n     * @param string $ticket 摇周边业务的ticket，可在摇到的URL 中得到，ticket生效时间为30 分钟\n     * @return boolean|mixed\n     * 正确返回JSON 数据示例:\n     * {\n        \"data\": {\n            \"page_id \": 14211,\n            \"beacon_info\": {\n                \"distance\": 55.00620700469034,\n                \"major\": 10001,\n                \"minor\": 19007,\n                \"uuid\": \"FDA50693-A4E2-4FB1-AFCF-C6EB07647825\"\n            },\n            \"openid\": \"oVDmXjp7y8aG2AlBuRpMZTb1-cmA\"\n        },\n        \"errcode\": 0,\n        \"errmsg\": \"success.\"\n       }\n     * 字段说明:\n     * beacon_info 设备信息，包括UUID、major、minor，以及距离\n     * UUID、major、minor UUID、major、minor\n     * distance Beacon 信号与手机的距离\n     * page_id 摇周边页面唯一ID\n     * openid 商户AppID 下用户的唯一标识\n     * poi_id 门店ID，有的话则返回，没有的话不会在JSON 格式内\n     * @access public\n     * @author polo<gao.bo168@gmail.com>\n     * @version 2015-3-25 下午3:28:20\n     * @copyright Show More\n     */\n    public function getShakeInfoShakeAroundUser($ticket){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $data = array('ticket' => $ticket);\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_USER_GETSHAKEINFO . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n    /**\n     * [deviceShakeAroundStatistics 以设备为维度的数据统计接口。\n     * 查询单个设备进行摇周边操作的人数、次数，点击摇周边消息的人数、次数；查询的最长时间跨度为30天。]\n     * @param int $device_id 设备编号，若填了UUID、major、minor，即可不填设备编号，二者选其一\n     * @param int $begin_date 起始日期时间戳，最长时间跨度为30 天\n     * @param int $end_date 结束日期时间戳，最长时间跨度为30 天\n     * @param string $uuid UUID、major、minor，三个信息需填写完成，若填了设备编辑，即可不填此信息，二者选其一\n     * @param int $major\n     * @param int $minor\n     * @return boolean|mixed\n     * 正确返回JSON 数据示例:\n     * {\n        \"data\": [\n            {\n                \"click_pv\": 0,\n                \"click_uv\": 0,\n                \"ftime\": 1425052800,\n                \"shake_pv\": 0,\n                \"shake_uv\": 0\n            },\n            {\n                \"click_pv\": 0,\n                \"click_uv\": 0,\n                \"ftime\": 1425139200,\n                \"shake_pv\": 0,\n                \"shake_uv\": 0\n            }\n        ],\n        \"errcode\": 0,\n        \"errmsg\": \"success.\"\n       }\n     * 字段说明:\n     * ftime 当天0 点对应的时间戳\n     * click_pv 点击摇周边消息的次数\n     * click_uv 点击摇周边消息的人数\n     * shake_pv 摇周边的次数\n     * shake_uv 摇周边的人数\n     * @access public\n     * @author polo<gao.bo168@gmail.com>\n     * @version 2015-4-21 00:39:00\n     * @copyright Show More\n     */\n    public function deviceShakeAroundStatistics($device_id,$begin_date,$end_date,$uuid='',$major=0,$minor=0){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        if(!$device_id){\n            if(!$uuid || !$major || !$minor){\n                return false;\n            }\n            $device_identifier = array(\n                'uuid' => $uuid,\n                'major' => $major,\n                'minor' => $minor\n            );\n        }else{\n            $device_identifier = array(\n                'device_id' => $device_id\n            );\n        }\n        $data = array(\n            'device_identifier' => $device_identifier,\n            'begin_date' => $begin_date,\n            'end_date' => $end_date\n        );\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_STATISTICS_DEVICE . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n\n    /**\n     * [pageShakeAroundStatistics 以页面为维度的数据统计接口。\n     * 查询单个页面通过摇周边摇出来的人数、次数，点击摇周边页面的人数、次数；查询的最长时间跨度为30天。]\n     * @param int $page_id 指定页面的ID\n     * @param int $begin_date 起始日期时间戳，最长时间跨度为30 天\n     * @param int $end_date 结束日期时间戳，最长时间跨度为30 天\n     * @return boolean|mixed\n     * 正确返回JSON 数据示例:\n     * {\n        \"data\": [\n            {\n                \"click_pv\": 0,\n                \"click_uv\": 0,\n                \"ftime\": 1425052800,\n                \"shake_pv\": 0,\n                \"shake_uv\": 0\n            },\n            {\n                \"click_pv\": 0,\n                \"click_uv\": 0,\n                \"ftime\": 1425139200,\n                \"shake_pv\": 0,\n                \"shake_uv\": 0\n            }\n        ],\n        \"errcode\": 0,\n        \"errmsg\": \"success.\"\n       }\n     * 字段说明:\n     * ftime 当天0 点对应的时间戳\n     * click_pv 点击摇周边消息的次数\n     * click_uv 点击摇周边消息的人数\n     * shake_pv 摇周边的次数\n     * shake_uv 摇周边的人数\n     * @author binsee<binsee@163.com>\n     * @version 2015-4-21 00:43:00\n     */\n    public function pageShakeAroundStatistics($page_id,$begin_date,$end_date){\n        if (!$this->access_token && !$this->checkAuth()) return false;\n        $data = array(\n            'page_id' => $page_id,\n            'begin_date' => $begin_date,\n            'end_date' => $end_date\n        );\n        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::SHAKEAROUND_STATISTICS_DEVICE . 'access_token=' . $this->access_token, self::json_encode($data));\n        $this->log($result);\n        if ($result) {\n            $json = json_decode($result, true);\n            if (!$json || !empty($json['errcode'])) {\n                $this->errCode = $json['errcode'];\n                $this->errMsg  = $json['errmsg'];\n                return false;\n            }\n            return $json;\n        }\n        return false;\n    }\n\n\t/**\n\t * 根据订单ID获取订单详情\n\t * @param string $order_id 订单ID\n\t * @return order array|bool\n\t */\n\tpublic function getOrderByID($order_id){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\tif (!$order_id) return false;\n\n\t\t$data = array(\n\t\t\t'order_id'=>$order_id\n\t\t);\n\t\t$result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_GETBYID.'access_token='.$this->access_token, self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (isset($json['errcode']) && $json['errcode']) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json['order'];\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 根据订单状态/创建时间获取订单详情\n\t * @param int $status 订单状态(不带该字段-全部状态, 2-待发货, 3-已发货, 5-已完成, 8-维权中, )\n\t * @param int $begintime 订单创建时间起始时间(不带该字段则不按照时间做筛选)\n\t * @param int $endtime 订单创建时间终止时间(不带该字段则不按照时间做筛选)\n\t * @return order list array|bool\n\t */\n\tpublic function getOrderByFilter($status = null, $begintime = null, $endtime = null){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\n\t\t$data = array();\n\n\t\t$valid_status = array(2, 3, 5, 8);\n\t\tif (is_numeric($status) && in_array($status, $valid_status)) {\n\t\t\t$data['status'] = $status;\n\t\t}\n\n\t\tif (is_numeric($begintime) && is_numeric($endtime)) {\n\t\t\t$data['begintime'] = $begintime;\n\t\t\t$data['endtime'] = $endtime;\n\t\t}\n\t\t$result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_GETBYFILTER.'access_token='.$this->access_token, self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (isset($json['errcode']) && $json['errcode']) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn $json['order_list'];\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 设置订单发货信息\n\t * @param string $order_id 订单 ID\n\t * @param int $need_delivery 商品是否需要物流(0-不需要，1-需要)\n\t * @param string $delivery_company 物流公司 ID\n\t * @param string $delivery_track_no 运单 ID\n\t * @param int $is_others 是否为 6.4.5 表之外的其它物流公司(0-否，1-是)\n\t * @return bool\n\t */\n\tpublic function setOrderDelivery($order_id, $need_delivery = 0, $delivery_company = null, $delivery_track_no = null, $is_others = 0){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\tif (!$order_id) return false;\n\n\t\t$data = array();\n\t\t$data['order_id'] = $order_id;\n\t\tif ($need_delivery) {\n\t\t\t$data['delivery_company'] = $delivery_company;\n\t\t\t$data['delivery_track_no'] = $delivery_track_no;\n\t\t\t$data['is_others'] = $is_others;\n\t\t}\n\t\telse {\n\t\t\t$data['need_delivery'] = $need_delivery;\n\t\t}\n\n\t\t$result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_SETDELIVERY.'access_token='.$this->access_token, self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (isset($json['errcode']) && $json['errcode']) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 关闭订单\n\t * @param string $order_id 订单 ID\n\t * @return bool\n\t */\n\tpublic function closeOrder($order_id){\n\t\tif (!$this->access_token && !$this->checkAuth()) return false;\n\t\tif (!$order_id) return false;\n\n\t\t$data = array(\n\t\t\t'order_id'=>$order_id\n\t\t);\n\n\t\t$result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_CLOSE.'access_token='.$this->access_token, self::json_encode($data));\n\t\tif ($result)\n\t\t{\n\t\t\t$json = json_decode($result,true);\n\t\t\tif (isset($json['errcode']) && $json['errcode']) {\n\t\t\t\t$this->errCode = $json['errcode'];\n\t\t\t\t$this->errMsg = $json['errmsg'];\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate function parseSkuInfo($skuInfo) {\n\t\t$skuInfo = str_replace(\"\\$\", \"\", $skuInfo);\n\t\t$matches = explode(\";\", $skuInfo);\n\n\t\t$result = array();\n\t\tforeach ($matches as $matche) {\n\t\t\t$arrs = explode(\":\", $matche);\n\t\t\t$result[$arrs[0]] = $arrs[1];\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t/**\n\t * 获取订单SkuInfo - 订单付款通知\n\t * 当Event为 merchant_order(订单付款通知)\n\t * @return array|boolean\n\t */\n\tpublic function getRevOrderSkuInfo(){\n\t\tif (isset($this->_receive['SkuInfo']))     //订单 SkuInfo\n\t\t\treturn $this->parseSkuInfo($this->_receive['SkuInfo']);\n\t\telse\n\t\t\treturn false;\n\t}\n}\n/**\n * PKCS7Encoder class\n *\n * 提供基于PKCS7算法的加解密接口.\n */\nclass PKCS7Encoder\n{\n    public static $block_size = 32;\n\n    /**\n     * 对需要加密的明文进行填充补位\n     * @param $text 需要进行填充补位操作的明文\n     * @return 补齐明文字符串\n     */\n    function encode($text)\n    {\n        $block_size = PKCS7Encoder::$block_size;\n        $text_length = strlen($text);\n        //计算需要填充的位数\n        $amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size);\n        if ($amount_to_pad == 0) {\n            $amount_to_pad = PKCS7Encoder::$block_size;\n        }\n        //获得补位所用的字符\n        $pad_chr = chr($amount_to_pad);\n        $tmp = \"\";\n        for ($index = 0; $index < $amount_to_pad; $index++) {\n            $tmp .= $pad_chr;\n        }\n        return $text . $tmp;\n    }\n\n    /**\n     * 对解密后的明文进行补位删除\n     * @param decrypted 解密后的明文\n     * @return 删除填充补位后的明文\n     */\n    function decode($text)\n    {\n\n        $pad = ord(substr($text, -1));\n        if ($pad < 1 || $pad > PKCS7Encoder::$block_size) {\n            $pad = 0;\n        }\n        return substr($text, 0, (strlen($text) - $pad));\n    }\n\n}\n\n/**\n * Prpcrypt class\n *\n * 提供接收和推送给公众平台消息的加解密接口.\n */\nclass Prpcrypt\n{\n    public $key;\n\n    function __construct($k) {\n        $this->key = base64_decode($k . \"=\");\n    }\n\n    /**\n     * 兼容老版本php构造函数，不能在 __construct() 方法前边，否则报错\n     */\n    function Prpcrypt($k)\n    {\n        $this->key = base64_decode($k . \"=\");\n    }\n\n  /**\n   * 对明文进行加密\n   * @param string $text 需要加密的明文\n   * @return string 加密后的密文\n   */\n  public function encrypt($text, $appid){\n    try {\n      //获得16位随机字符串，填充到明文之前\n      $random = $this->getRandomStr();\n      $text = $random . pack(\"N\", strlen($text)) . $text . $appid;\n      $iv = substr($this->key, 0, 16);\n      $pkc_encoder = new PKCS7Encoder;\n      $text = $pkc_encoder->encode($text);\n      $encrypted = openssl_encrypt($text,'AES-256-CBC',substr($this->key, 0, 32),OPENSSL_ZERO_PADDING,$iv);\n      return array(ErrorCode::$OK, $encrypted);\n    } catch (Exception $e) {\n      //print $e;\n      return array(ErrorCode::$EncryptAESError, null);\n    }\n  }\n  /**\n   * 对密文进行解密\n   * @param string $encrypted 需要解密的密文\n   * @return string 解密得到的明文\n   */\n  public function decrypt($encrypted, $appid){\n    try {\n      $iv = substr($this->key, 0, 16);\n      $decrypted = openssl_decrypt($encrypted,'AES-256-CBC',substr($this->key, 0, 32),OPENSSL_ZERO_PADDING,$iv);\n    } catch (Exception $e) {\n      return array(ErrorCode::$DecryptAESError, null);\n    }\n    try {\n      //去除补位字符\n      $pkc_encoder = new PKCS7Encoder;\n      $result = $pkc_encoder->decode($decrypted);\n      //去除16位随机字符串,网络字节序和AppId\n      if (strlen($result) < 16)\n        return \"\";\n      $content = substr($result, 16, strlen($result));\n      $len_list = unpack(\"N\", substr($content, 0, 4));\n      $xml_len = $len_list[1];\n      $xml_content = substr($content, 4, $xml_len);\n      $from_appid = substr($content, $xml_len + 4);\n      if (!$appid)\n        $appid = $from_appid;\n      //如果传入的appid是空的，则认为是订阅号，使用数据中提取出来的appid\n    } catch (Exception $e) {\n      //print $e;\n      return array(ErrorCode::$IllegalBuffer, null);\n    }\n    if ($from_appid != $appid)\n      return array(ErrorCode::$ValidateAppidError, null);\n    //不注释上边两行，避免传入appid是错误的情况\n    return array(0, $xml_content, $from_appid);\n    //增加appid，为了解决后面加密回复消息的时候没有appid的订阅号会无法回复\n  }\n\n    /**\n     * 随机生成16位字符串\n     * @return string 生成的字符串\n     */\n    function getRandomStr()\n    {\n\n        $str = \"\";\n        $str_pol = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\";\n        $max = strlen($str_pol) - 1;\n        for ($i = 0; $i < 16; $i++) {\n            $str .= $str_pol[mt_rand(0, $max)];\n        }\n        return $str;\n    }\n\n}\n\n/**\n * error code\n * 仅用作类内部使用，不用于官方API接口的errCode码\n */\nclass ErrorCode\n{\n    public static $OK = 0;\n    public static $ValidateSignatureError = 40001;\n    public static $ParseXmlError = 40002;\n    public static $ComputeSignatureError = 40003;\n    public static $IllegalAesKey = 40004;\n    public static $ValidateAppidError = 40005;\n    public static $EncryptAESError = 40006;\n    public static $DecryptAESError = 40007;\n    public static $IllegalBuffer = 40008;\n    public static $EncodeBase64Error = 40009;\n    public static $DecodeBase64Error = 40010;\n    public static $GenReturnXmlError = 40011;\n    public static $errCode=array(\n            '0' => '处理成功',\n            '40001' => '校验签名失败',\n            '40002' => '解析xml失败',\n            '40003' => '计算签名失败',\n            '40004' => '不合法的AESKey',\n            '40005' => '校验AppID失败',\n            '40006' => 'AES加密失败',\n            '40007' => 'AES解密失败',\n            '40008' => '公众平台发送的xml不合法',\n            '40009' => 'Base64编码失败',\n            '40010' => 'Base64解码失败',\n            '40011' => '公众帐号生成回包xml失败'\n    );\n    public static function getErrText($err) {\n        if (isset(self::$errCode[$err])) {\n            return self::$errCode[$err];\n        }else {\n            return false;\n        };\n    }\n}\n"
  },
  {
    "path": "wiki/API接口错误码.md",
    "content": "## errCode.php\r\n### 关于API接口错误码有两个版本：\r\n**一个是普通公众号平台的errCode.php；\r\n一个是企业号平台的 qyerrCode.php\r\n用法都是一样的。**\r\n\r\n当调用API接口失败时，可以用此类来换取失败原因的中文说明。\r\n\r\n使用方法：\r\n```php\r\ninclude \"errCode.php\";  //或 qyerrCode.php\r\n\r\n$ret=ErrCode::getErrText(48001); //错误码可以通过公众号类库的公开变量errCode得到\r\n\r\nif ($ret) \r\n\techo $ret;\r\nelse \r\n    echo \"未找到对应的内容\";\r\n\r\n```"
  },
  {
    "path": "wiki/Home.md",
    "content": "# wechat-php-sdk\r\n\r\n微信公众平台php开发包,细化各项接口操作,支持链式调用  \r\n项目地址：**https://github.com/dodgepudding/wechat-php-sdk**  \r\n项目wiki：**http://binsee.github.io/wechat-php-sdk**  \r\n\r\n----\r\n\r\n## 使用详解\r\n使用前需先打开微信帐号的开发模式，详细步骤请查看微信公众平台接口使用说明：  \r\n微信公众平台： http://mp.weixin.qq.com/wiki/\r\n微信企业平台： http://qydev.weixin.qq.com/wiki/\r\n\r\n微信支付接入文档：https://mp.weixin.qq.com/cgi-bin/readtemplate?t=business/course2_tmpl\r\n\r\n微信多客服：http://dkf.qq.com\r\n\r\n## 功能目录\r\n\r\n - [[官方API类库]]\r\n    > wechat.class.php\r\n    > 调用官方API，具有更灵活的消息分类响应方式，支持链式调用操作 ；\r\n\r\n - [[企业号API类库]]\r\n    > qywechat.class.php\r\n    > 微信公众平台企业号PHP-SDK\r\n    > 调用官方API，具有更灵活的消息分类响应方式，支持链式调用操作 ；\r\n\r\n - [[API接口错误码]]\r\n    > errCode.php 或 qyerrCode.php\r\n    > 当调用API接口失败时，可以用此类来换取失败原因的中文说明。\r\n\r\n - [[旧版微信支付V2接口类库]]\r\n    > old_version/wechatpay.class.php\r\n    > 当调用API接口失败时，可以用此类来换取失败原因的中文说明。\r\n\r\n - ~~[[非官方扩展类库]]~~*(停止维护)*\r\n    > old_version/wechatext.class.php\r\n    > 非官方扩展API，模拟人工操作微信平台，此方式不保证长期有效。  \r\n\r\n - ~~[[授权登陆类库]]~~*(停止维护)*\r\n    > old_version/wechatauth.class.php\r\n    > 通过微信二维码登陆微信的API, 能实现第三方网站同步登陆\r\n\r\n - ~~[[内嵌JS]]~~*(已废弃)*\r\n    > old_version/wechat.js\r\n    > 微信内嵌网页功能调用js\r\n\r\n - [[为开发框架进行适配]]\r\n    > 为不同的开发框架进行适配缓存操作(保存access_token、jsapi_ticket)，及输出调试日志。\r\n\r\n\r\n\r\n"
  },
  {
    "path": "wiki/README.md",
    "content": "wiki目录说明\r\n==============\r\n这个目录是wechat-php-sdk项目的wiki文档\r\nMake By：binsee\r\n\r\n##说明 \r\n**这里的wiki文档可以让你更好的了解`wechat-php-sdk`项目，更好的使用。 **\r\n\r\n**欢迎对wiki文档内容进行补充，把`wechat-php-sdk`项目变得更清晰易懂。**\r\n\r\n##为你的github生成wiki\r\n**如果你在github上fork了`wechat-php-sdk`项目，而且想为项目生成wiki，可以用这里的文件来生成。**\r\n\r\n\r\n###使用步骤：\r\n1. 在你的github上，fork或者创建`wechat-php-sdk`项目\r\n\r\n2. 激活项目wiki，已激活的请跳过\r\n```\r\n进入项目的设置页面：\r\nhttps://github.com/你的用户名/wechat-php-sdk/settings\r\n找到Features一栏，把 Wikis 选项打钩，就可以激活你项目的wiki了\r\n```\r\n\r\n3. 进入项目的wiki页面：\r\n`https://github.com/你的用户名/wechat-php-sdk/wiki`\r\n\r\n4. 点绿色的 `Create the first page` 按钮\r\n\r\n5. 直接到下方点 `Save Page` 按钮\r\n\r\n6. 在右边找到 `Clone this wiki locally` 一栏，复制git地址：\r\n`git@github.com:你的用户名/wechat-php-sdk.wiki.git`\r\n\r\n7. 在项目的上一层目录执行 \r\n`git clone git@github.com:你的用户名/wechat-php-sdk.wiki.git`\r\n\r\n8. 进入新出现的 `wechat-php-sdk.wiki` 目录，把wiki目录下的文件都复制过来\r\n> **这里有个高级用法，就是使用连接方式把wiki目录链接过来，而不是复制**\r\n> windows下的用法:\r\n```\r\n#如项目目录为：E:\\wechat-php-sdk\\\r\n#项目wiki目录为:E:\\wechat-php-sdk.wiki\\\r\n执行命令：\r\nmklink /j E:\\wechat-php-sdk.wiki\\wiki E:\\wechat-php-sdk\\wiki\r\n```\r\n> 这样的话，两个目录就会被联接到一起。\r\n> 以后进行更改wiki在哪个目录都行，另一个目录都是同步的。\r\n> 分别在 项目目录 和 项目wiki目录 进行git提交就可以了。\r\n\r\n9. 然后直接缓存上传即可。\r\n\r\n10. 现在去你github上项目的wiki目录里看一下吧\r\n\r\n\r\n##生成page\r\n**你也可以将wiki文档，生成为个人站点，会更加直观。**\r\n\r\n**比如使用 Hexo 或者其他的框架之类。**\r\n\r\n**这块的话，请自行搜索相关资料。**"
  },
  {
    "path": "wiki/为开发框架进行适配.md",
    "content": "#为开发框架进行适配\r\n\r\n为不同的开发框架进行适配缓存操作(保存access_token、jsapi_ticket)，及输出调试日志。\r\n\r\n由于微信api需要缓存access_token与jsapi_ticket，而在不同框架下的缓存方式不同，所以原先在Wechat.class.php和QYWechat.class.php中缓存代码做了TODO标志。\r\n需要各位在使用不同框架时再进行修改，但确实很麻烦，因为对结构进行了修改。\r\n\r\n>取消了原先同步维护的Thinkphp版本，为Wechat类增加操作缓存3个重载方法`setCache`, `getCache`, `removeCache`，以及修改`log`方法可以重载。\r\n>分别来实现在不同开发框架下的设置缓存、读取缓存、清除缓存、日志输出4个功能。  \r\n\r\n在不同的开发框架下使用Wechat类库，请继承Wechat类，根据需要实现这4个方法。  \r\n可参考Thinkphp版的`TPWechat.class.php`为不同框架进行适配。\r\n欢迎提交其他框架的适配文件到项目库来。  \r\n\r\n为Thinkphp进行适配的示例如下：\r\n```php\r\n/**\r\n *\t微信公众平台PHP-SDK, ThinkPHP实例\r\n *  @author dodgepudding@gmail.com\r\n *  @link https://github.com/dodgepudding/wechat-php-sdk\r\n *  @version 1.2\r\n *  usage:\r\n *   $options = array(\r\n *\t\t\t'token'=>'tokenaccesskey', //填写你设定的key\r\n *\t\t\t'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey\r\n *\t\t\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id\r\n *\t\t\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥\r\n *\t\t);\r\n *\t $weObj = new TPWechat($options);\r\n *   $weObj->valid();\r\n *   ...\r\n *  \r\n */\r\nclass TPWechat extends Wechat\r\n{\r\n\t/**\r\n\t * log overwrite\r\n\t * @see Wechat::log()\r\n\t */\r\n\tprotected function log($log){\r\n\t\tif ($this->debug) {\r\n\t\t\tif (function_exists($this->logcallback)) {\r\n\t\t\t\tif (is_array($log)) $log = print_r($log,true);\r\n\t\t\t\treturn call_user_func($this->logcallback,$log);\r\n\t\t\t}elseif (class_exists('Log')) {\r\n\t\t\t\tLog::write('wechat：'.$log, Log::DEBUG);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\t\r\n\t/**\r\n\t * 重载设置缓存\r\n\t * @param string $cachename\r\n\t * @param mixed $value\r\n\t * @param int $expired\r\n\t * @return boolean\r\n\t */\r\n\tprotected function setCache($cachename,$value,$expired){\r\n\t\treturn S($cachename,$value,$expired);\r\n\t}\r\n\t\r\n\t/**\r\n\t * 重载获取缓存\r\n\t * @param string $cachename\r\n\t * @return mixed\r\n\t */\r\n\tprotected function getCache($cachename){\r\n\t\treturn S($cachename);\r\n\t}\r\n\t\r\n\t/**\r\n\t * 重载清除缓存\r\n\t * @param string $cachename\r\n\t * @return boolean\r\n\t */\r\n\tprotected function removeCache($cachename){\r\n\t\treturn S($cachename,null);\r\n\t}\r\n}\r\n```"
  },
  {
    "path": "wiki/企业号API类库.md",
    "content": "# qywechat.class.php\r\n\r\n## 企业号API类库\r\n调用官方API，具有更灵活的消息分类响应方式，支持链式调用操作 ； \r\n\r\n## 主要功能 \r\n- 接入验证\r\n- 自动回复（文本、图片、语音、视频、音乐、图文）\r\n- 菜单操作（查询、创建、删除）\r\n- 部门管理（创建、更新、删除、获取部门列表）\r\n- 成员管理（创建、更新、删除、获取成员信息，获取部门成员列表）\r\n- 标签管理（创建、更新、删除、获取成员、添加成员、删除成员,获取标签列表）\r\n- 媒体文件管理（上传、获取）\r\n- 二次验证\r\n- OAuth2（生成授权url、获取成员信息）\r\n- 获取企业微信服务器IP列表\r\n- 微信JSAPI授权(获取ticket、获取签名)\r\n\r\n\r\n## 初始化动作 \r\n```php\r\n$options = array(\r\n  'token'=>'tokenaccesskey', //填写应用接口的Token\r\n  'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey\r\n  'appid'=>'wxdk1234567890', //填写高级调用功能的app id\r\n  'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥\r\n  'agentid'=>'1', //应用的id\r\n  'debug'=>false, //调试开关\r\n  '_logcallback'=>'logg', //调试输出方法，需要有一个string类型的参数\r\n);\r\n $weObj = new Wechat($options); //创建实例对象\r\n //TODO：调用$weObj各实例方法\r\n\r\n```\r\n\r\n## 被动接口方法:   \r\n* valid() 验证连接，被动接口必须调用\r\n* \r\n* getRev() 获取微信服务器发来信息(不返回结果)，被动接口必须调用\r\n* getRevData() 返回微信服务器发来的信息（数组）\r\n* getRevPostXml() 返回微信服务器发来的原始加密xml信息\r\n* getRevFrom()  返回消息发送者的userid\r\n* getRevTo()  返回消息接收者的id（即公众号id，一般与等同appid一致）\r\n* getRevAgentID() 返回接收消息的应用id\r\n* getRevType() 返回接收消息的类型\r\n* getRevID() 返回消息id\r\n* getRevCtime() 返回消息发送事件\r\n* getRevContent() 返回消息内容正文（文本型消息）\r\n* getRevPic() 返回图片信息（图片型信息） 返回数组{'mediaid'=>'','picurl'=>''}\r\n* getRevGeo() 返回地理位置（位置型信息） 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''}\r\n* getRevEventGeo() 返回事件地理位置（事件型信息） 返回数组{'x'=>'','y'=>'','precision'=>''}\r\n* getRevEvent() 返回事件类型（事件型信息） 返回数组{'event'=>'','key'=>''}\r\n* getRevScanInfo() 获取自定义菜单的扫码推事件信息，事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123')\r\n* getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明\r\n* getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送，事件类型为`location_select` 数组结构见php文件内方法说明\r\n* getRevVoice() 返回语音信息（语音型信息） 返回数组{'mediaid'=>'','format'=>''}\r\n* getRevVideo() 返回视频信息（视频型信息） 返回数组{'mediaid'=>'','thumbmediaid'=>''}\r\n* \r\n* text($text) 设置文本型消息，参数：文本内容\r\n* image($mediaid) 设置图片型消息，参数：图片的media_id\r\n* voice($mediaid) 设置语音型消息，参数：语音的media_id\r\n* video($mediaid='',$title,$description) 设置视频型消息，参数：视频的media_id、标题、摘要\r\n* news($newsData) 设置图文型消息，参数：数组。数组结构见php文件内方法说明\r\n* image($mediaid) 设置图片型消息，参数：图片的media_id\r\n* Message($msg = '',$append = false) 设置发送的消息（一般不需要调用这个方法）\r\n* reply() 将已经设置好的消息，回复给微信服务器\r\n  \r\n### 预定义常量列表：\r\n```php\r\n////消息类型，使用实例调用getRevType()方法取得\r\n    const MSGTYPE_TEXT = 'text';\r\n    const MSGTYPE_IMAGE = 'image';\r\n    const MSGTYPE_LOCATION = 'location';\r\n    const MSGTYPE_LINK = 'link';    //暂不支持\r\n    const MSGTYPE_EVENT = 'event';\r\n    const MSGTYPE_MUSIC = 'music';    //暂不支持\r\n    const MSGTYPE_NEWS = 'news';\r\n    const MSGTYPE_VOICE = 'voice';\r\n    const MSGTYPE_VIDEO = 'video';\r\n////事件类型，使用实例调用getRevEvent()方法取得\r\n    const EVENT_SUBSCRIBE = 'subscribe';       //订阅\r\n    const EVENT_UNSUBSCRIBE = 'unsubscribe';   //取消订阅\r\n    const EVENT_LOCATION = 'LOCATION';         //上报地理位置\r\n    const EVENT_ENTER_AGENT = 'enter_agent';   //用户进入应用\r\n    const EVENT_MENU_VIEW = 'VIEW';                     //菜单 - 点击菜单跳转链接\r\n    const EVENT_MENU_CLICK = 'CLICK';                   //菜单 - 点击菜单拉取消息\r\n    const EVENT_MENU_SCAN_PUSH = 'scancode_push';       //菜单 - 扫码推事件(客户端跳URL)\r\n    const EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL)\r\n    const EVENT_MENU_PIC_SYS = 'pic_sysphoto';          //菜单 - 弹出系统拍照发图\r\n    const EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album';  //菜单 - 弹出拍照或者相册发图\r\n    const EVENT_MENU_PIC_WEIXIN = 'pic_weixin';         //菜单 - 弹出微信相册发图器\r\n    const EVENT_MENU_LOCATION = 'location_select';      //菜单 - 弹出地理位置选择器\r\n    const EVENT_SEND_MASS = 'MASSSENDJOBFINISH';        //发送结果 - 高级群发完成\r\n    const EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果\r\n```\r\n\r\n## 主动接口方法：\r\n* checkAuth($appid='',$appsecret='',$token='') 通用auth验证方法,也用来换取ACCESS_TOKEN 。仅在需要手动指定access_token时才用`$token`\r\n* resetAuth($appid='') 清除记录的ACCESS_TOKEN\r\n* resetJsTicket($appid='') 删除JSAPI授权TICKET\r\n* getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET\r\n* getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组，可只提供url地址 \r\n* getSignature($arrdata,'sha1') 生成签名字串  \r\n* generateNonceStr($length=16) 获取随机字串  \r\n* createMenu($data,$agentid='') 创建菜单,参数:菜单内容数组,要创建菜单应用id\r\n* getMenu($agentid='') 获取菜单内容,参数:要获取菜单内容的应用id\r\n* deleteMenu($agentid='') 删除菜单,参数:要删除菜单的应用id\r\n* uploadMedia($data, $type) 上传媒体文件,参数请看php文件内方法说明(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时)\r\n* getMedia($media_id) 根据媒体文件ID获取媒体文件,参数:媒体id\r\n* getServerIp() 获取企业微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1')\r\n* createDepartment($data) 创建部门,参数: array(\"name\"=>\"邮箱产品组\",\"parentid\"=>\"1\",\"order\" =>  \"1\")\r\n* updateDepartment($data) 更新部门,参数: array(\"id\"=>\"1\"，\"name\"=>\"邮箱产品组\",\"parentid\"=>\"1\",\"order\" =>  \"1\")\r\n* deleteDepartment($id) 删除部门,参数：要删除的部门id\r\n* moveDepartment($data) 移动部门,参数：array(\"department_id\" => \"5\",\"to_parentid\" => \"2\",\"to_position\" => \"1\")\r\n* getDepartment() 获取部门列表，返回部门数组。其中department部门列表数据。以部门的order字段从小到大排列\r\n* createUser($data) 创建成员，参数请看php文件内方法说明\r\n* updateUser($data) 更新成员，参数请看php文件内方法说明\r\n* deleteUser($userid) 删除成员，参数：员工UserID\r\n* deleteUsers($userids) 批量删除成员，参数：员工UserID数组\r\n* getUserInfo($userid) 获取成员信息，参数：员工UserID\r\n* getUserList($department_id,$fetch_child=0,$status=0) 获取部门成员，参数：部门id，是否递归获取子部门，获取类型。\r\n> 0获取全部员工，1获取已关注成员列表，2获取禁用成员列表，4获取未关注成员列表。status可叠加\r\n* getUserListInfo($department_id,$fetch_child=0,$status=0) 获取部门成员详情，参数同上\r\n* getUserId($code,$agentid) 根据code获取员工UserID与手机设备号，参数：Oauth2.0或者二次验证返回的code值，跳转链接时所在的企业应用ID\r\n* sendInvite($userid,$invite_tips='') 邀请成员关注\r\n* createTag($data) 创建标签，参数：array(\"tagname\" => \"UI\")\r\n* updateTag($data) 更新标签，参数：array(\"tagid\" => \"1\",\"tagname\" => \"UI\")\r\n* deleteTag($tagid) 删除标签，参数：标签TagID\r\n* getTag($tagid) 获取标签成员，参数：标签TagID\r\n* addTagUser($data) 增加标签成员，参数请看php文件内方法说明\r\n* delTagUser($data) 删除标签成员，参数请看php文件内方法说明\r\n* getTagList() 获取标签列表，返回标签数组\r\n* sendMessage($data) 主动发送信息接口，参数请看php文件内方法说明\r\n* authSucc($userid) 二次验证，参数： 员工UserID\r\n* getOauthRedirect($callback,$state='STATE',$scope='snsapi_base') 组合授权跳转接口url\r\n\r\n\r\n\r\n\r\n\r\n\r\n## 企业号API类库调用示例：\r\n-------- \r\n可参考**test**目录下的**qydemo.php**\r\n```php\r\ninclude \"wechat.class.php\";\r\n$options = array(\r\n        'token'=>'9Ixxxxxxx',\t//填写应用接口的Token\r\n        'encodingaeskey'=>'d4o9WVg8sxxxxxxxxxxxxxxxxxxxxxx',//填写加密用的EncodingAESKey\r\n        'appid'=>'wxa07979baxxxxxxxx',\t//填写高级调用功能的appid\r\n);\r\n$weObj = new Wechat($options);\r\n$weObj->valid(); //注意, 企业号与普通公众号不同，必须打开验证，不要注释掉\r\n$type = $weObj->getRev()->getRevType();\r\nswitch($type) {\r\n\tcase Wechat::MSGTYPE_TEXT:\r\n\t\t\t$weObj->text(\"hello, I'm wechat\")->reply();\r\n\t\t\texit;\r\n\t\t\tbreak;\r\n\tcase Wechat::MSGTYPE_EVENT:\r\n\t\t\tbreak;\r\n\tcase Wechat::MSGTYPE_IMAGE:\r\n\t\t\tbreak;\r\n\tdefault:\r\n\t\t\t$weObj->text(\"help info\")->reply();\r\n}\r\n```\r\n"
  },
  {
    "path": "wiki/内嵌JS.md",
    "content": "#wechat.js\r\n\r\n**此JS脚本已经废弃不再更新，原因是官方在微信6.0.2版本开放了全新的JSAPI接口，更全面好用。请查看：[微信公众平台WIKI](http://mp.weixin.qq.com/wiki)**\r\n\r\n##微信内嵌网页特殊功能js调用：\r\n * WeixinJS.hideOptionMenu() 隐藏右上角按钮\r\n * WeixinJS.showOptionMenu() 显示右上角按钮\r\n * WeixinJS.hideToolbar() 隐藏工具栏\r\n * WeixinJS.showToolbar() 显示工具栏\r\n * WeixinJS.getNetworkType() 获取网络状态\r\n * WeixinJS.closeWindow() 关闭窗口\r\n * WeixinJS.scanQRCode() 扫描二维码\r\n * WeixinJS.openUrlByExtBrowser(url) 使用浏览器打开网址\r\n * WeixinJS.jumpToBizProfile(username) 跳转到指定公众账号页面\r\n * WeixinJS.sendEmail(title,content) 发送邮件\r\n * WeixinJS.openProductView(latitude,longitude,name,address,scale,infoUrl) 查看地图\r\n * WeixinJS.addContact(username) 添加微信账号\r\n * WeixinJS.imagePreview(urls,current) 调出微信内图片预览\r\n * WeixinJS.payCallback(appId,package,timeStamp,nonceStr,signType,paySign,callback) 微信JsApi支付接口\r\n * WeixinJS.editAddress(appId,addrSign,timeStamp,nonceStr,callback) 微信JsApi支付接口\r\n * 通过定义全局变量dataForWeixin配置触发分享的内容：\r\n\r\n ```javascript\r\n var dataForWeixin={\r\n\t   appId:\"\",\r\n\t   MsgImg:\"消息图片路径\",\r\n\t   TLImg:\"时间线图路径\",\r\n\t   url:\"分享url路径\",\r\n\t   title:\"标题\",\r\n\t   desc:\"描述\",\r\n\t   fakeid:\"\",\r\n\t   callback:function(){}\r\n\t};\r\n ```\r\n可以参考weshare.html及wechat.js的备注进行使用"
  },
  {
    "path": "wiki/官方API类库.md",
    "content": "# wechat.class.php\r\n\r\n调用官方API，具有更灵活的消息分类响应方式，支持链式调用操作 ； \r\n\r\n## 主要功能 \r\n- 接入验证 **（初级权限）**\r\n- 自动回复（文本、图片、语音、视频、音乐、图文） **（初级权限）**\r\n- 菜单操作（查询、创建、删除） **（菜单权限）**\r\n- 客服消息（文本、图片、语音、视频、音乐、图文） **（认证权限）**\r\n- 二维码（创建临时、永久二维码，获取二维码URL） **（服务号、认证权限）**\r\n- 长链接转短链接接口 **（服务号、认证权限）**\r\n- 分组操作（查询、创建、修改、移动用户到分组） **（认证权限）**\r\n- 网页授权（基本授权，用户信息授权） **（服务号、认证权限）**\r\n- 用户信息（查询用户基本信息、获取关注者列表） **（认证权限）**\r\n- 多客服功能（客服管理、获取客服记录、客服会话管理） **（认证权限）**\r\n- 媒体文件（上传、获取） **（认证权限）**\r\n- 高级群发 **（认证权限）**\r\n- 模板消息（设置所属行业、添加模板、发送模板消息） **（服务号、认证权限）**\r\n- 卡券管理（创建、修改、删除、发放、门店管理等） **（认证权限）**\r\n- 语义理解 **（服务号、认证权限）**\r\n- 获取微信服务器IP列表 **（初级权限）**  \r\n- 微信JSAPI授权(获取ticket、获取签名) **（初级权限）**  \r\n- 数据统计(用户、图文、消息、接口分析数据) **（认证权限）**  \r\n> 备注：  \r\n> 初级权限：基本权限，任何正常的公众号都有此权限  \r\n> 菜单权限：正常的服务号、认证后的订阅号拥有此权限  \r\n> 认证权限：分为订阅号、服务号认证，如前缀服务号则仅认证的服务号有此权限，否则为认证后的订阅号、服务号都有此权限  \r\n> 支付权限：仅认证后的服务号可以申请此权限  \r\n\r\n\r\n## 初始化动作 \r\n```php\r\n $options = array(\r\n\t'token'=>'tokenaccesskey', //填写你设定的key\r\n\t'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey\r\n\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询\r\n\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx' //填写高级调用功能的密钥\r\n\t);\r\n $weObj = new Wechat($options); //创建实例对象\r\n //TODO：调用$weObj各实例方法\r\n```\r\n\r\n### 被动接口方法:   \r\n* valid() 验证连接，被动接口处于加密模式时必须调用\r\n* \r\n* getRev() 获取微信服务器发来信息(不返回结果)，被动接口必须调用\r\n* getRevData() 返回微信服务器发来的信息（数组）\r\n* getRevFrom()  返回消息发送者的userid\r\n* getRevTo()  返回消息接收者的id（即公众号id）\r\n* getRevType() 返回接收消息的类型\r\n* getRevID() 返回消息id\r\n* getRevCtime() 返回消息发送时间\r\n* getRevContent() 返回消息内容正文或语音识别结果（文本型）\r\n* getRevPic() 返回图片信息（图片型信息） 返回数组{'mediaid'=>'','picurl'=>''}\r\n* getRevLink() 接收消息链接（链接型信息） 返回数组{'url'=>'','title'=>'','description'=>''}\r\n* getRevGeo() 返回地理位置（位置型信息） 返回数组{'x'=>'','y'=>'','scale'=>'','label'=>''}\r\n* getRevEventGeo() 返回事件地理位置（事件型信息） 返回数组{'x'=>'','y'=>'','precision'=>''}\r\n* getRevEvent() 返回事件类型（事件型信息） 返回数组{'event'=>'','key'=>''}\r\n* getRevScanInfo() 获取自定义菜单的扫码推事件信息，事件类型为`scancode_push`或`scancode_waitmsg` 返回数组array ('ScanType'=>'qrcode','ScanResult'=>'123123')\r\n* getRevSendPicsInfo() 获取自定义菜单的图片发送事件信息,事件类型为`pic_sysphoto`或`pic_photo_or_album`或`pic_weixin` 数组结构见php文件内方法说明\r\n* getRevSendGeoInfo() 获取自定义菜单的地理位置选择器事件推送，事件类型为`location_select` 数组结构见php文件内方法说明\r\n* getRevVoice() 返回语音信息（语音型信息） 返回数组{'mediaid'=>'','format'=>''}\r\n* getRevVideo() 返回视频信息（视频型信息） 返回数组{'mediaid'=>'','thumbmediaid'=>''}\r\n* getRevTicket() 返回接收TICKET（扫描带参数二维码,关注或SCAN事件） 返回二维码的ticket值\r\n* getRevSceneId() 返回二维码的场景值（扫描带参数二维码的关注事件） 返回二维码的参数值\r\n* getRevTplMsgID() 返回主动推送的消息ID（群发或模板消息事件） 返回MsgID值\r\n* getRevStatus() 返回模板消息发送状态（模板消息事件） 返回文本：success(成功)|failed:user block(用户拒绝接收)|failed: system failed(发送失败（非用户拒绝）)\r\n* getRevResult() 返回群发或模板消息发送结果（群发或模板消息事件） 返回数组，内容依事件类型而不同，参考开发文档中群发、模板消息推送事件\r\n* getRevKFCreate() 返回多客服-接入会话的客服账号（多客服-接入会话事件） 返回文本型\r\n* getRevKFClose() 返回多客服-处理会话的客服账号（多客服-接入会话事件） 返回文本型\r\n* getRevKFSwitch() 返回多客服-转接会话信息（多客服-转接会话事件） 返回数组\t{'FromKfAccount' => '','ToKfAccount' => ''}\r\n* getRevCardPass() 返回卡券-审核通过的卡券ID（卡券-卡券审核事件） 返回文本型\r\n* getRevCardGet() 返回卡券-用户领取卡券的相关信息（卡券-领取卡券事件） 返回数组{'CardId' => '','IsGiveByFriend' => '','UserCardCode' => ''}\r\n* getRevCardDel() 返回卡券-用户删除卡券的相关信息（卡券-删除卡券事件） 返回数组{'CardId' => '','UserCardCode' => ''}\r\n* \r\n* text($text) 设置文本型消息，参数：文本内容\r\n* image($mediaid) 设置图片型消息，参数：图片的media_id\r\n* voice($mediaid) 设置语音型消息，参数：语音的media_id\r\n* video($mediaid='',$title,$description) 设置视频型消息，参数：视频的media_id、标题、摘要\r\n* music($title,$desc,$musicurl,$hgmusicurl='',$thumbmediaid='') 设置回复音乐，参数：音乐标题、音乐描述、音乐链接、高音质链接、缩略图的媒体id\r\n* news($newsData) 设置图文型消息，参数：数组。数组结构见php文件内方法说明\r\n* image($mediaid) 设置图片型消息，参数：图片的media_id\r\n* Message($msg = '',$append = false) 设置发送的消息（一般不需要调用这个方法）\r\n* transfer_customer_service($customer_account = '') 转接多客服，如不指定客服可不提供参数，参数：指定客服的账号\r\n* reply() 将以上已经设置好的消息，回复给微信服务器\r\n\r\n### 预定义常量列表：\r\n```php\r\n////消息类型，使用实例调用getRevType()方法取得\r\nconst MSGTYPE_TEXT = 'text';\r\nconst MSGTYPE_IMAGE = 'image';\r\nconst MSGTYPE_LOCATION = 'location';\r\nconst MSGTYPE_LINK = 'link';\r\nconst MSGTYPE_EVENT = 'event';\r\nconst MSGTYPE_MUSIC = 'music';\r\nconst MSGTYPE_NEWS = 'news';\r\nconst MSGTYPE_VOICE = 'voice';\r\nconst MSGTYPE_VIDEO = 'video';\r\n////事件类型，使用实例调用getRevEvent()方法取得\r\nconst EVENT_SUBSCRIBE = 'subscribe';       //订阅\r\nconst EVENT_UNSUBSCRIBE = 'unsubscribe';   //取消订阅\r\nconst EVENT_SCAN = 'SCAN';                 //扫描带参数二维码\r\nconst EVENT_LOCATION = 'LOCATION';         //上报地理位置\r\nconst EVENT_MENU_VIEW = 'VIEW';                     //菜单 - 点击菜单跳转链接\r\nconst EVENT_MENU_CLICK = 'CLICK';                   //菜单 - 点击菜单拉取消息\r\nconst EVENT_MENU_SCAN_PUSH = 'scancode_push';       //菜单 - 扫码推事件(客户端跳URL)\r\nconst EVENT_MENU_SCAN_WAITMSG = 'scancode_waitmsg'; //菜单 - 扫码推事件(客户端不跳URL)\r\nconst EVENT_MENU_PIC_SYS = 'pic_sysphoto';          //菜单 - 弹出系统拍照发图\r\nconst EVENT_MENU_PIC_PHOTO = 'pic_photo_or_album';  //菜单 - 弹出拍照或者相册发图\r\nconst EVENT_MENU_PIC_WEIXIN = 'pic_weixin';         //菜单 - 弹出微信相册发图器\r\nconst EVENT_MENU_LOCATION = 'location_select';      //菜单 - 弹出地理位置选择器\r\nconst EVENT_SEND_MASS = 'MASSSENDJOBFINISH';        //发送结果 - 高级群发完成\r\nconst EVENT_SEND_TEMPLATE = 'TEMPLATESENDJOBFINISH';//发送结果 - 模板消息发送结果\r\nconst EVENT_KF_SEESION_CREATE = 'kfcreatesession';  //多客服 - 接入会话\r\nconst EVENT_KF_SEESION_CLOSE = 'kfclosesession';    //多客服 - 关闭会话\r\nconst EVENT_KF_SEESION_SWITCH = 'kfswitchsession';  //多客服 - 转接会话\r\nconst EVENT_CARD_PASS = 'card_pass_check';          //卡券 - 审核通过\r\nconst EVENT_CARD_NOTPASS = 'card_not_pass_check';   //卡券 - 审核未通过\r\nconst EVENT_CARD_USER_GET = 'user_get_card';        //卡券 - 用户领取卡券\r\nconst EVENT_CARD_USER_DEL = 'user_del_card';        //卡券 - 用户删除卡券\r\n```\r\n\r\n### 主动接口方法:   \r\n *  checkAuth($appid,$appsecret,$token) 此处传入公众后台高级接口提供的appid和appsecret, 或者手动指定$token为access_token。函数将返回access_token操作令牌\r\n *  resetAuth($appid='') 删除验证数据\r\n *  resetJsTicket($appid='') 删除JSAPI授权TICKET\r\n *  getJsTicket($appid='',$jsapi_ticket='') 获取JSAPI授权TICKET\r\n *  getJsSign($url, $timestamp=0, $noncestr='', $appid='') 获取JsApi使用签名信息数组，可只提供url地址 \r\n *  createMenu($data) 创建菜单 $data菜单结构详见 **[自定义菜单创建接口](http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口)**\r\n *  getServerIp() 获取微信服务器IP地址列表 返回数组array('127.0.0.1','127.0.0.1')\r\n *  getMenu() 获取菜单 \r\n *  deleteMenu() 删除菜单 \r\n *  uploadMedia($data, $type) 上传临时素材，有效期为3天(注意上传大文件时可能需要先调用 set_time_limit(0) 避免超时)\r\n *  getMedia($media_id,$is_video=false) 获取临时素材（含接收到的音频、视频媒体文件）\r\n *  uploadForeverMedia($data, $type,$is_video=false,$video_info=array()) 上传永久素材，可以在公众平台官网素材管理模块中看到\r\n *  uploadForeverArticles($data) 上传永久图文素材\r\n *  updateForeverArticles($media_id,$data,$index=0) 修改永久图文素材(认证后的订阅号可用)\r\n *  getForeverMedia($media_id,$is_video=false) 获取永久素材\r\n *  delForeverMedia($media_id) 删除永久素材\r\n *  getForeverList($type,$offset,$count) 获取永久素材列表(认证后的订阅号可用)\r\n *  getForeverCount() 获取永久素材总数\r\n *  uploadMpVideo($data) 上传视频素材，当需要群发视频时，必须使用此方法得到的MediaID，否则无法显示\r\n *  uploadArticles($data) 上传图文消息素材\r\n *  sendMassMessage($data) 高级群发消息\r\n *  sendGroupMassMessage($data) 高级群发消息（全体或分组群发）\r\n *  deleteMassMessage($msg_id) 删除群发图文消息\r\n *  previewMassMessage($data) 预览群发消息\r\n *  queryMassMessage($msg_id) 查询群发消息发送状态\r\n *  getQRCode($scene_id,$type=0,$expire=1800) 获取推广二维码ticket字串 \r\n *  getQRUrl($ticket) 获取二维码图片地址\r\n *  getShortUrl($long_url) 长链接转短链接接口\r\n *  getUserList($next_openid) 批量获取关注用户列表 \r\n *  getUserInfo($openid) 获取关注者详细信息 \r\n *  updateUserRemark($openid,$remark) 设置用户备注名\r\n *  getGroup() 获取用户分组列表 \r\n *  getUserGroup($openid) 获取用户所在分组\r\n *  createGroup($name) 新增自定分组 \r\n *  updateGroup($groupid,$name) 更改分组名称 \r\n *  updateGroupMembers($groupid,$openid) 移动用户分组  \r\n *  batchUpdateGroupMembers($groupid,$openid_list) 批量移动用户分组 \r\n *  sendCustomMessage($data) 发送客服消息  \r\n *  getOauthRedirect($callback,$state,$scope) 获取网页授权oAuth跳转地址  \r\n *  getOauthAccessToken() 通过回调的code获取网页授权access_token  \r\n *  getOauthRefreshToken($refresh_token) 通过refresh_token对access_token续期  \r\n *  getOauthUserinfo($access_token,$openid) 通过网页授权的access_token获取用户资料  \r\n *  getOauthAuth($access_token,$openid)  检验授权凭证access_token是否有效\r\n *  getSignature($arrdata,'sha1') 生成签名字串  \r\n *  generateNonceStr($length=16) 获取随机字串  \r\n *  setTMIndustry($id1,$id2='') 模板消息，设置所属行业\r\n *  addTemplateMessage($tpl_id) 模板消息，添加消息模板\r\n *  sendTemplateMessage($data) 发送模板消息\r\n *  getCustomServiceMessage($data) 获取多客服会话记录\r\n *  transfer_customer_service($customer_account) 转发多客服消息\r\n *  getCustomServiceKFlist() 获取多客服客服基本信息\r\n *  getCustomServiceOnlineKFlist() 获取多客服在线客服接待信息\r\n *  createKFSession($openid,$kf_account,$text='') 创建指定多客服会话\r\n *  closeKFSession($openid,$kf_account,$text='') 关闭指定多客服会话\r\n *  getKFSession($openid) 获取用户会话状态\r\n *  getKFSessionlist($kf_account) 获取指定客服的会话列表\r\n *  getKFSessionWait() 获取未接入会话列表\r\n *  addKFAccount($account,$nickname,$password) 添加客服账号\r\n *  updateKFAccount($account,$nickname,$password) 修改客服账号信息\r\n *  deleteKFAccount($account) 删除客服账号\r\n *  setKFHeadImg($account,$imgfile) 上传客服头像\r\n *  querySemantic($uid,$query,$category,$latitude=0,$longitude=0,$city=\"\",$region=\"\") 语义理解接口 参数含义及返回的json内容请查看 **[微信语义理解接口](http://mp.weixin.qq.com/wiki/index.php?title=语义理解)**\r\n *  getDatacube($type,$subtype,$begin_date,$end_date='') 获取统计数据 参数需注意$type与$subtype的定义\r\n> 获取统计数据方法 参数定义\r\n> \r\n| 数据分类 | $type值(字符串)  | 数据子分类 | $subtype值(字符串) | 时间跨度(天) |\r\n| --------- | :-------:  | --------- | :------: | ----: |\r\n| 用户分析 | 'user' | 获取用户增减数据 | 'summary' | 7 |\r\n| 用户分析 | 'user' | 获取累计用户数据 | 'cumulate' | 7 |\r\n| 图文分析 | 'article' | 获取图文群发每日数据 | 'summary' | 1 |\r\n| 图文分析 | 'article' | 获取图文群发总数据 | 'total' | 1 |\r\n| 图文分析 | 'article' | 获取图文统计数据 | 'read' | 3 |\r\n| 图文分析 | 'article' | 获取图文统计分时数据 | 'readhour' | 1 |\r\n| 图文分析 | 'article' | 获取图文分享转发数据 | 'share' | 7 |\r\n| 图文分析 | 'article' | 获取图文分享转发分时数据 | 'sharehour' | 1 |\r\n| 消息分析 | 'upstreammsg' | 获取消息发送概况数据 | 'summary' | 7 |\r\n| 消息分析 | 'upstreammsg' | 获取消息分送分时数据 | 'hour' | 1 |\r\n| 消息分析 | 'upstreammsg' | 获取消息发送周数据 | 'week' | 30 |\r\n| 消息分析 | 'upstreammsg' | 获取消息发送月数据 | 'month' | 30 |\r\n| 消息分析 | 'upstreammsg' | 获取消息发送分布数据 | 'dist' | 15 |\r\n| 消息分析 | 'upstreammsg' | 获取消息发送分布周数据 | 'distweek' | 30 |\r\n| 消息分析 | 'upstreammsg' | 获取消息发送分布月数据 | 'distmonth' | 30 |\r\n| 接口分析 | 'interface' | 获取接口分析数据 | 'summary' | 30 |\r\n| 接口分析 | 'interface' | 获取接口分析分时数据 | 'summaryhour' | 1 |\r\n需要注意 `begin_date`和`end_date`的差值需小于“最大时间跨度”（比如最大时间跨度为1时，`begin_date`和`end_date`的差值只能为0，才能小于1）\r\n\r\n *  createCard($data) 创建卡券\r\n *  updateCard($data) 修改卡券\r\n *  delCard($card_id) 删除卡券\r\n *  getCardInfo($card_id) 查询卡券详情\r\n *  getCardColors() 获取颜色列表\r\n *  getCardLocations() 拉取门店列表\r\n *  addCardLocations($data) 批量导入门店信息\r\n *  createCardQrcode($card_id) 生成卡券二维码\r\n *  consumeCardCode($code) 消耗 code\r\n *  decryptCardCode($encrypt_code) code 解码\r\n *  checkCardCode($code) 获取 code 的有效性\r\n *  getCardIdList($data) 批量查询卡列表\r\n *  updateCardCode($code,$card_id,$new_code) 更改 code\r\n *  unavailableCardCode($code,$card_id='') 设置卡券失效**(不可逆)**\r\n *  modifyCardStock($data) 库存修改\r\n *  activateMemberCard($data) 激活/绑定会员卡，参数结构请参看卡券开发文档(6.1.1 激活/绑定会员卡)章节\r\n *  updateMemberCard($data) 会员卡交易，参数结构请参看卡券开发文档(6.1.2 会员卡交易)章节\r\n *  updateLuckyMoney($code,$balance,$card_id='') 更新红包金额\r\n *  setCardTestWhiteList($openid=array(),$user=array()) 设置卡券测试白名单\r\n\r\n\r\n\r\n## 官方Wechat调用示例：\r\n```php\r\n//test1.php\r\ninclude \"wechat.class.php\";\r\n$options = array(\r\n\t\t'token'=>'tokenaccesskey' //填写你设定的key\r\n\t);\r\n$weObj = new Wechat($options);\r\n$weObj->valid();//明文或兼容模式可以在接口验证通过后注释此句，但加密模式一定不能注释，否则会验证失败\r\n$type = $weObj->getRev()->getRevType();\r\nswitch($type) {\r\n\tcase Wechat::MSGTYPE_TEXT:\r\n\t\t\t$weObj->text(\"hello, I'm wechat\")->reply();\r\n\t\t\texit;\r\n\t\t\tbreak;\r\n\tcase Wechat::MSGTYPE_EVENT:\r\n\t\t\tbreak;\r\n\tcase Wechat::MSGTYPE_IMAGE:\r\n\t\t\tbreak;\r\n\tdefault:\r\n\t\t\t$weObj->text(\"help info\")->reply();\r\n}\r\n```\r\n"
  },
  {
    "path": "wiki/授权登陆类库.md",
    "content": "# wechatauth.class.php  \r\n\r\n**此扩展类库已经不再更新，原因是官方开放平台对网站应用开放的有授权登陆接口，更标准，更好用。请查看：[微信开放平台](http://open.weixin.qq.com)**  \r\n\r\n通过微信二维码登陆微信的API, 能实现第三方网站同步登陆, 首先程序分别通过get_login_code和get_code_image方法获取授权二维码图片, 然后利用微信手机客户端扫描二维码图片后将自动跳出授权页面, 用户点击授权后即可获取对应的用户资料和头像信息. 详细验证步骤请看test3.php例子.   \r\n\r\n## 类主要方法:\r\n *  get_login_code() 获取登陆授权码, 通过授权码才能获取二维码  \r\n *  get_code_image($code='') 将上面获取的授权码转换为图片二维码  \r\n *  verify_code() 鉴定是否登陆成功,返回200为最终授权成功.  \r\n *  get_login_info() 鉴定成功后调用此方法即可获取用户基本信息  \r\n *  get_avatar($url) 获取用户头像图片数据  \r\n *  logout() 注销登陆  \r\n\r\n## 微信二维码Wechatauth登陆示例: \r\n```php\r\n//test3.php\r\n\tinclude \"../wechatauth.class.php\";\r\n\tsession_start();\r\n\t$sid  = session_id();\r\n\t$options = array(\r\n\t\t'account'=>$sid,\r\n\t\t'datapath'=>'../data/cookiecode_',\r\n\t); \r\n\t$wechat = new Wechatauth($options);\r\n\t\r\n\tif (isset($_POST['code'])) {\r\n\t\t$logincode = $_POST['code'];\r\n\t\t$vres = $wechat->set_login_code($logincode)->verify_code();\r\n\t\tif ($vres===false) {\r\n\t\t\t$result = array('status'=>0);\r\n\t\t} else {\r\n\t\t\t$result = array('status'=>$vres);\r\n\t\t\tif ($vres==200) {\r\n\t\t\t\t$result['info'] = $wechat->get_login_info();\r\n\t\t\t\t$result['cookie'] = $wechat->get_login_cookie(true);\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tdie(json_encode($result));\t\r\n\t}\r\n\t$logincode =  $wechat->get_login_code(); //获取授权码\r\n\t$qrimg = $wechat->get_code_image(); //待输出的二维码图片\r\n```\r\nHTML部分请看test/test3.php, 主要是定时ajax查询是否已经授权成功"
  },
  {
    "path": "wiki/旧版微信支付V2接口类库.md",
    "content": "# wechatpay.class.php \r\n\r\n旧版微信支付类库(微信支付V2)，已移动至old_version目录下。  \r\n自2014年8月开始申请到的微信支付都是V3接口，据官方说V2的会陆续升级为V3接口，但时间及升级渠道未确认。\r\n\r\n### 主要功能 \r\n- 获取access_token **（初级权限）**\r\n- 调用地址组件 **（支付权限）**\r\n- 生成订单签名数据 **（支付权限）**\r\n- 订单成功回调 **（支付权限）**\r\n- 发货通知 **（支付权限）**\r\n- 支付订单查询 **（支付权限）**  \r\n> 备注：  \r\n> 初级权限：基本权限，任何正常的公众号都有此权限  \r\n> 菜单权限：正常的服务号、认证后的订阅号拥有此权限  \r\n> 认证权限：分为订阅号、服务号认证，如前缀服务号则仅认证的服务号有此权限，否则为认证后的订阅号、服务号都有此权限  \r\n> 支付权限：仅认证后的服务号可以申请此权限  \r\n\r\n\r\n### 初始化动作 \r\n```php\r\n $options = array(\r\n\t'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询\r\n\t'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥\r\n\t'partnerid'=>'88888888', //财付通商户身份标识，支付权限专用，没有可不填\r\n\t'partnerkey'=>'', //财付通商户权限密钥Key，支付权限专用\r\n\t'paysignkey'=>'' //商户签名密钥Key，支付权限专用\r\n\t);\r\n $weObj = new Wechat($options); //创建实例对象\r\n //TODO：调用$weObj各实例方法\r\n```\r\n\r\n### 主动接口方法:   \r\n *  checkAuth($appid='',$appsecret='',$token='') 获取access_token。可根据appid和appsecret获取，或手动指定access_token\r\n *  resetAuth($appid='') 删除验证数据\r\n *  getSignature($arrdata,'sha1') 生成签名字串  \r\n *  generateNonceStr($length=16) 获取随机字串  \r\n *  createNativeUrl($productid) 生成原生支付url\r\n *  createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type=1,$bank_type=\"WX\",$input_charset=\"UTF-8\",$time_start=\"\",$time_expire=\"\",$transport_fee=\"\",$product_fee=\"\",$goods_tag=\"\",$attach=\"\") 生成订单package字符串  \r\n *  getPaySign($package, $timeStamp, $nonceStr) 支付签名(paySign)生成方法  \r\n *  checkOrderSignature($orderxml='') 回调通知签名验证  \r\n *  sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok') 发货通知  \r\n *  getPayOrder($out_trade_no) 查询订单信息  \r\n *  setUserToken($user_token) 设置用户授权密钥\r\n *  getAddrSign($url, $timeStamp, $nonceStr, $user_token='') 获取收货地址JS的签名"
  },
  {
    "path": "wiki/非官方扩展类库.md",
    "content": "# wechatext.class.php\r\n\r\n**此扩展类库已经不再更新，原因是官方对公众号开放了众多接口，此类库继续维护的意义不大**  \r\n\r\n非官方扩展API，需要配置公众平台账户和密码，能实现对已关注用户的点对点微信，此方式不保证长期有效。  \r\n类方法里提及的用户id在接口返回结构里表述为FakeId, 属同一概念, 在下面wechatauth类里则表示为Uin, 用户id对应的微信号必须通过getInfo()方法通过返回数组的Username值获取, 但非关注关系用户资料不能获取.  \r\n调用下列方法前必须经过login()方法和checkValid()验证方法才能获得调用权限. 有的账户无法通过登陆可能因为要求提供验证码, 可以手动登陆后把获取到的cookie写进程序存放cookie的文件解决.  \r\n程序使用了经过修改的snoopy兼容式HTTP类方法, 在类似BAE/SAE云服务器上可能不能正常运行, 因为云服务的curl方法是经过重写的, 某些header参数如网站来源参数不被支持.  \r\n\r\n## 类主要方法:\r\n *  send($id,$content) 向某用户id发送微信文字信息 \r\n *  sendNews($id,$msgid) 发送图文消息, 可通过getNewsList获取$msgid\r\n *  getUserList($page,$pagesize,$groupid) 获取用户信息\r\n *  getGroupList($page,$pagesize) 获取群组信息\r\n *  getNewsList($page,$pagesize) 获取图文信息列表 \r\n *  uploadFile($filepath,$type) 上传附件,包括图片/音频/视频/缩略图\r\n *  getFileList($type,$page,$pagesize) 获取素材库文件列表\r\n *  sendImage($id,$fid) 发送图片消息\r\n *  sendAudio($id,$fid) 发送音频消息\r\n *  sendVideo($id,$fid) 发送视频消息 \r\n *  getInfo($id) 根据id获取用户资料,注: 非关注关系用户资料不能获取  \r\n *  getNewMsgNum($lastid) 获取从$lastid算起新消息的数目  \r\n *  getTopMsg() 获取最新一条消息的数据, 此方法获取的消息id可以作为检测新消息的$lastid依据  \r\n *  getMsg($lastid,$offset=0,$perpage=50,$day=0,$today=0,$star=0) 获取最新的消息列表, 列表将返回消息id, 用户id, 消息类型, 文字消息等参数  \r\n *  消息返回结构:  {\"id\":\"消息id\",\"type\":\"类型号(1为文字,2为图片,3为语音)\",\"fileId\":\"0\",\"hasReply\":\"0\",\"fakeId\":\"用户uid\",\"nickName\":\"昵称\",\"dateTime\":\"时间戳\",\"content\":\"文字内容\"}   \r\n *  getMsgImage($msgid,$mode='large') 若消息type类型为2, 调用此方法获取图片数据  \r\n *  getMsgVoice($msgid) 若消息type类型为3, 调用此方法获取语音数据  \r\n\r\n\r\n## 扩展包Wechatext调用示例:\r\n```php\r\n//test2.php \r\n\tinclude \"wechatext.class.php\";\r\n\t\r\n\tfunction logdebug($text){\r\n\t\tfile_put_contents('./data/log.txt',$text.\"\\n\",FILE_APPEND);\t\t\r\n\t};\r\n\t\r\n\t$options = array(\r\n\t\t'account'=>'demo@domain.com',\r\n\t\t'password'=>'demo',\r\n\t\t'datapath'=>'./data/cookie_',\r\n\t\t\t'debug'=>true,\r\n\t\t\t'logcallback'=>'logdebug'\t\r\n\t); \r\n\t$wechat = new Wechatext($options);\r\n\tif ($wechat->checkValid()) {\r\n\t\t// 获取用户信息\r\n\t\t$data = $wechat->getInfo('3974255');\r\n\t\tvar_dump($data);\r\n\t\t// 获取最新一条消息\r\n\t\t$topmsg = $wechat->getTopMsg();\r\n\t\tvar_dump($topmsg);\r\n\t\t// 主动回复消息\r\n\t\tif ($topmsg && $topmsg['has_reply']==0)\r\n\t\t$wechat->send($topmsg['fakeid'],'hi '.$topmsg['nick_name'].',rev:'.$topmsg['content']);\t\r\n\t}\r\n```"
  }
]