完整的微信接口类

本类包含了微信官方公布的所有可使用的接口,包括了普通接口及高级接口,具体哪些接口可用还是要看您的账号级别。
具体接口如下:

    基础支持
    接收消息
    发送消息
    用户管理
    自定义菜单
    推广支持
    Weixin JS接口

代码如下:

<?php

class WeiXin {
  const MSG_TYPE_TEXT = 'text';
  const MSG_TYPE_IMAGE = 'image';
  const MSG_TYPE_VOICE = 'voice';
  const MSG_TYPE_VIDEO = 'video';
  const MSG_TYPE_LOCATION = 'location';
  const MSG_TYPE_LINK = 'link';
  const MSG_TYPE_EVENT = 'event';

  const MSG_EVENT_SUBSCRIBE = 'subscribe';
  const MSG_EVENT_UNSUBSCRIBE = 'unsubscribe';
  const MSG_EVENT_SCAN = 'scan';
  const MSG_EVENT_LOCATION = 'LOCATION';
  const MSG_EVENT_CLICK = 'CLICK';

  const REPLY_TYPE_TEXT = 'text';
  const REPLY_TYPE_IMAGE = 'image';
  const REPLY_TYPE_VOICE = 'voice';
  const REPLY_TYPE_VIDEO = 'video';
  const REPLY_TYPE_MUSIC = 'music';
  const REPLY_TYPE_NEWS = 'news';

  const MEDIA_TYPE_IMAGE = "image";
  const MEDIA_TYPE_VOICE = 'voice';
  const MEDIA_TYPE_VIDEO = 'video';
  const MEDIA_TYPE_THUMB = 'thumb';

  const SCOPE_REDIRECT = "snsapi_base";
  const SCOPE_POP = "snsapi_userinfo";

  private static $links = array(
      'message' => "https://api.weixin.qq.com/cgi-bin/message/custom/send",
      'group_create' => "https://api.weixin.qq.com/cgi-bin/groups/create",
      'group_get' => "https://api.weixin.qq.com/cgi-bin/groups/get",
      'group_getid' => "https://api.weixin.qq.com/cgi-bin/groups/getid",
      'group_rename' => "https://api.weixin.qq.com/cgi-bin/groups/update",
      'group_move' => "https://api.weixin.qq.com/cgi-bin/groups/members/update",
      'user_info' => "https://api.weixin.qq.com/cgi-bin/user/info",
      'user_get' => 'https://api.weixin.qq.com/cgi-bin/user/get',
      'menu_create' => 'https://api.weixin.qq.com/cgi-bin/menu/create',
      'menu_get' => 'https://api.weixin.qq.com/cgi-bin/menu/get',
      'menu_delete' => 'https://api.weixin.qq.com/cgi-bin/menu/delete',
      'qrcode' => 'https://api.weixin.qq.com/cgi-bin/qrcode/create',
      'showqrcode' => 'https://mp.weixin.qq.com/cgi-bin/showqrcode',
      'media_download' => 'http://file.api.weixin.qq.com/cgi-bin/media/get',
      'media_upload' => 'http://file.api.weixin.qq.com/cgi-bin/media/upload',
      'oauth_code' => 'https://open.weixin.qq.com/connect/oauth2/authorize',
      'oauth_access_token' => 'https://api.weixin.qq.com/sns/oauth2/access_token',
      'oauth_refresh' => 'https://api.weixin.qq.com/sns/oauth2/refresh_token',
      'oauth_userinfo' => 'https://api.weixin.qq.com/sns/userinfo'
  );

