/**
*微信公众平台企业号PHP-SDK, 官方API类库
* @author binsee
* @link https://github.com/binsee/wechat-php-sdk
* @version 1.0
* usage:
* $options = array(
*'token'=>'tokenaccesskey', //填写应用接口的Token
*'encodingaeskey'=>'encodingaeskey', //填写加密用的EncodingAESKey
*'appid'=>'wxdk1234567890', //填写高级调用功能的app id
*'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥
*'agentid'=>'1', //应用的id
*'debug'=>false, //调试开关
*'_logcallback'=>'logg', //调试输出方法,需要有一个string类型的参数
*);
*
*/
class Wechat
{
const MSGTYPE_TEXT = 'text';
const MSGTYPE_IMAGE = 'image';
const MSGTYPE_LOCATION = 'location';
const MSGTYPE_LINK = 'link'; //暂不支持
const MSGTYPE_EVENT = 'event';
const MSGTYPE_MUSIC = 'music'; //暂不支持
const MSGTYPE_NEWS = 'news';
const MSGTYPE_VOICE = 'voice';
const MSGTYPE_VIDEO = 'video';
const API_URL_PREFIX = 'https://qyapi.weixin.qq.com/cgi-bin';
const USER_CREATE_URL = '/user/create?';
const USER_UPDATE_URL = '/user/update?';
const USER_DELETE_URL = '/user/delete?';
const USER_GET_URL = '/user/get?';
const USER_LIST_URL = '/user/simplelist?';
const USER_GETINFO_URL = '/user/getuserinfo?';
const DEPARTMENT_CREATE_URL = '/department/create?';
const DEPARTMENT_UPDATE_URL = '/department/update?';
const DEPARTMENT_DELETE_URL = '/department/delete?';
const DEPARTMENT_MOVE_URL = '/department/move?';
const DEPARTMENT_LIST_URL = '/department/list?';
const TAG_CREATE_URL = '/tag/create?';
const TAG_UPDATE_URL = '/tag/update?';
const TAG_DELETE_URL = '/tag/delete?';
const TAG_GET_URL = '/tag/get?';
const TAG_ADDUSER_URL = '/tag/addtagusers?';
const TAG_DELUSER_URL = '/tag/deltagusers?';
const TAG_LIST_URL = '/tag/list?';
const MEDIA_UPLOAD_URL = '/media/upload?';
const MEDIA_GET_URL = '/media/get?';
const AUTHSUCC_URL = '/user/authsucc?';
const MASS_SEND_URL = '/message/send?';
const MENU_CREATE_URL = '/menu/create?';
const MENU_GET_URL = '/menu/get?';
const MENU_DELETE_URL = '/menu/delete?';
const TOKEN_GET_URL = '/gettoken?';
const OAUTH_PREFIX = 'https://open.weixin.qq.com/connect/oauth2';
const OAUTH_AUTHORIZE_URL = '/authorize?';
private $token;
private $encodingAesKey;
private $appid; //也就是企业号的CorpID
private $appsecret;
private $access_token;
private $agentid; //应用id AgentID
private $postxml;
private $agentidxml; //接收的应用id AgentID
private $_msg;
private $_receive;
private $_sendmsg; //主动发送消息的内容
private $_text_filter = true;
public $debug = false;
public $errCode = 40001;
public $errMsg = "no access";
private $_logcallback;
public function __construct($options)
{
$this->token = isset($options['token'])?$options['token']:'';
$this->encodingAesKey = isset($options['encodingaeskey'])?$options['encodingaeskey']:'';
$this->appid = isset($options['appid'])?$options['appid']:'';
$this->appsecret = isset($options['appsecret'])?$options['appsecret']:'';
$this->agentid = isset($options['agentid'])?$options['agentid']:'';
$this->debug = isset($options['debug'])?$options['debug']:false;
$this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;
}
private function log($log){
if ($this->debug && function_exists($this->_logcallback)) {
if (is_array($log)) $log = print_r($log,true);
return call_user_func($this->_logcallback,$log);
}
}
/**
* 数据XML编码
* @param mixed $data 数据
* @return string
*/
public static function data_to_xml($data) {
$xml = '';
foreach ($data as $key => $val) {
is_numeric($key) && $key = "item id=\"$key\"";
$xml .= "";
$xml .= ( is_array($val) || is_object($val)) ? self::data_to_xml($val) : self::xmlSafeStr($val);
list($key, ) = explode(' ', $key);
$xml .= "$key>";
}
return $xml;
}
public static function xmlSafeStr($str)
{
return '';
}
/**
* XML编码
* @param mixed $data 数据
* @param string $root 根节点名
* @param string $item 数字索引的子节点名
* @param string $attr 根节点属性
* @param string $id 数字索引子节点key转换的属性名
* @param string $encoding 数据编码
* @return string
*/
public function xml_encode($data, $root='xml', $item='item', $attr='', $id='id', $encoding='utf-8') {
if(is_array($attr)){
$_attr = array();
foreach ($attr as $key => $value) {
$_attr[] = "{$key}=\"{$value}\"";
}
$attr = implode(' ', $_attr);
}
$attr = trim($attr);
$attr = empty($attr) ? '' : " {$attr}";
$xml = "";
$xml .= self::data_to_xml($data, $item, $id);
$xml .= "{$root}>";
return $xml;
}
/**
* 微信api不支持中文转义的json结构
* @param array $arr
*/
static function json_encode($arr) {
$parts = array ();
$is_list = false;
//Find out if the given array is a numerical array
$keys = array_keys ( $arr );
$max_length = count ( $arr ) - 1;
if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1
$is_list = true;
for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position
if ($i != $keys [$i]) { //A key fails at position check.
$is_list = false; //It is an associative array.
break;
}
}
}
foreach ( $arr as $key => $value ) {
if (is_array ( $value )) { //Custom handling for arrays
if ($is_list)
$parts [] = self::json_encode ( $value ); /* :RECURSION: */
else
$parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */
} else {
$str = '';
if (! $is_list)
$str = '"' . $key . '":';
//Custom handling for multiple data types
if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)
$str .= $value; //Numbers
elseif ($value === false)
$str .= 'false'; //The booleans
elseif ($value === true)
$str .= 'true';
else
$str .= '"' . addslashes ( $value ) . '"'; //All other things
// :TODO: Is there any more datatype we should be in the lookout for? (Object?)
$parts [] = $str;
}
}
$json = implode ( ',', $parts );
if ($is_list)
return '[' . $json . ']'; //Return numerical JSON
return '{' . $json . '}'; //Return associative JSON
}
/**
* 过滤文字回复\r\n换行符
* @param string $text
* @return string|mixed
*/
private function _auto_text_filter($text) {
if (!$this->_text_filter) return $text;
return str_replace("\r\n", "\n", $text);
}
/**
* GET 请求
* @param string $url
*/
private function http_get($url){
$oCurl = curl_init();
if(stripos($url,"https://")!==FALSE){
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
}
curl_setopt($oCurl, CURLOPT_URL, $url);
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
$sContent = curl_exec($oCurl);
$aStatus = curl_getinfo($oCurl);
curl_close($oCurl);
if(intval($aStatus["http_code"])==200){
return $sContent;
}else{
return false;
}
}
/**
* POST 请求
* @param string $url
* @param array $param
* @param boolean $post_file 是否文件上传
* @return string content
*/
private function http_post($url,$param,$post_file=false){
$oCurl = curl_init();
if(stripos($url,"https://")!==FALSE){
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
}
if (is_string($param) || $post_file) {
$strPOST = $param;
} else {
$aPOST = array();
foreach($param as $key=>$val){
$aPOST[] = $key."=".urlencode($val);
}
$strPOST = join("&", $aPOST);
}
curl_setopt($oCurl, CURLOPT_URL, $url);
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($oCurl, CURLOPT_POST,true);
curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);
$sContent = curl_exec($oCurl);
$aStatus = curl_getinfo($oCurl);
curl_close($oCurl);
if(intval($aStatus["http_code"])==200){
return $sContent;
}else{
return false;
}
}
/**
* For weixin server validation
*/
private function checkSignature($str)
{
$signature = isset($_GET["msg_signature"])?$_GET["msg_signature"]:'';
$timestamp = isset($_GET["timestamp"])?$_GET["timestamp"]:'';
$nonce = isset($_GET["nonce"])?$_GET["nonce"]:'';
$tmpArr = array($str,$this->token, $timestamp, $nonce);//比普通公众平台多了一个加密的密文
sort($tmpArr, SORT_STRING);
$tmpStr = implode($tmpArr);
$shaStr = sha1($tmpStr);
if( $shaStr == $signature ){
return true;
}else{
return false;
}
}
/**
* 微信验证,包括post来的xml解密
* @param bool $return 是否返回
*/
public function valid($return=false)
{
$encryptStr="";
if ($_SERVER['REQUEST_METHOD'] == "POST") {
$postStr = file_get_contents("php://input");
$array = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$this->log($postStr);
if (isset($array['Encrypt'])) {
$encryptStr = $array['Encrypt'];
$this->agentidxml = isset($array['AgentID']) ? $array['AgentID']: '';
}
} else {
$encryptStr = isset($_GET["echostr"]) ? $_GET["echostr"]: '';
}
if ($encryptStr) {
$ret=$this->checkSignature($encryptStr);
}
if (!isset($ret) || !$ret) {
if (!$return) {
die('no access');
} else {
return false;
}
}
$pc = new Prpcrypt($this->encodingAesKey);
$array = $pc->decrypt($encryptStr,$this->appid);
if (!isset($array[0]) || ($array[0] != 0)) {
if (!$return) {
die('解密失败!');
} else {
return false;
}
}
if ($_SERVER['REQUEST_METHOD'] == "POST") {
$this->postxml = $array[1];
//$this->log($array[1]);
return ($this->postxml!="");
} else {
$echoStr = $array[1];
if ($return) {
return $echoStr;
} else {
die($echoStr);
}
}
return false;
}
/**
* 获取微信服务器发来的信息
*/
public function getRev()
{
if ($this->_receive) return $this;
$postStr = $this->postxml;
$this->log($postStr);
if (!empty($postStr)) {
$this->_receive = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
if (!isset($this->_receive['AgentID'])) {
$this->_receive['AgentID']=$this->agentidxml; //当前接收消息的应用id
}
}
return $this;
}
/**
* 获取微信服务器发来的信息
*/
public function getRevData()
{
return $this->_receive;
}
/**
* 获取微信服务器发来的原始加密信息
*/
public function getRevPostXml()
{
return $this->postxml;
}
/**
* 获取消息发送者
*/
public function getRevFrom() {
if (isset($this->_receive['FromUserName']))
return $this->_receive['FromUserName'];
else
return false;
}
/**
* 获取消息接受者
*/
public function getRevTo() {
if (isset($this->_receive['ToUserName']))
return $this->_receive['ToUserName'];
else
return false;
}
/**
* 获取接收消息的应用id
*/
public function getRevAgentID() {
if (isset($this->_receive['AgentID']))
return $this->_receive['AgentID'];
else
return false;
}
/**
* 获取接收消息的类型
*/
public function getRevType() {
if (isset($this->_receive['MsgType']))
return $this->_receive['MsgType'];
else
return false;
}
/**
* 获取消息ID
*/
public function getRevID() {
if (isset($this->_receive['MsgId']))
return $this->_receive['MsgId'];
else
return false;
}
/**
* 获取消息发送时间
*/
public function getRevCtime() {
if (isset($this->_receive['CreateTime']))
return $this->_receive['CreateTime'];
else
return false;
}
/**
* 获取接收消息内容正文
*/
public function getRevContent(){
if (isset($this->_receive['Content']))
return $this->_receive['Content'];
else
return false;
}
/**
* 获取接收消息图片
*/
public function getRevPic(){
if (isset($this->_receive['PicUrl']))
return array(
'mediaid'=>$this->_receive['MediaId'],
'picurl'=>(string)$this->_receive['PicUrl'], //防止picurl为空导致解析出错
);
else
return false;
}
/**
* 获取接收地理位置
*/
public function getRevGeo(){
if (isset($this->_receive['Location_X'])){
return array(
'x'=>$this->_receive['Location_X'],
'y'=>$this->_receive['Location_Y'],
'scale'=>(string)$this->_receive['Scale'],
'label'=>(string)$this->_receive['Label']
);
} else
return false;
}
/**
* 获取上报地理位置事件
*/
public function getRevEventGeo(){
if (isset($this->_receive['Latitude'])){
return array(
'x'=>$this->_receive['Latitude'],
'y'=>$this->_receive['Longitude'],
'precision'=>$this->_receive['Precision'],
);
} else
return false;
}
/**
* 获取接收事件推送
*/
public function getRevEvent(){
if (isset($this->_receive['Event'])){
$array['event'] = $this->_receive['Event'];
}
if (isset($this->_receive['EventKey'])){
$array['key'] = $this->_receive['EventKey'];
}
if (isset($array) && count($array) > 0) {
return $array;
} else {
return false;
}
}
/**
* 获取自定义菜单的扫码推事件信息
*
* 事件类型为以下两种时则调用此方法有效
* Event 事件类型,scancode_push
* Event 事件类型,scancode_waitmsg
*
* @return: array | false
* array (
* 'ScanType'=>'qrcode',
* 'ScanResult'=>'123123'
* )
*/
public function getRevScanInfo(){
if (isset($this->_receive['ScanCodeInfo'])){
if (!is_array($this->_receive['SendPicsInfo'])) {
$array=(array)$this->_receive['ScanCodeInfo'];
$this->_receive['ScanCodeInfo']=$array;
}else {
$array=$this->_receive['ScanCodeInfo'];
}
}
if (isset($array) && count($array) > 0) {
return $array;
} else {
return false;
}
}
/**
* 获取自定义菜单的图片发送事件信息
*
* 事件类型为以下三种时则调用此方法有效
* Event 事件类型,pic_sysphoto 弹出系统拍照发图的事件推送
* Event 事件类型,pic_photo_or_album 弹出拍照或者相册发图的事件推送
* Event 事件类型,pic_weixin 弹出微信相册发图器的事件推送
*
* @return: array | false
* array (
* 'Count' => '2',
* 'PicList' =>array (
* 'item' =>array (
* 0 =>array ('PicMd5Sum' => 'aaae42617cf2a14342d96005af53624c'),
* 1 =>array ('PicMd5Sum' => '149bd39e296860a2adc2f1bb81616ff8'),
* ),
* ),
* )
*
*/
public function getRevSendPicsInfo(){
if (isset($this->_receive['SendPicsInfo'])){
if (!is_array($this->_receive['SendPicsInfo'])) {
$array=(array)$this->_receive['SendPicsInfo'];
if (isset($array['PicList'])){
$array['PicList']=(array)$array['PicList'];
$item=$array['PicList']['item'];
$array['PicList']['item']=array();
foreach ( $item as $key => $value ){
$array['PicList']['item'][$key]=(array)$value;
}
}
$this->_receive['SendPicsInfo']=$array;
} else {
$array=$this->_receive['SendPicsInfo'];
}
}
if (isset($array) && count($array) > 0) {
return $array;
} else {
return false;
}
}
/**
* 获取自定义菜单的地理位置选择器事件推送
*
* 事件类型为以下时则可以调用此方法有效
* Event 事件类型,location_select 弹出系统拍照发图的事件推送
*
* @return: array | false
* array (
* 'Location_X' => '33.731655000061',
* 'Location_Y' => '113.29955200008047',
* 'Scale' => '16',
* 'Label' => '某某市某某区某某路',
* 'Poiname' => '',
* )
*
*/
public function getRevSendGeoInfo(){
if (isset($this->_receive['SendLocationInfo'])){
if (!is_array($this->_receive['SendLocationInfo'])) {
$array=(array)$this->_receive['SendLocationInfo'];
if (empty($array['Poiname'])) {
$array['Poiname']="";
}
if (empty($array['Label'])) {
$array['Label']="";
}
$this->_receive['SendLocationInfo']=$array;
} else {
$array=$this->_receive['SendLocationInfo'];
}
}
if (isset($array) && count($array) > 0) {
return $array;
} else {
return false;
}
}
/**
* 获取接收语音推送
*/
public function getRevVoice(){
if (isset($this->_receive['MediaId'])){
return array(
'mediaid'=>$this->_receive['MediaId'],
'format'=>$this->_receive['Format'],
);
} else
return false;
}
/**
* 获取接收视频推送
*/
public function getRevVideo(){
if (isset($this->_receive['MediaId'])){
return array(
'mediaid'=>$this->_receive['MediaId'],
'thumbmediaid'=>$this->_receive['ThumbMediaId']
);
} else
return false;
}
/**
* 设置回复消息
* Example: $obj->text('hello')->reply();
* @param string $text
*/
public function text($text='')
{
$msg = array(
'ToUserName' => $this->getRevFrom(),
'FromUserName'=>$this->getRevTo(),
'MsgType'=>self::MSGTYPE_TEXT,
'Content'=>$this->_auto_text_filter($text),
'CreateTime'=>time(),
);
$this->Message($msg);
return $this;
}
/**
* 设置回复消息
* Example: $obj->image('media_id')->reply();
* @param string $mediaid
*/
public function image($mediaid='')
{
$msg = array(
'ToUserName' => $this->getRevFrom(),
'FromUserName'=>$this->getRevTo(),
'MsgType'=>self::MSGTYPE_IMAGE,
'Image'=>array('MediaId'=>$mediaid),
'CreateTime'=>time(),
);
$this->Message($msg);
return $this;
}
/**
* 设置回复消息
* Example: $obj->voice('media_id')->reply();
* @param string $mediaid
*/
public function voice($mediaid='')
{
$msg = array(
'ToUserName' => $this->getRevFrom(),
'FromUserName'=>$this->getRevTo(),
'MsgType'=>self::MSGTYPE_IMAGE,
'Voice'=>array('MediaId'=>$mediaid),
'CreateTime'=>time(),
);
$this->Message($msg);
return $this;
}
/**
* 设置回复消息
* Example: $obj->video('media_id','title','description')->reply();
* @param string $mediaid
*/
public function video($mediaid='',$title='',$description='')
{
$msg = array(
'ToUserName' => $this->getRevFrom(),
'FromUserName'=>$this->getRevTo(),
'MsgType'=>self::MSGTYPE_IMAGE,
'Video'=>array(
'MediaId'=>$mediaid,
'Title'=>$title,
'Description'=>$description
),
'CreateTime'=>time(),
);
$this->Message($msg);
return $this;
}
/**
* 设置回复图文
* @param array $newsData
* 数组结构:
* array(
* "0"=>array(
* 'Title'=>'msg title',
* 'Description'=>'summary text',
* 'PicUrl'=>'http://www.domain.com/1.jpg',
* 'Url'=>'http://www.domain.com/1.html'
* ),
* "1"=>....
* )
*/
public function news($newsData=array())
{
$count = count($newsData);
$msg = array(
'ToUserName' => $this->getRevFrom(),
'FromUserName'=>$this->getRevTo(),
'MsgType'=>self::MSGTYPE_NEWS,
'CreateTime'=>time(),
'ArticleCount'=>$count,
'Articles'=>$newsData,
);
$this->Message($msg);
return $this;
}
/**
* 设置发送消息
* @param array $msg 消息数组
* @param bool $append 是否在原消息数组追加
*/
public function Message($msg = '',$append = false){
if (is_null($msg)) {
$this->_msg =array();
}elseif (is_array($msg)) {
if ($append)
$this->_msg = array_merge($this->_msg,$msg);
else
$this->_msg = $msg;
return $this->_msg;
} else {
return $this->_msg;
}
}
/**
*
* 回复微信服务器, 此函数支持链式操作
* Example: $this->text('msg tips')->reply();
* @param string $msg 要发送的信息, 默认取$this->_msg
* @param bool $return 是否返回信息而不抛出到浏览器 默认:否
*/
public function reply($msg=array(),$return = false)
{
if (empty($msg))
$msg = $this->_msg;
$xmldata= $this->xml_encode($msg);
$this->log($xmldata);
$pc = new Prpcrypt($this->encodingAesKey);
$array = $pc->encrypt($xmldata, $this->appid);
$ret = $array[0];
if ($ret != 0) {
$this->log('encrypt err!');
return false;
}
$timestamp = time();
$nonce = rand(77,999)*rand(605,888)*rand(11,99);
$encrypt = $array[1];
$tmpArr = array($this->token, $timestamp, $nonce,$encrypt);//比普通公众平台多了一个加密的密文
sort($tmpArr, SORT_STRING);
$signature = implode($tmpArr);
$signature = sha1($signature);
$smsg = $this->generate($encrypt, $signature, $timestamp, $nonce);
$this->log($smsg);
if ($return)
return $smsg;
elseif ($smsg){
echo $smsg;
return true;
}else
return false;
}
private function generate($encrypt, $signature, $timestamp, $nonce)
{
//格式化加密信息
$format = "
%s
";
return sprintf($format, $encrypt, $signature, $timestamp, $nonce);
}
/**
* 通用auth验证方法
* @param string $appid
* @param string $appsecret
* @param string $token 手动指定access_token,非必要情况不建议用
*/
public function checkAuth($appid='',$appsecret='',$token=''){
if (!$appid || !$appsecret) {
$appid = $this->appid;
$appsecret = $this->appsecret;
}
if ($token) { //手动指定token,优先使用
$this->access_token=$token;
return $this->access_token;
}
//TODO: get the cache access_token
$result = $this->http_get(self::API_URL_PREFIX.self::TOKEN_GET_URL.'corpid='.$appid.'&corpsecret='.$appsecret);
if ($result)
{
$json = json_decode($result,true);
if (!$json || isset($json['errcode'])) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
$this->access_token = $json['access_token'];
$expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;
//TODO: cache access_token
return $this->access_token;
}
return false;
}
/**
* 删除验证数据
* @param string $appid
*/
public function resetAuth($appid=''){
if (!$appid) $appid = $this->appid;
$this->access_token = '';
//TODO: remove cache
return true;
}
/**
* 创建菜单
* @param array $data 菜单数组数据
* example:
* array (
* 'button' => array (
* 0 => array (
* 'name' => '扫码',
* 'sub_button' => array (
* 0 => array (
* 'type' => 'scancode_waitmsg',
* 'name' => '扫码带提示',
* 'key' => 'rselfmenu_0_0',
* ),
* 1 => array (
* 'type' => 'scancode_push',
* 'name' => '扫码推事件',
* 'key' => 'rselfmenu_0_1',
* ),
* ),
* ),
* 1 => array (
* 'name' => '发图',
* 'sub_button' => array (
* 0 => array (
* 'type' => 'pic_sysphoto',
* 'name' => '系统拍照发图',
* 'key' => 'rselfmenu_1_0',
* ),
* 1 => array (
* 'type' => 'pic_photo_or_album',
* 'name' => '拍照或者相册发图',
* 'key' => 'rselfmenu_1_1',
* )
* ),
* ),
* 2 => array (
* 'type' => 'location_select',
* 'name' => '发送位置',
* 'key' => 'rselfmenu_2_0'
* ),
* ),
* )
* type可以选择为以下几种,会收到相应类型的事件推送。请注意,3到8的所有事件,仅支持微信iPhone5.4.1以上版本,
* 和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回应,开发者也不能正常接收到事件推送。
* 1、click:点击推事件
* 2、view:跳转URL
* 3、scancode_push:扫码推事件
* 4、scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框
* 5、pic_sysphoto:弹出系统拍照发图
* 6、pic_photo_or_album:弹出拍照或者相册发图
* 7、pic_weixin:弹出微信相册发图器
* 8、location_select:弹出地理位置选择器
*/
public function createMenu($data,$agentid=''){
if ($agentid=='') {
$agentid=$this->agentid;
}
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::MENU_CREATE_URL.'access_token='.$this->access_token.'&agentid='.$agentid,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return true;
}
return false;
}
/**
* 获取菜单
* @return array('menu'=>array(....s))
*/
public function getMenu($agentid=''){
if ($agentid=='') {
$agentid=$this->agentid;
}
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::MENU_GET_URL.'access_token='.$this->access_token.'&agentid='.$agentid);
if ($result)
{
$json = json_decode($result,true);
if (!$json || isset($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 删除菜单
* @return boolean
*/
public function deleteMenu($agentid=''){
if ($agentid=='') {
$agentid=$this->agentid;
}
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::MENU_DELETE_URL.'access_token='.$this->access_token.'&agentid='.$agentid);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode'])) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return true;
}
return false;
}
/**
* 上传多媒体文件 (只有三天的有效期,过期自动被删除)
* 注意:数组的键值任意,但文件名前必须加@,使用单引号以避免本地路径斜杠被转义
* @param array $data {"media":'@Path\filename.jpg'}
* @param type 媒体文件类型:图片(image)、语音(voice)、视频(video),普通文件(file)
* @return boolean|array
* {
* "type": "image",
* "media_id": "0000001",
* "created_at": "1380000000"
* }
*/
public function uploadMedia($data, $type){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::MEDIA_UPLOAD.'access_token='.$this->access_token.'&type='.$type,$data,true);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode'])) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 根据媒体文件ID获取媒体文件
* @param string $media_id 媒体文件id
* @return raw data
*/
public function getMedia($media_id){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::MEDIA_GET_URL.'access_token='.$this->access_token.'&media_id='.$media_id);
if ($result)
{
$json = json_decode($result,true);
if (isset($json['errcode'])) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $result;
}
return false;
}
/**
* 创建部门
* @param array $data 结构体为:
* array (
* "name" => "邮箱产品组", //部门名称
* "parentid" => "1" //父部门id
* "order" => "1", //(非必须)在父部门中的次序。从1开始,数字越大排序越靠后
* )
* @return boolean|array
* 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "created", //对返回码的文本描述内容
* "id": 2 //创建的部门id。
* }
*/
public function createDepartment($data){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::DEPARTMENT_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 更新部门
* @param array $data 结构体为:
* array(
* "id" => "1" //(必须)部门id
* "name" => "邮箱产品组", //(非必须)部门名称
* "parentid" => "1", //(非必须)父亲部门id。根部门id为1
* "order" => "1", //(非必须)在父部门中的次序。从1开始,数字越大排序越靠后
* )
* @return boolean|array 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "updated" //对返回码的文本描述内容
* }
*/
public function updateDepartment($data){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::DEPARTMENT_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 删除部门
* @param $id
* @return boolean|array 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "deleted" //对返回码的文本描述内容
* }
*/
public function deleteDepartment($id){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::DEPARTMENT_DELETE_URL.'access_token='.$this->access_token.'&id='.$id);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 移动部门
* @param $data
* array(
* "department_id" => "5",//所要移动的部门
* "to_parentid" => "2",//想移动到的父部门节点,根部门为1
* "to_position" => "1"//(非必须)想移动到的父部门下的位置,1表示最上方,往后位置为2,3,4,以此类推,默认为1
* )
* @return boolean|array 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "ok" //对返回码的文本描述内容
* }
*/
public function moveDepartment($data){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::DEPARTMENT_MOVE_URL.'access_token='.$this->access_token,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 获取部门列表
* @return boolean|array 成功返回结果
* {
* "errcode": 0,
* "errmsg": "ok",
* "department": [ //部门列表数据。以部门的order字段从小到大排列
* {
* "id": 1,
* "name": "广州研发中心",
* "parentid": 0
* },
* {
* "id": 2
* "name": "邮箱产品部",
* "parentid": 1
* }
* ]
* }
*/
public function getDepartment(){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::DEPARTMENT_LIST_URL.'access_token='.$this->access_token);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode'])) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 创建成员
* @param array $data 结构体为:
* array(
* "userid" => "zhangsan",
* "name" => "张三",
* "department" => [1, 2],
* "position" => "产品经理",
* "mobile" => "15913215421",
* "gender" => 1, //性别。gender=0表示男,=1表示女
* "tel" => "62394",
* "email" => "zhangsan@gzdev.com",
* "weixinid" => "zhangsan4dev"
* )
* @return boolean|array
* 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "created", //对返回码的文本描述内容
* }
*/
public function createUser($data){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::USER_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 更新成员
* @param array $data 结构体为:
* array(
* "userid" => "zhangsan",
* "name" => "张三",
* "department" => [1, 2],
* "position" => "产品经理",
* "mobile" => "15913215421",
* "gender" => 1, //性别。gender=0表示男,=1表示女
* "tel" => "62394",
* "email" => "zhangsan@gzdev.com",
* "weixinid" => "zhangsan4dev"
* )
* @return boolean|array 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "updated" //对返回码的文本描述内容
* }
*/
public function updateUser($data){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::USER_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 删除成员
* @param $userid 员工UserID。对应管理端的帐号
* @return boolean|array 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "deleted" //对返回码的文本描述内容
* }
*/
public function deleteUser($userid){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::USER_DELETE_URL.'access_token='.$this->access_token.'&userid='.$userid);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 获取成员信息
* @param $userid 员工UserID。对应管理端的帐号
* @return boolean|array 成功返回结果
* {
* "errcode": 0,
* "errmsg": "ok",
* "userid": "zhangsan",
* "name": "李四",
* "department": [1, 2],
* "position": "后台工程师",
* "mobile": "15913215421",
* "gender": 1, //性别。gender=0表示男,=1表示女
* "tel": "62394",
* "email": "zhangsan@gzdev.com",
* "weixinid": "lisifordev", //微信号
* "avatar": "http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3W..../0", //头像url。注:如果要获取小图将url最后的"/0"改成"/64"即可
* "status": 1 //关注状态: 1=已关注,2=已冻结,4=未关注
* }
*/
public function getUserInfo($userid){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::USER_GET_URL.'access_token='.$this->access_token.'&userid='.$userid);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 获取部门成员
* @param $department_id 部门id
* @param $fetch_child 1/0:是否递归获取子部门下面的成员
* @param $status 0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加
* @return boolean|array 成功返回结果
* {
* "errcode": 0,
* "errmsg": "ok",
* "userlist": [
* {
* "userid": "zhangsan",
* "name": "李四"
* }
* ]
* }
*/
public function getUserList($department_id,$fetch_child=0,$status=0){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::USER_LIST_URL.'access_token='.$this->access_token
.'&department_id='.$department_id.'&fetch_child='.$fetch_child.'&status='.$status);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 根据code获取成员信息
* 通过Oauth2.0或者设置了二次验证时获取的code,用于换取成员的UserId和DeviceId
*
* @param $code Oauth2.0或者二次验证时返回的code值
* @param $agentid 跳转链接时所在的企业应用ID,未填则默认为当前配置的应用id
* @return boolean|array 成功返回数组
* array(
* 'UserId' => 'USERID', //员工UserID
* 'DeviceId' => 'DEVICEID' //手机设备号(由微信在安装时随机生成)
* )
*/
public function getUserId($code,$agentid=0){
if (!$agentid) $agentid=$this->agentid;
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::USER_GETINFO_URL.'access_token='.$this->access_token.'&code='.$code.'&agentid'.$agentid);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 创建标签
* @param array $data 结构体为:
* array(
* "tagname" => "UI"
* )
* @return boolean|array
* 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "created", //对返回码的文本描述内容
* "tagid": "1"
* }
*/
public function createTag($data){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::TAG_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 更新标签
* @param array $data 结构体为:
* array(
* "tagid" => "1",
* "tagname" => "UI design"
* )
* @return boolean|array 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "updated" //对返回码的文本描述内容
* }
*/
public function updateTag($data){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::TAG_UPDATE_URL.'access_token='.$this->access_token,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 删除标签
* @param $tagid 标签TagID
* @return boolean|array 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "deleted" //对返回码的文本描述内容
* }
*/
public function deleteTag($tagid){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::TAG_DELETE_URL.'access_token='.$this->access_token.'&tagid='.$tagid);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 获取标签成员
* @param $tagid 标签TagID
* @return boolean|array 成功返回结果
* {
* "errcode": 0,
* "errmsg": "ok",
* "userlist": [
* {
* "userid": "zhangsan",
* "name": "李四"
* }
* ]
* }
*/
public function getTag($tagid){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::TAG_GET_URL.'access_token='.$this->access_token.'&tagid='.$tagid);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 增加标签成员
* @param array $data 结构体为:
* array (
* "tagid" => "1",
* "userlist" => array( //企业员工ID列表
* "user1",
* "user2"
* )
* )
* @return boolean|array
* 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "ok", //对返回码的文本描述内容
* "invalidlist":"usr1|usr2|usr" //若部分userid非法,则会有此段。不在权限内的员工ID列表,以“|”分隔
* }
*/
public function addTagUser($data){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::TAG_ADDUSER_URL.'access_token='.$this->access_token,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 删除标签成员
* @param array $data 结构体为:
* array (
* "tagid" => "1",
* "userlist" => array( //企业员工ID列表
* "user1",
* "user2"
* )
* )
* @return boolean|array
* 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "deleted", //对返回码的文本描述内容
* "invalidlist":"usr1|usr2|usr" //若部分userid非法,则会有此段。不在权限内的员工ID列表,以“|”分隔
* }
*/
public function delTagUser($data){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::TAG_DELUSER_URL.'access_token='.$this->access_token,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 获取标签列表
* @return boolean|array 成功返回数组结果,这里附上json样例
* {
* "errcode": 0,
* "errmsg": "ok",
* "taglist":[
* {"tagid":1,"tagname":"a"},
* {"tagid":2,"tagname":"b"}
* ]
* }
*/
public function getTagList(){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::TAG_LIST_URL.'access_token='.$this->access_token);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode'])) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 主动发送信息接口
* @param array $data 结构体为:
* array(
* "touser" => "UserID1|UserID2|UserID3",
* "toparty" => "PartyID1|PartyID2 ",
* "totag" => "TagID1|TagID2 ",
* "safe":"0"//是否为保密消息,对于news无效
* "agentid" => "001",//应用id
* "msgtype" => "text", //根据信息类型,选择下面对应的信息结构体
*
* "text" => array(
* "content" => "Holiday Request For Pony(http://xxxxx)"
* ),
*
* "image" => array(
* "media_id" => "MEDIA_ID"
* ),
*
* "voice" => array(
* "media_id" => "MEDIA_ID"
* ),
*
* " video" => array(
* "media_id" => "MEDIA_ID",
* "title" => "Title",
* "description" => "Description"
* ),
*
* "file" => array(
* "media_id" => "MEDIA_ID"
* ),
*
* "news" => array(//不支持保密
* "articles" => array( //articles 图文消息,一个图文消息支持1到10个图文
* array(
* "title" => "Title", //标题
* "description" => "Description", //描述
* "url" => "URL", //点击后跳转的链接。可根据url里面带的code参数校验员工的真实身份。
* "picurl" => "PIC_URL", //图文消息的图片链接,支持JPG、PNG格式,较好的效果为大图640*320,
* //小图80*80。如不填,在客户端不显示图片
* ),
* )
* ),
*
* "mpnews" => array(
* "articles" => array( //articles 图文消息,一个图文消息支持1到10个图文
* array(
* "title" => "Title", //图文消息的标题
* "thumb_media_id" => "id", //图文消息缩略图的media_id
* "author" => "Author", //图文消息的作者(可空)
* "content_source_url" => "URL", //图文消息点击“阅读原文”之后的页面链接(可空)
* "content" => "Content" //图文消息的内容,支持html标签
* "digest" => "Digest description", //图文消息的描述
* "show_cover_pic" => "0" //是否显示封面,1为显示,0为不显示(可空)
* ),
* )
* )
* )
* 请查看官方开发文档中的 发送消息 -> 消息类型及数据格式
*
* @return boolean|array
* 如果对应用或收件人、部门、标签任何一个无权限,则本次发送失败;
* 如果收件人、部门或标签不存在,发送仍然执行,但返回无效的部分。
* {
* "errcode": 0,
* "errmsg": "ok",
* "invaliduser": "UserID1",
* "invalidparty":"PartyID1",
* "invalidtag":"TagID1"
* }
*/
public function sendMessage($data){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_post(self::API_URL_PREFIX.self::MASS_SEND_URL.'access_token='.$this->access_token,self::json_encode($data));
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* 二次验证
* 企业在开启二次验证时,必须填写企业二次验证页面的url。
* 当员工绑定通讯录中的帐号后,会收到一条图文消息,
* 引导员工到企业的验证页面验证身份,企业在员工验证成功后,
* 调用如下接口即可让员工关注成功。
*
* @param $userid
* @return boolean|array 成功返回结果
* {
* "errcode": 0, //返回码
* "errmsg": "ok" //对返回码的文本描述内容
* }
*/
public function authSucc($userid){
if (!$this->access_token && !$this->checkAuth()) return false;
$result = $this->http_get(self::API_URL_PREFIX.self::AUTHSUCC_URL.'access_token='.$this->access_token.'&userid='.$userid);
if ($result)
{
$json = json_decode($result,true);
if (!$json || !empty($json['errcode']) || $json['errcode']!=0) {
$this->errCode = $json['errcode'];
$this->errMsg = $json['errmsg'];
return false;
}
return $json;
}
return false;
}
/**
* oauth 授权跳转接口
* @param string $callback 回调URI
* @param string $state 重定向后会带上state参数,企业可以填写a-zA-Z0-9的参数值
* @return string
*/
public function getOauthRedirect($callback,$state='STATE',$scope='snsapi_base'){
return self::OAUTH_PREFIX.self::OAUTH_AUTHORIZE_URL.'appid='.$this->appid.'&redirect_uri='.urlencode($callback).'&response_type=code&scope='.$scope.'&state='.$state.'#wechat_redirect';
}
}
/**
* PKCS7Encoder class
*
* 提供基于PKCS7算法的加解密接口.
*/
class PKCS7Encoder
{
public static $block_size = 32;
/**
* 对需要加密的明文进行填充补位
* @param $text 需要进行填充补位操作的明文
* @return 补齐明文字符串
*/
function encode($text)
{
$block_size = PKCS7Encoder::$block_size;
$text_length = strlen($text);
//计算需要填充的位数
$amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size);
if ($amount_to_pad == 0) {
$amount_to_pad = PKCS7Encoder::block_size;
}
//获得补位所用的字符
$pad_chr = chr($amount_to_pad);
$tmp = "";
for ($index = 0; $index < $amount_to_pad; $index++) {
$tmp .= $pad_chr;
}
return $text . $tmp;
}
/**
* 对解密后的明文进行补位删除
* @param decrypted 解密后的明文
* @return 删除填充补位后的明文
*/
function decode($text)
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > PKCS7Encoder::$block_size) {
$pad = 0;
}
return substr($text, 0, (strlen($text) - $pad));
}
}
/**
* Prpcrypt class
*
* 提供接收和推送给公众平台消息的加解密接口.
*/
class Prpcrypt
{
public $key;
function Prpcrypt($k)
{
$this->key = base64_decode($k . "=");
}
/**
* 对明文进行加密
* @param string $text 需要加密的明文
* @return string 加密后的密文
*/
public function encrypt($text, $appid)
{
try {
//获得16位随机字符串,填充到明文之前
$random = $this->getRandomStr();//"aaaabbbbccccdddd";
$text = $random . pack("N", strlen($text)) . $text . $appid;
// 网络字节序
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = substr($this->key, 0, 16);
//使用自定义的填充方式对明文进行补位填充
$pkc_encoder = new PKCS7Encoder;
$text = $pkc_encoder->encode($text);
mcrypt_generic_init($module, $this->key, $iv);
//加密
$encrypted = mcrypt_generic($module, $text);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
//print(base64_encode($encrypted));
//使用BASE64对加密后的字符串进行编码
return array(ErrorCode::$OK, base64_encode($encrypted));
} catch (Exception $e) {
//print $e;
return array(ErrorCode::$EncryptAESError, null);
}
}
/**
* 对密文进行解密
* @param string $encrypted 需要解密的密文
* @return string 解密得到的明文
*/
public function decrypt($encrypted, $appid)
{
try {
//使用BASE64对需要解密的字符串进行解码
$ciphertext_dec = base64_decode($encrypted);
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = substr($this->key, 0, 16);
mcrypt_generic_init($module, $this->key, $iv);
//解密
$decrypted = mdecrypt_generic($module, $ciphertext_dec);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
} catch (Exception $e) {
return array(ErrorCode::$DecryptAESError, null);
}
try {
//去除补位字符
$pkc_encoder = new PKCS7Encoder;
$result = $pkc_encoder->decode($decrypted);
//去除16位随机字符串,网络字节序和AppId
if (strlen($result) < 16)
return "";
$content = substr($result, 16, strlen($result));
$len_list = unpack("N", substr($content, 0, 4));
$xml_len = $len_list[1];
$xml_content = substr($content, 4, $xml_len);
$from_appid = substr($content, $xml_len + 4);
} catch (Exception $e) {
//print $e;
return array(ErrorCode::$IllegalBuffer, null);
}
if ($from_appid != $appid)
return array(ErrorCode::$ValidateAppidError, null);
return array(0, $xml_content);
}
/**
* 随机生成16位字符串
* @return string 生成的字符串
*/
function getRandomStr()
{
$str = "";
$str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
$max = strlen($str_pol) - 1;
for ($i = 0; $i < 16; $i++) {
$str .= $str_pol[mt_rand(0, $max)];
}
return $str;
}
}
/**
* error code
* 仅用作类内部使用,不用于官方API接口的errCode码
*/
class ErrorCode
{
public static $OK = 0;
public static $ValidateSignatureError = 40001;
public static $ParseXmlError = 40002;
public static $ComputeSignatureError = 40003;
public static $IllegalAesKey = 40004;
public static $ValidateAppidError = 40005;
public static $EncryptAESError = 40006;
public static $DecryptAESError = 40007;
public static $IllegalBuffer = 40008;
public static $EncodeBase64Error = 40009;
public static $DecodeBase64Error = 40010;
public static $GenReturnXmlError = 40011;
public static $errCode=array(
'0'=>'无问题',
'40001'=>'签名验证错误',
'40002'=>'xml解析失败',
'40003'=>'sha加密生成签名失败',
'40004'=>'encodingAesKey 非法',
'40005'=>'appid 校验错误',
'40006'=>'aes 加密失败',
'40007'=>'aes 解密失败',
'40008'=>'解密后得到的buffer非法',
'40009'=>'base64加密失败',
'40010'=>'base64解密失败',
'40011'=>'生成xml失败',
);
public static function getErrText($err) {
if (isset(self::$errCode[$err])) {
return self::$errCode[$err];
}else {
return false;
};
}
}