  private static $errors = array(
      '-1' => '系统繁忙',
      '0' => '请求成功',
      '40001' => '获取access_token时AppSecret错误,或者access_token无效',
      '40002' => '不合法的凭证类型',
      '40003' => '不合法的OpenID',
      '40004' => '不合法的媒体文件类型',
      '40005' => '不合法的文件类型',
      '40006' => '不合法的文件大小',
      '40007' => '不合法的媒体文件id',
      '40008' => '不合法的消息类型',
      '40009' => '不合法的图片文件大小',
      '40010' => '不合法的语音文件大小',
      '40011' => '不合法的视频文件大小',
      '40012' => '不合法的缩略图文件大小',
      '40013' => '不合法的APPID',
      '40014' => '不合法的access_token',
      '40015' => '不合法的菜单类型',
      '40016' => '不合法的按钮个数',
      '40017' => '不合法的按钮个数',
      '40018' => '不合法的按钮名字长度',
      '40019' => '不合法的按钮KEY长度',
      '40020' => '不合法的按钮URL长度',
      '40021' => '不合法的菜单版本号',
      '40022' => '不合法的子菜单级数',
      '40023' => '不合法的子菜单按钮个数',
      '40024' => '不合法的子菜单按钮类型',
      '40025' => '不合法的子菜单按钮名字长度',
      '40026' => '不合法的子菜单按钮KEY长度',
      '40027' => '不合法的子菜单按钮URL长度',
      '40028' => '不合法的自定义菜单使用用户',
      '40029' => '不合法的oauth_code',
      '40030' => '不合法的refresh_token',
      '40031' => '不合法的openid列表',
      '40032' => '不合法的openid列表长度',
      '40033' => '不合法的请求字符,不能包含uxxxx格式的字符',
      '40035' => '不合法的参数',
      '40038' => '不合法的请求格式',
      '40039' => '不合法的URL长度',
      '40050' => '不合法的分组id',
      '40051' => '分组名字不合法',
      '41001' => '缺少access_token参数',
      '41002' => '缺少appid参数',
      '41003' => '缺少refresh_token参数',
      '41004' => '缺少secret参数',
      '41005' => '缺少多媒体文件数据',
      '41006' => '缺少media_id参数',
      '41007' => '缺少子菜单数据',
      '41008' => '缺少oauth code',
      '41009' => '缺少openid',
      '42001' => 'access_token超时',
      '42002' => 'refresh_token超时',
      '42003' => 'oauth_code超时',
      '43001' => '需要GET请求',
      '43002' => '需要POST请求',
      '43003' => '需要HTTPS请求',
      '43004' => '需要接收者关注',
      '43005' => '需要好友关系',
      '44001' => '多媒体文件为空',
      '44002' => 'POST的数据包为空',
      '44003' => '图文消息内容为空',
      '44004' => '文本消息内容为空',
      '45001' => '多媒体文件大小超过限制',
      '45002' => '消息内容超过限制',
      '45003' => '标题字段超过限制',
      '45004' => '描述字段超过限制',
      '45005' => '链接字段超过限制',
      '45006' => '图片链接字段超过限制',
      '45007' => '语音播放时间超过限制',
      '45008' => '图文消息超过限制',
      '45009' => '接口调用超过限制',
      '45010' => '创建菜单个数超过限制',
      '45015' => '回复时间超过限制',
      '45016' => '系统分组,不允许修改',
      '45017' => '分组名字过长',
      '45018' => '分组数量超过上限',
      '46001' => '不存在媒体数据',
      '46002' => '不存在的菜单版本',
      '46003' => '不存在的菜单数据',
      '46004' => '不存在的用户',
      '47001' => '解析JSON/XML内容错误',
      '48001' => 'api功能未授权',
      '50001' => '用户未授权该api'
  );

  private static $debug = false;

  private $token;
  private $postStr;
  private $postObj;

  private $appid;
  private $appsecret;
  private $access_token;
  private $valid = 0;

  /**
   * @param      $token
   * @param null $appid
   * @param null $appsecret
   * @param bool $debug
   */
  public function __construct($token, $appid = null, $appsecret = null, $debug = false) {
    $this->token = $token;
    if (!empty($_GET) && $this->checkSignature())
      $this->handleRequest();
    $this->appid = $appid;
    $this->appsecret = $appsecret;
    self::$debug = $debug;
  }

  /**
   * 检查签名
   *
   * @return bool
   */
  private function checkSignature() {
    if (!isset($_GET['signature']) || !isset($_GET['timestamp']) || !isset($_GET['nonce']))
      return false;
    $signature = $_GET["signature"];
    $timestamp = $_GET["timestamp"];
    $nonce = $_GET["nonce"];

    $tmpArr = array($this->token, $timestamp, $nonce);
    sort($tmpArr);
    $tmpStr = implode($tmpArr);
    $tmpStr = sha1($tmpStr);

    if ($tmpStr == $signature)
      return true;
    else
      return false;
  }

  /**
   * 处理请求
   */
  private function handleRequest() {
    if (isset($_GET['echostr']))
    {
      echo $_GET['echostr'];
      exit;
    }
    else
    {
      $this->postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
      if (!empty($this->postStr))
      {
        $this->postObj = simplexml_load_string($this->postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
      }
      else
      {
        Log::simpleappend('fail', $this->postStr);
        exit;
      }
    }
  }

  /**
   * 获取高级接口的access_token
   *
   * @return bool
   */
  private function getAccessToken() {
    if ($this->appid && $this->appsecret)
    {
      if ($this->valid <= time())
      {
        $access = json_decode(Tools::curl("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appid}&secret={$this->appsecret}"));
        if (isset($access->access_token) && isset($access->expires_in))
        {
          $this->access_token = $access->access_token;
          $this->valid = time() + $access->expires_in;
        }
        else
          return false;
      }

      return true;
    }

    return false;
  }

  /**
   * 获取请求中的收信人OpenId,一般为公众账号自身
   *
   * @return mixed
   */
  public function getToUserName() {
    return $this->postObj->ToUserName;
  }

  /**
   * 请求中的发信人OpenId
   *
   * @return mixed
   */
  public function getFromUserName() {
    return $this->postObj->FromUserName;
  }

  /**
   * 获取Object格式的消息内容
   *
   * @return mixed
   */
  public function getPostObj() {
    return $this->postObj;
  }

  /**
   * 获取字符串格式的消息内容
   *
   * @return mixed
   */
  public function getPostStr() {
    return $this->postStr;
  }

  /**
   * 获取消息ID
   *
   * @return mixed
   */
  public function getMsgId() {
    return $this->postObj->MsgId;
  }

  /**
   * 消息创建时间
   *
   * @return string
   */
  public function getCreateTime() {
    return strval($this->postObj->CreateTime);
  }

  //获取消息类型
  public function getMsgType() {
    return $this->postObj->MsgType;
  }

  /**
   * 获取事件推送的时间类型,非事件的消息返回false
   *
   * @return mixed
   */
  public function getEvent() {
    if ($this->postObj->MsgType == self::MSG_TYPE_EVENT)
    {
      return $this->postObj->Event;
    }

    return false;
  }

  /**
   * 是否为文本消息
   *
   * @return bool
   */
  public function isTextMessage() {
    return $this->postObj->MsgType == self::MSG_TYPE_TEXT;
  }

  /**
   * 是否为图片消息
   *
   * @return bool
   */
  public function isImageMessage() {
    return $this->postObj->MsgType == self::MSG_TYPE_IMAGE;
  }

  /**
   * 是否为语音消息
   *
   * @return bool
   */
  public function isVoiceMessage() {
    return $this->postObj->MsgType == self::MSG_TYPE_VOICE;
  }

  /**
   * 是否为视频消息
   *
   * @return bool
   */
  public function isVideoMessage() {
    return $this->postObj->MsgType == self::MSG_TYPE_VIDEO;
  }

  /**
   * 是否为地理位置消息
   *
   * @return bool
   */
  public function isLocationMessage() {
    return $this->postObj->MsgType == self::MSG_TYPE_LOCATION;
  }

  /**
   * 是否为链接消息
   *
   * @return bool
   */
  public function isLinkMessage() {
    return $this->postObj->MsgType == self::MSG_TYPE_LINK;
  }

  /**
   * 是否为普通关注事件
   *
   * @return bool
   */
  public function isEventSubscribe() {
    return $this->postObj->MsgType == self::MSG_TYPE_EVENT && $this->postObj->Event == self::MSG_EVENT_SUBSCRIBE && !isset($this->postObj->EventKey);
  }

  /**
   * 是否为取消关注事件
   *
   * @return bool
   */
  public function isEventUnSubscribe() {
    return $this->postObj->MsgType == self::MSG_TYPE_EVENT && $this->postObj->Event == self::MSG_EVENT_UNSUBSCRIBE;
  }

  /**
   * 是否为扫描二维码关注事件
   *
   * @return bool
   */
  public function isEventScanSubscript() {
    return $this->postObj->MsgType == self::MSG_TYPE_EVENT && $this->postObj->Event == self::MSG_EVENT_SUBSCRIBE && isset($this->postObj->EventKey);
  }

  /**
   * 是否为已关注扫描二维码事件
   *
   * @return bool
   */
  public function isEventScan() {
    return $this->postObj->MsgType == self::MSG_TYPE_EVENT && $this->postObj->Event == self::MSG_EVENT_SCAN;
  }

  /**
   * 是否为上报地理位置事件
   *
   * @return bool
   */
  public function isEventLocation() {
    return $this->postObj->MsgType == self::MSG_TYPE_EVENT && $this->postObj->Event == self::MSG_EVENT_LOCATION;
  }

  /**
   * 是否为菜单点击事件
   *
   * @return bool
   */
  public function isEventClick() {
    return $this->postObj->MsgType == self::MSG_TYPE_EVENT && $this->postObj->Event == self::MSG_EVENT_CLICK;
  }

  /**
   * 获取文本消息内容
   *
   * @return string
   */
  public function requestText() {
    return strval($this->postObj->Content);
  }

  /**
   * 获取图片消息内容
   *
   * @return array
   */
  public function requestImage() {
    $image = array();
    $image['PicUrl'] = strval($this->postObj->PicUrl);
    $image['MediaId'] = strval($this->postObj->MediaId);

    return $image;
  }

  /**
   * 获取语音消息内容,可能包含语音识别结果
   *
   * @return array
   */
  public function requestVoice() {
    $voice = array();
    $voice['MediaId'] = $this->postObj->MediaId;
    $voice['Format'] = $this->postObj->Format;
    if (isset($this->postObj->Recognition))
      $voice['Recognition'] = $this->postObj->Recognition;

    return $voice;
  }

  /**
   * 获取视频消息内容
   *
   * @return array
   */
  public function requestVideo() {
    $video = array();
    $video['MediaId'] = $this->postObj->MediaId;
    $video['ThumbMediaId'] = $this->postObj->ThumbMediaId;

    return $video;
  }

  /**
   * 获取地理位置消息内容
   *
   * @return array
   */
  public function requestLocation() {
    $location = array();
    $location['Location_X'] = strval($this->postObj->Location_X);
    $location['Location_Y'] = strval($this->postObj->Location_Y);
    $location['Scale'] = strval($this->postObj->Scale);
    $location['Label'] = strval($this->postObj->Label);

    return $location;
  }

  /**
   * 获取链接消息内容
   *
   * @return array
   */
  public function requestLink() {
    $link = array();
    $link['Title'] = strval($this->postObj->Title);
    $link['Description'] = strval($this->postObj->Description);
    $link['Url'] = strval($this->postObj->Url);

    return $link;
  }

  /**
   * 获取扫描二维码事件内容
   *
   * @return array
   */
  public function requestEventScan() {
    $info = array();
    $info['EventKey'] = $this->postObj->EventKey;
    $info['Ticket'] = $this->postObj->Ticket;
    $info['Scene_Id'] = str_replace('qrscene_', '', $this->postObj->EventKey);

    return $info;
  }

  /**
   * 获取上报地理位置事件内容
   *
   * @return array
   */
  public function requestEventLocation() {
    $location = array();
    $location['Latitude'] = $this->postObj->Latitude;
    $location['Longitude'] = $this->postObj->Longitude;
    $location['Precision'] = $this->postObj->Precision;

    return $location;
  }

  /**
   * 获取菜单点击事件内容
   *
   * @return string
   */
  public function requestEventClick() {
    return strval($this->postObj->EventKey);
  }

  /**
   * GET方法
   *
   * @param $link
   *
   * @return mixed
   */
  private static function get($link) {
    if (self::$debug)
      Log::out("weixin_debug", 'I', "get:" . $link);

    return json_decode(Tools::curl($link));
  }

  /**
   * POST方法
   *
   * @param $link
   * @param $data
   *
   * @return mixed
   */
  private static function post($link, $data) {
    if (self::$debug)
      Log::out("weixin_debug", 'I', "post:", $link . ":" . serialize($data));

    return json_decode(Tools::curl($link, 'POST', $data));
  }

  /**
   * 向微信服务器ECHO内容
   *
   * @param $content
   */
  private static function response($content) {
    if (self::$debug)
      Log::out("weixin_debug", 'I', 'echo:' . $content);
    echo $content;
  }

  /**
   * 获取错误代码中文描述
   *
   * @param $errorcode
   *
   * @return mixed
   */
  public static function error($errorcode) {
    return self::$errors[$errorcode];
  }

  /**
   * 回复文本消息
   *
   * @param $content
   */
  public function responseTextMessage($content) {
    $textTpl = "<xml>
    <ToUserName><![CDATA[%s]]></ToUserName>
    <FromUserName><![CDATA[%s]]></FromUserName>
    <CreateTime>%s</CreateTime>
    <MsgType><![CDATA[%s]]></MsgType>
    <Content><![CDATA[%s]]></Content>
    </xml>";
    $resultStr = sprintf($textTpl, $this->postObj->FromUserName, $this->postObj->ToUserName, time(), self::REPLY_TYPE_TEXT, $content);
    if (!headers_sent())
      header('Content-Type: application/xml; charset=utf-8');
    self::response($resultStr);
  }

  /**
   * 回复图片消息
   *
   * @param $mediaid
   */
  public function responseImageMessage($mediaid) {
    $textTpl = "<xml>
    <ToUserName><![CDATA[%s]]></ToUserName>
    <FromUserName><![CDATA[%s]]></FromUserName>
    <CreateTime>%s</CreateTime>
    <MsgType><![CDATA[%s]]></MsgType>
    <Image>
    <MediaId><![CDATA[%s]]></MediaId>
    </Image>
    </xml>";
    $resultStr = sprintf($textTpl, $this->postObj->FromUserName, $this->postObj->ToUserName, time(), self::REPLY_TYPE_IMAGE, $mediaid);
    if (!headers_sent())
      header('Content-Type: application/xml; charset=utf-8');
    self::response($resultStr);
  }

  /**
   * 回复语音消息
   *
   * @param $mediaid
   */
  public function responseVoiceMessage($mediaid) {
    $textTpl = "<xml>
    <ToUserName><![CDATA[%s]]></ToUserName>
    <FromUserName><![CDATA[%s]]></FromUserName>
    <CreateTime>%s</CreateTime>
    <MsgType><![CDATA[%s]]></MsgType>
    <Voice>
    <MediaId><![CDATA[%s]]></MediaId>
    </Voice>
    </xml>";
    $resultStr = sprintf($textTpl, $this->postObj->FromUserName, $this->postObj->ToUserName, time(), self::REPLY_TYPE_VOICE, $mediaid);
    if (!headers_sent())
      header('Content-Type: application/xml; charset=utf-8');
    self::response($resultStr);
  }

  /**
   * 回复视频消息
   *
   * @param        $mediaid
   * @param string $title
   * @param string $description
   */
  public function responseVideoMessage($mediaid, $title = "", $description = "") {
    $textTpl = "<xml>
    <ToUserName><![CDATA[%s]]></ToUserName>
    <FromUserName><![CDATA[%s]]></FromUserName>
    <CreateTime>%s</CreateTime>
    <MsgType><![CDATA[%s]]></MsgType>
    <Video>
    <MediaId><![CDATA[%s]]></MediaId>
    <Title><![CDATA[%s]]></Title>
    <Description><![CDATA[%s]]></Description>
    </Video>
    </xml>";
    $resultStr = sprintf($textTpl, $this->postObj->FromUserName, $this->postObj->ToUserName, time(), self::REPLY_TYPE_VIDEO, $mediaid, $title, $description);
    if (!headers_sent())
      header('Content-Type: application/xml; charset=utf-8');
    self::response($resultStr);
  }

  /**
   * 回复音乐消息
   *
   * @param string $title
   * @param string $description
   * @param string $url
   * @param string $hq_url
   */
  public function responseMusicMessage($title = '', $description = '', $url = '', $hq_url = '') {
    $textTpl = '<xml>
    <ToUserName><![CDATA[%s]]></ToUserName>
    <FromUserName><![CDATA[%s]]></FromUserName>
    <CreateTime>%s</CreateTime>
    <MsgType><![CDATA[%s]]></MsgType>
    <Music>
    <Title><![CDATA[%s]]></Title>
    <Description><![CDATA[%s]]></Description>
    <MusicUrl><![CDATA[%s]]></MusicUrl>
    <HQMusicUrl><![CDATA[%s]]></HQMusicUrl>
    </Music>
    </xml>';
    $resultStr = sprintf($textTpl, $this->postObj->FromUserName, $this->postObj->ToUserName, time(), self::REPLY_TYPE_MUSIC, $title, $description, $url, $hq_url);
    if (!headers_sent())
      header('Content-Type: application/xml; charset=utf-8');
    self::response($resultStr);
  }

  /**
   * 回复图文消息
   *
   * @param $items
   *
   * @throws Exception
   */
  public function responseNewsMessage($items) {
    $textTpl = '<xml>
    <ToUserName><![CDATA[%s]]></ToUserName>
    <FromUserName><![CDATA[%s]]></FromUserName>
    <CreateTime>%s</CreateTime>
    <MsgType><![CDATA[%s]]></MsgType>
    <ArticleCount>%d</ArticleCount>
    <Articles>%s</Articles>
    </xml>';

    $itemTpl = '<item>
    <Title><![CDATA[%s]]></Title>
    <Discription><![CDATA[%s]]></Discription>
    <PicUrl><![CDATA[%s]]></PicUrl>
    <Url><![CDATA[%s]]></Url>
    </item>';

    $articles = '';
    if ($items && is_array($items))
    {
      foreach ($items as $item)
      {
        if (is_array($item) && (isset($item['Title']) || isset($item['Description']) || isset($item['PicUrl']) || isset($item['Url'])))
          $articles .= sprintf($itemTpl, $item['Title'], $item['Description'], $item['PicUrl'], $item['Url']);
        else
          throw new Exception("item => array('Title'=>'','Description'=>'','PicUrl'=>'','Url'=>'')");
      }
    }

    $resultStr = sprintf($textTpl, $this->postObj->FromUserName, $this->postObj->ToUserName, time(), self::REPLY_TYPE_NEWS, count($items), $articles);
    if (!headers_sent())
      header('Content-Type: application/xml; charset=utf-8');
    self::response($resultStr);
  }

  /**发送文本客服消息,需要access_token
   *
   * @param $openid
   * @param $content
   *
   * @return bool|mixed
   */
  public function sendTextMessage($openid, $content) {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['touser'] = $openid;
      $message['msgtype'] = "text";
      $message['text']['content'] = $content;

      return self::post(self::$links['message'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 发送图片客服消息,需要access_token
   *
   * @param $openid
   * @param $mediaid
   *
   * @return bool|mixed
   */
  public function sendImageMessage($openid, $mediaid) {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['touser'] = $openid;
      $message['msgtype'] = "image";
      $message['image']['media_id'] = $mediaid;

      return self::post(self::$links['message'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 发送语音客服消息,需要access_token
   *
   * @param $openid
   * @param $media_id
   *
   * @return bool|mixed
   */
  public function sendVoiceMessage($openid, $media_id) {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['touser'] = $openid;
      $message['msgtype'] = "voice";
      $message['voice']['media_id'] = $media_id;

      return self::post(self::$links['message'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 发送视频客服消息,需要access_token
   *
   * @param        $openid
   * @param        $media_id
   * @param string $title
   * @param string $description
   *
   * @return bool|mixed
   */
  public function sendVideoMessage($openid, $media_id, $title = "", $description = "") {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['touser'] = $openid;
      $message['msgtype'] = "video";
      $message['video']['media_id'] = $media_id;
      $message['video']['title'] = $title;
      $message['video']['description'] = $description;

      return self::post(self::$links['message'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 发送音乐客服消息,需要access_token
   *
   * @param        $openid
   * @param        $url
   * @param        $hq_url
   * @param        $media_id
   * @param string $title
   * @param string $description
   *
   * @return bool|mixed
   */
  public function sendMusicMessage($openid, $url, $hq_url, $media_id, $title = "", $description = "") {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['touser'] = $openid;
      $message['msgtype'] = "music";
      $message['music']['title'] = $title;
      $message['music']['description'] = $description;
      $message['music']['musicurl'] = $url;
      $message['music']['hqmusicurl'] = $hq_url;
      $message['music']['thumb_media_id'] = $media_id;

      return self::post(self::$links['message'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 发送图文客服消息,需要access_token
   *
   * @param $openid
   * @param $items
   *
   * @return bool|mixed
   * @throws Exception
   */
  public function sendNewsMessage($openid, $items) {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['touser'] = $openid;
      $message['msgtype'] = "news";
      if ($items && is_array($items))
      {
        foreach ($items as $item)
        {
          if (is_array($item) && (isset($item['Title']) || isset($item['Description']) || isset($item['PicUrl']) || isset($item['Url'])))
          {
            $it['title'] = isset($item['Title']) ? $item['Title'] : "";
            $it['description'] = isset($item['Description']) ? $item['Description'] : "";
            $it['url'] = isset($item['Url']) ? $item['Url'] : "";
            $it['picurl'] = isset($item['PicUrl']) ? $item['PicUrl'] : "";
            if ($it['title'] && $it['description'] && $it['url'] && $it['picurl'])
              $message['news']['articles'][] = $it;
          }
          else
            throw new Exception("item => array('Title'=>'','Description'=>'','PicUrl'=>'','Url'=>'')");
        }
      }

      return self::post(self::$links['message'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 创建分组
   *
   * @param $name
   *
   * @return bool|mixed
   */
  public function groupCreate($name) {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['name'] = $name;

      return self::post(self::$links['group_create'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 查询所有分组
   *
   * @return bool|mixed
   */
  public function groupGetAll() {
    if ($this->getAccessToken())
    {
      return self::get(self::$links['group_get'] . "?access_token={$this->access_token}");
    }

    return false;
  }

  /**
   * 查询用户所在分组
   *
   * @param $openid
   *
   * @return bool|mixed
   */
  public function groupGet($openid) {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['openid'] = $openid;

      return self::post(self::$links['group_getid'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 修改分组名
   *
   * @param $groupid
   * @param $name
   *
   * @return bool|mixed
   */
  public function groupRename($groupid, $name) {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['id'] = $groupid;
      $message['name'] = $name;

      return self::post(self::$links['group_rename'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 移动用户分组
   *
   * @param $openid
   * @param $to_groupid
   *
   * @return bool|mixed
   */
  public function groupMove($openid, $to_groupid) {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['openid'] = $openid;
      $message['to_groupid'] = $to_groupid;

      return self::post(self::$links['group_move'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 获取用户基本信息
   *
   * @param $openid
   *
   * @return bool|mixed
   */
  public function userInfo($openid) {
    if ($this->getAccessToken())
    {
      return self::get(self::$links['user_info'] . "?access_token={$this->access_token}&openid={$openid}");
    }

    return false;
  }

  /**
   * 获取关注者列表
   *
   * @param null $next_openid
   *
   * @return bool|mixed
   */
  public function userGet($next_openid = null) {
    if ($this->getAccessToken())
    {
      if ($next_openid)
        return self::get(self::$links['user_get'] . "?access_token={$this->access_token}&openid=");
      else
        return self::get(self::$links['user_get'] . "?access_token={$this->access_token}&next_openid={$next_openid}");
    }

    return false;
  }

  /**
   * 创建自定义菜单
   *
   * @param $menus
   *
   * @return bool|mixed
   */
  public function menuCreate($menus) {
    if ($this->getAccessToken())
    {
      $message = array();
      $message['button'] = $menus;

      return self::post(self::$links['menu_create'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 获取自定义菜单
   *
   * @return bool|mixed
   */
  public function menuGet() {
    if ($this->getAccessToken())
    {
      return self::get(self::$links['menu_get'] . "?access_token={$this->access_token}");
    }

    return false;
  }

  /**
   * 删除自定义菜单
   *
   * @return bool|mixed
   */
  public function menuDelete() {
    if ($this->getAccessToken())
    {
      return self::get(self::$links['menu_delete'] . "?access_token={$this->access_token}");
    }

    return false;
  }

  /**
   * 生成带参数的二维码,通过expire_seconds可以设置临时二维码或永久二维码
   *
   * @param     $sceneid
   * @param int $expire_seconds
   *
   * @return bool|mixed
   */
  public function qrcodeCreate($sceneid, $expire_seconds = 0) {
    if ($this->getAccessToken())
    {
      $message = array();
      if ($expire_seconds > 0)
      {
        if ($expire_seconds > 1800)
          return false;
        $message['expire_seconds'] = $expire_seconds;
        $message['action_name'] = 'QR_SCENE';
      }
      else
        $message['action_name'] = 'QR_LIMIT_SCENE';
      $message['action_info']['scene']['scene_id'] = $sceneid;

      return self::post(self::$links['qrcode'] . "?access_token={$this->access_token}", json_encode($message, JSON_UNESCAPED_UNICODE));
    }

    return false;
  }

  /**
   * 用ticket换取二维码图片,返回结果未图片,可以直接展示或者下载
   *
   * @param $ticket
   *
   * @return mixed
   */
  public static function qrcodeGet($ticket) {
    return self::get(self::$links['showqrcode'] . "?tocket=" . urlencode($ticket));
  }

  /**
   * 下载多媒体文件
   *
   * @param $mediaid
   *
   * @return mixed
   */
  public function mediaDownload($mediaid) {
    return self::get(self::$links['media_download'] . "?access_token={$this->access_token}&media_id=" . $mediaid);
  }

  /**
   * 上传多媒体文件
   * 图片(image): 128K,支持JPG格式
   * 语音(voice):256K,播放长度不超过60s,支持AMRMP3格式
   * 视频(video):1MB,支持MP4格式
   * 缩略图(thumb):64KB,支持JPG格式
   *
   * @param $filetype
   * @param $filepath
   *
   * @return bool|mixed
   */
  public function mediaUpload($filetype, $filepath) {
    if ($this->getAccessToken() && file_exists($filepath))
    {
      $fileext = strtolower(Tools::getFileExtension($filepath));
      $filesize = filesize($filepath);
      switch ($filetype)
      {
        case self::MEDIA_TYPE_IMAGE:
          if ($fileext != 'jpg' || $filesize > 128 * 1024)
            return false;
          break;
        case self::MEDIA_TYPE_VOICE:
          if (!in_array($fileext, array('amr', 'mp3')) || $filesize > 256 * 1024)
            return false;
          break;
        case self::MEDIA_TYPE_VIDEO:
          if ($fileext != 'mp4' || $filesize > 1024 * 1024)
            return false;
          break;
        case self::MEDIA_TYPE_THUMB:
          if ($fileext != 'jpg' || $filesize > 64 * 1024)
            return false;
          break;
        default:
          return false;
          break;
      }
      $media = array();
      $media['access_token'] = $this->access_token;
      $media['type'] = $filetype;
      $media['media'] = '@' . $filepath;

      return self::post(self::$links['media_upload'], $media);
    }

    return false;
  }

  /**
   * 第三方网页通过Oauth2.0获取用户授权
   * 获取code
   */
  public function oauthGetCode($redirect, $scope, $state) {
    return self::$links['oauth_code'] . "?appid={$this->appid}&redirect_uri={$redirect}&response_type=code&scope={$scope}&state={$state}#wechat_redirect";
  }

  /**
   * 第三方网页通过Oauth2.0获取用户授权
   * 通过code获取access_token
   */
  public function oauthGetAccessToken($code) {
    return self::get(self::$links['oauth_access_token'] . "?appid={$this->appid}&secret={$this->appsecret}&code={$code}&grant_type=authorization_code");
  }

  /**
   * 第三方网页通过Oauth2.0获取用户授权
   * 刷新access_token
   */
  public function oauthRefreshAccessToken($refresh_token) {
    return self::get(self::$links['oauth_refresh'] . "?appid={$this->appid}&grant_type=refresh_token&refresh_token={$refresh_token}");
  }

  /**
   * 第三方网页通过Oauth2.0获取用户权限
   * 获取用户信息,仅限scope为SCOPE_POP
   */
  public function oauthUserInfo($access_token, $openid) {
    return self::get(self::$links['oauth_userinfo'] . "?access_token={$access_token}&openid={$openid}");
  }

  /**
   * 返回js代码,隐藏微信中网页右上角按钮
   *
   * @return string
   */
  public static function hideOptionMenu() {
    return <<<EOF
<script type="text/javascript">
document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {
  WeixinJSBridge.call('hideOptionMenu');
});
</script>
EOF;
  }

  /**
   * 返回js代码,隐藏微信中网页底部导航栏
   *
   * @return string
   */
  public static function hideToolbar() {
    return <<<EOF
<script type="text/javascript">
document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {
  WeixinJSBridge.call('hideToolbar');
});
</script>
EOF;
  }

  /**
   * 返回js代码,网页获取用户网络状态
   *
   * @return string
   */
  public static function getNetworkType() {
    return <<<EOF
<script type="text/javascript">
WeixinJSBridge.invoke('getNetworkType',{},
  function(e){
    WeixinJSBridge.log(e.err_msg);
  }
);
</script>
EOF;
  }

  /**
   * 返回微信支付的JS代码
   */
  public static function getPayment($params,$account){
    global $_W;
    $wOpt['appId'] = $_W['account']['key'];
    $wOpt['timeStamp'] = TIMESTAMP;
    $wOpt['nonceStr'] = random(8);
    $package = array();
    $package['bank_type'] = 'WX';
    $package['body'] = $params['title'];
    $package['attach'] = $_W['weid'];
    $package['partner'] = $wechat['partner'];
    $package['out_trade_no'] = $params['tid'];
    $package['total_fee'] = $params['fee'] * 100;
    $package['fee_type'] = '1';
    $package['notify_url'] = $_W['siteroot'] . 'payment/wechat/notify.php';
    $package['spbill_create_ip'] = CLIENT_IP;
    $package['time_start'] = date('YmdHis', TIMESTAMP);
    $package['time_expire'] = date('YmdHis', TIMESTAMP + 600);
    $package['input_charset'] = 'UTF-8';
    ksort($package);
    $string1 = '';
    foreach ($package as $key => $v)
    {
      $string1 .= "{$key}={$v}&";
    }
    $string1 .= "key={$wechat['key']}";
    $sign = strtoupper(md5($string));

    $string2 = '';
    foreach ($package as $key => $v)
    {
      $v = urlencode($v);
      $string2 .= "{$key}={$v}&";
    }
    $string2 .= "sign={$sign}";

    $string = '';
    $keys = array('appId', 'timeStamp', 'nonceStr', 'package', 'appKey');
    sort($keys);
    foreach ($keys as $key)
    {
      $v = $wOpt[$key];
      if ($key == 'appKey')
      {
        $v = $wechat['signkey'];
      }
      $key = strtolower($key);
      $string .= "{$key}={$v}&";
    }
    $string = rtrim($string, '&');

    $wOpt['package'] = $string2;
    $wOpt['signType'] = 'SHA1';
    $wOpt['paySign'] = sha1($string);

    return $wOpt;
  }
}
log.php
<?php
class Log {
    private static $logpath = LOG_DIR;
    /**
     * 写入日志
     *
     * @param string $strFileName
     * @param string $strType
     * @param string $strMSG
     * @param string $strExtra
     * @param string $line
     */
    public static function out($strFileName = "", $strType = "I", $strMSG = "", $strExtra = "", $line = "") {
        if ($strType == "")
            $strType = "I";
        if (!file_exists(self::$logpath))
        {
            if (!mkdir(self::$logpath, '0777'))
            {
                if (DEBUG_MODE)
                {
                    die(Tools::displayError("Make " . self::$logpath . " error"));
                }
                else
                {
                    die("error");
                }
            }
        }
        elseif (!is_dir(self::$logpath))
        {
            if (DEBUG_MODE)
            {
                die(Tools::displayError(self::$logpath . " is already token by a file"));
            }
            else
            {
                die("error");
            }
        }
        else
        {
            if (!is_writable(self::$logpath))
            {
                @chmod(self::$logpath, 0777);
            }
            $logfile = rtrim(self::$logpath, '/') . '/' . $strFileName . '_' . date("ymd") . '.log';
            if (file_exists($logfile) && !is_writable($logfile))
            {
                @chmod($logfile, 0644);
            }
            $handle = @fopen($logfile, "a+");
            if ($handle)
            {
                if (Tools::isCli())
                {
                    $arg = "";
                    if ($_SERVER['argc'] > 0)
                    {
                        $arg = " ARGV:" . json_encode($_SERVER['argv']);
                    }
                    $strContent = "[" . date("Y-m-d H:i:s") . "] [" . strtoupper($strType) . "] [CLI] MSG:[" . $strMSG . "]" . $strExtra . " Location:" . $_SERVER["SCRIPT_FILENAME"] . $arg . ($line ? " Line:" . $line : "") . "
";
                }
                else
                    $strContent = "[" . date("Y-m-d H:i:s") . "] [" . strtoupper($strType) . "] [" . Tools::getRemoteAddr() . "] MSG:[" . $strMSG . "]" . $strExtra . " Location:" . $_SERVER["SCRIPT_FILENAME"] . ($line ? " Line:" . $line : "") . " QUERY_STRING:" . $_SERVER["QUERY_STRING"] . " HTTP_REFERER:" . (isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : "") . " User-Agent:" . $_SERVER["HTTP_USER_AGENT"] . "
";
                if (!fwrite($handle, $strContent))
                {
                    @fclose($handle);
                    die("Write permission deny");
                }
                @fclose($handle);
            }
        }
    }
    /**
     * 将$strMSG写入$strFileName文件,覆盖原来内容
     *
     * @param $strFileName
     * @param $strMSG
     */
    public static function simplewrite($strFileName, $strMSG) {
        if (!file_exists(self::$logpath))
        {
            if (!mkdir(self::$logpath, '0777'))
            {
                if (DEBUG_MODE)
                {
                    die(Tools::displayError("Make " . self::$logpath . " error"));
                }
                else
                {
                    die("error");
                }
            }
        }
        elseif (!is_dir(self::$logpath))
        {
            if (DEBUG_MODE)
            {
                die(Tools::displayError(self::$logpath . " is already token by a file"));
            }
            else
            {
                die("error");
            }
        }
        else
        {
            if (!is_writable(self::$logpath))
            {
                @chmod(self::$logpath, 0777);
            }
            $logfile = rtrim(self::$logpath, '/') . '/' . $strFileName . '.log';
            if (file_exists($logfile) && !is_writable($logfile))
            {
                @chmod($logfile, 0644);
            }
            $handle = @fopen($logfile, "w");
            if ($handle)
            {
                $strContent = $strMSG . "
";
                if (!fwrite($handle, $strContent))
                {
                    @fclose($handle);
                    die("Write permission deny");
                }
                @fclose($handle);
            }
        }
    }
    /**
     * 写入文件,追加方式
     *
     * @param $strFileName
     * @param $strMSG
     */
    public static function simpleappend($strFileName, $strMSG) {
        if (!file_exists(self::$logpath))
        {
            if (!mkdir(self::$logpath, '0777'))
            {
                if (DEBUG_MODE)
                {
                    die(Tools::displayError("Make " . self::$logpath . " error"));
                }
                else
                {
                    die("error");
                }
            }
        }
        elseif (!is_dir(self::$logpath))
        {
            if (DEBUG_MODE)
            {
                die(Tools::displayError(self::$logpath . " is already token by a file"));
            }
            else
            {
                die("error");
            }
        }
        else
        {
            if (!is_writable(self::$logpath))
            {
                @chmod(self::$logpath, 0777);
            }
            $logfile = rtrim(self::$logpath, '/') . '/' . $strFileName . '.log';
            if (file_exists($logfile) && !is_writable($logfile))
            {
                @chmod($logfile, 0644);
            }
            $handle = @fopen($logfile, "a");
            if ($handle)
            {
                $strContent = $strMSG . "
";
                if (!fwrite($handle, $strContent))
                {
                    @fclose($handle);
                    die("Write permission deny");
                }
                @fclose($handle);
            }
        }
    }
    /**
     * 读文件内容
     *
     * @param $strFileName
     *
     * @return bool|string
     */
    public static function simpleread($strFileName) {
        $logfile = trim(self::$logpath, '/') . '/' . $strFileName . '.log';
        if (file_exists($logfile) && is_readable($logfile))
        {
            $strContent = '';
            $handler = @fopen($logfile, 'r');
            if ($handler)
            {
                while (!feof($handler))
                {
                    $strContent .= fgets($handler);
                }
                @fclose($handler);
            }
            return $strContent;
        }
        return false;
    }
}
?>


 
原文地址:https://www.cnblogs.com/zuochuang/p/4685287.html