mirror of
https://gitee.com/zoujingli/WeChatDeveloper.git
synced 2025-04-06 03:58:03 +08:00
[更新]完善微信消息通知处理
This commit is contained in:
parent
c531c74a65
commit
7cc1736e90
190
Wechat/Contracts/Prpcrypt.php
Normal file
190
Wechat/Contracts/Prpcrypt.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | WeChatDeveloper
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: http://think.ctolog.com
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// +----------------------------------------------------------------------
|
||||
// | github开源项目:https://github.com/zoujingli/WeChatDeveloper
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* PKCS7算法 - 加解密
|
||||
* Class PKCS7Encoder
|
||||
*/
|
||||
class PKCS7Encoder
|
||||
{
|
||||
|
||||
public static $block_size = 32;
|
||||
|
||||
/**
|
||||
* 对需要加密的明文进行填充补位
|
||||
* @param string $text 需要进行填充补位操作的明文
|
||||
* @return string 补齐明文字符串
|
||||
*/
|
||||
function encode($text)
|
||||
{
|
||||
$amount_to_pad = PKCS7Encoder::$block_size - (strlen($text) % PKCS7Encoder::$block_size);
|
||||
if ($amount_to_pad == 0) {
|
||||
$amount_to_pad = PKCS7Encoder::$block_size;
|
||||
}
|
||||
list($pad_chr, $tmp) = [chr($amount_to_pad), ''];
|
||||
for ($index = 0; $index < $amount_to_pad; $index++) {
|
||||
$tmp .= $pad_chr;
|
||||
}
|
||||
return $text . $tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对解密后的明文进行补位删除
|
||||
* @param string $text 解密后的明文
|
||||
* @return string 删除填充补位后的明文
|
||||
*/
|
||||
function decode($text)
|
||||
{
|
||||
$pad = ord(substr($text, -1));
|
||||
if ($pad < 1 || $pad > PKCS7Encoder::$block_size) {
|
||||
$pad = 0;
|
||||
}
|
||||
return substr($text, 0, (strlen($text) - $pad));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 公众号消息 - 加解密
|
||||
* Class Prpcrypt
|
||||
*/
|
||||
class Prpcrypt
|
||||
{
|
||||
|
||||
public $key;
|
||||
|
||||
/**
|
||||
* Prpcrypt constructor.
|
||||
* @param $key
|
||||
*/
|
||||
function __construct($key)
|
||||
{
|
||||
$this->key = base64_decode("{$key}=");
|
||||
}
|
||||
|
||||
/**
|
||||
* 对明文进行加密
|
||||
* @param string $text 需要加密的明文
|
||||
* @param string $appid 公众号APPID
|
||||
* @return array
|
||||
*/
|
||||
public function encrypt($text, $appid)
|
||||
{
|
||||
try {
|
||||
$random = $this->getRandomStr();
|
||||
$iv = substr($this->key, 0, 16);
|
||||
$pkcEncoder = new PKCS7Encoder();
|
||||
$text = $pkcEncoder->encode($random . pack("N", strlen($text)) . $text . $appid);
|
||||
$encrypted = openssl_encrypt($text, 'AES-256-CBC', substr($this->key, 0, 32), OPENSSL_ZERO_PADDING, $iv);
|
||||
return [ErrorCode::$OK, $encrypted];
|
||||
} catch (Exception $e) {
|
||||
return [ErrorCode::$EncryptAESError, null];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对密文进行解密
|
||||
* @param string $encrypted 需要解密的密文
|
||||
* @return array
|
||||
*/
|
||||
public function decrypt($encrypted)
|
||||
{
|
||||
try {
|
||||
$iv = substr($this->key, 0, 16);
|
||||
$decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', substr($this->key, 0, 32), OPENSSL_ZERO_PADDING, $iv);
|
||||
} catch (Exception $e) {
|
||||
return [ErrorCode::$DecryptAESError, null];
|
||||
}
|
||||
try {
|
||||
$pkcEncoder = new PKCS7Encoder();
|
||||
$result = $pkcEncoder->decode($decrypted);
|
||||
if (strlen($result) < 16) {
|
||||
return [ErrorCode::$DecryptAESError, null];
|
||||
}
|
||||
$content = substr($result, 16, strlen($result));
|
||||
$len_list = unpack("N", substr($content, 0, 4));
|
||||
$xml_len = $len_list[1];
|
||||
return [0, substr($content, 4, $xml_len), substr($content, $xml_len + 4)];
|
||||
} catch (Exception $e) {
|
||||
return [ErrorCode::$IllegalBuffer, null];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成16位字符串
|
||||
* @param string $str
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅用作类内部使用
|
||||
* 不用于官方API接口的errCode码
|
||||
* Class ErrorCode
|
||||
*/
|
||||
class ErrorCode
|
||||
{
|
||||
|
||||
public static $OK = 0;
|
||||
public static $ParseXmlError = 40002;
|
||||
public static $IllegalAesKey = 40004;
|
||||
public static $IllegalBuffer = 40008;
|
||||
public static $EncryptAESError = 40006;
|
||||
public static $DecryptAESError = 40007;
|
||||
public static $EncodeBase64Error = 40009;
|
||||
public static $DecodeBase64Error = 40010;
|
||||
public static $GenReturnXmlError = 40011;
|
||||
public static $ValidateAppidError = 40005;
|
||||
public static $ComputeSignatureError = 40003;
|
||||
public static $ValidateSignatureError = 40001;
|
||||
public static $errCode = [
|
||||
'0' => '处理成功',
|
||||
'40001' => '校验签名失败',
|
||||
'40002' => '解析xml失败',
|
||||
'40003' => '计算签名失败',
|
||||
'40004' => '不合法的AESKey',
|
||||
'40005' => '校验AppID失败',
|
||||
'40006' => 'AES加密失败',
|
||||
'40007' => 'AES解密失败',
|
||||
'40008' => '公众平台发送的xml不合法',
|
||||
'40009' => 'Base64编码失败',
|
||||
'40010' => 'Base64解码失败',
|
||||
'40011' => '公众帐号生成回包xml失败',
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取错误消息内容
|
||||
* @param string $err
|
||||
* @return bool
|
||||
*/
|
||||
public static function getErrText($err)
|
||||
{
|
||||
if (isset(self::$errCode[$err])) {
|
||||
return self::$errCode[$err];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -15,303 +15,213 @@
|
||||
namespace Wechat\Contracts;
|
||||
|
||||
use Wechat\Exceptions\InvalidArgumentException;
|
||||
use Wechat\Exceptions\InvalidDecryptException;
|
||||
use Wechat\Exceptions\InvalidResponseException;
|
||||
use Wechat\Exceptions\LocalCacheException;
|
||||
|
||||
/**
|
||||
* 网络请求支持
|
||||
* 微信通知处理基本类
|
||||
* Class Request
|
||||
* @package Wechat
|
||||
* @package Wechat\Contracts
|
||||
*/
|
||||
class Request
|
||||
{
|
||||
/**
|
||||
* 缓存路径
|
||||
* @var null
|
||||
* 公众号APPID
|
||||
* @var string
|
||||
*/
|
||||
public static $cache_path = null;
|
||||
protected $appid;
|
||||
|
||||
/**
|
||||
* 根据文件后缀获取文件MINE
|
||||
* @param array $ext 文件后缀
|
||||
* @param array $mine 文件后缀MINE信息
|
||||
* @return string
|
||||
* @throws LocalCacheException
|
||||
* 公众号推送XML内容
|
||||
* @var string
|
||||
*/
|
||||
public static function getExtMine($ext, $mine = [])
|
||||
{
|
||||
$mines = self::getMines();
|
||||
foreach (is_string($ext) ? explode(',', $ext) : $ext as $e) {
|
||||
$mine[] = isset($mines[strtolower($e)]) ? $mines[strtolower($e)] : 'application/octet-stream';
|
||||
}
|
||||
return join(',', array_unique($mine));
|
||||
}
|
||||
protected $postxml;
|
||||
|
||||
/**
|
||||
* 获取所有文件扩展的mine
|
||||
* @return array
|
||||
* @throws LocalCacheException
|
||||
* 公众号推送加密类型
|
||||
* @var string
|
||||
*/
|
||||
private static function getMines()
|
||||
{
|
||||
$mines = self::getCache('all_ext_mine');
|
||||
if (empty($mines)) {
|
||||
$content = file_get_contents('http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types');
|
||||
preg_match_all('#^([^\s]{2,}?)\s+(.+?)$#ism', $content, $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $match) {
|
||||
foreach (explode(" ", $match[2]) as $ext) {
|
||||
$mines[$ext] = $match[1];
|
||||
}
|
||||
}
|
||||
self::setCache('all_ext_mine', $mines);
|
||||
}
|
||||
return $mines;
|
||||
}
|
||||
protected $encryptType;
|
||||
|
||||
/**
|
||||
* 创建CURL文件对象
|
||||
* @param $filename
|
||||
* @param string $mimetype
|
||||
* @param string $postname
|
||||
* @return \CURLFile|string
|
||||
* @throws LocalCacheException
|
||||
* 当前公众号配置对象
|
||||
* @var Config
|
||||
*/
|
||||
public static function createCurlFile($filename, $mimetype = '', $postname = '')
|
||||
{
|
||||
$basename = $postname ?: basename($filename);
|
||||
$basemine = $mimetype ?: self::getExtMine(pathinfo($filename, 4));
|
||||
if (function_exists('curl_file_create')) {
|
||||
return curl_file_create($filename, $basemine, $basename);
|
||||
}
|
||||
return "@{$filename};filename={$basename};type={$basemine}";
|
||||
}
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* 以get访问模拟访问
|
||||
* @param string $url 访问URL
|
||||
* @param array $query GET数
|
||||
* 公众号的推送请求参数
|
||||
* @var Config
|
||||
*/
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* 公众号推送内容对象
|
||||
* @var Config
|
||||
*/
|
||||
protected $receive;
|
||||
|
||||
/**
|
||||
* 准备回复的消息内容
|
||||
* @var array
|
||||
*/
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* Request constructor.
|
||||
* @param array $options
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function get($url, $query = [], $options = [])
|
||||
{
|
||||
$options['query'] = $query;
|
||||
return self::doRequest('get', $url, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以post访问模拟访问
|
||||
* @param string $url 访问URL
|
||||
* @param array $data POST数据
|
||||
* @param array $options
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function post($url, $data = [], $options = [])
|
||||
{
|
||||
$options['data'] = $data;
|
||||
return self::doRequest('post', $url, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转XML内容
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public static function toXml($data)
|
||||
{
|
||||
return "<xml>" . self::_data_to_xml($data) . "</xml>";
|
||||
}
|
||||
|
||||
/**
|
||||
* XML内容生成
|
||||
* @param array $data 数据
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
private static function _data_to_xml($data, $content = '')
|
||||
{
|
||||
foreach ($data as $key => $val) {
|
||||
$content .= "<{$key}>";
|
||||
if (is_array($val) || is_object($val)) {
|
||||
$content .= self::_data_to_xml($val);
|
||||
} elseif (is_string($val)) {
|
||||
$content .= '<![CDATA[' . preg_replace("/[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]/", '', $val) . ']]>';
|
||||
} else {
|
||||
$content .= $val;
|
||||
}
|
||||
$content .= "</{$key}>";
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析XML内容到数组
|
||||
* @param string $xml
|
||||
* @return array
|
||||
*/
|
||||
public static function fromXml($xml)
|
||||
{
|
||||
return json_decode(self::toJson(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转xml内容
|
||||
* @param array $data
|
||||
* @return null|string|string
|
||||
*/
|
||||
public static function toJson($data)
|
||||
{
|
||||
return preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function ($matches) {
|
||||
return mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE");
|
||||
}, json_encode($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析JSON内容到数组
|
||||
* @param string $json
|
||||
* @return array
|
||||
* @throws InvalidResponseException
|
||||
*/
|
||||
public static function fromJson($json)
|
||||
public function __construct(array $options)
|
||||
{
|
||||
$result = json_decode($json, true);
|
||||
if (empty($result)) {
|
||||
throw new InvalidResponseException('invalid response.', '0');
|
||||
if (empty($options['appid'])) {
|
||||
throw new InvalidArgumentException("Missing Config -- [appid]");
|
||||
}
|
||||
if (!empty($result['errcode'])) {
|
||||
throw new InvalidResponseException($result['errmsg'], $result['errcode'], $result);
|
||||
if (empty($options['appsecret'])) {
|
||||
throw new InvalidArgumentException("Missing Config -- [appsecret]");
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* CURL模拟网络请求
|
||||
* @param string $method 请求方法
|
||||
* @param string $url 请求方法
|
||||
* @param array $options 请求参数[headers,data,ssl_cer,ssl_key]
|
||||
* @return bool|string
|
||||
*/
|
||||
protected static function doRequest($method, $url, $options = [])
|
||||
{
|
||||
$curl = curl_init();
|
||||
// GET参数设置
|
||||
if (!empty($options['query'])) {
|
||||
$url .= (stripos($url, '?') !== false ? '&' : '?') . http_build_query($options['query']);
|
||||
if (empty($options['token'])) {
|
||||
throw new InvalidArgumentException("Missing Config -- [token]");
|
||||
}
|
||||
// POST数据设置
|
||||
if (strtolower($method) === 'post') {
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, self::build($options['data']));
|
||||
}
|
||||
// CURL头信息设置
|
||||
if (!empty($options['headers'])) {
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $options['headers']);
|
||||
}
|
||||
// 证书文件设置
|
||||
if (!empty($options['ssl_cer'])) {
|
||||
if (file_exists($options['ssl_cer'])) {
|
||||
curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
|
||||
curl_setopt($curl, CURLOPT_SSLCERT, $options['ssl_cer']);
|
||||
} else {
|
||||
throw new InvalidArgumentException("Certificate files that do not exist. --- [{$options['ssl_cer']}]");
|
||||
}
|
||||
}
|
||||
// 证书文件设置
|
||||
if (!empty($options['ssl_key'])) {
|
||||
if (file_exists($options['ssl_key'])) {
|
||||
curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
|
||||
curl_setopt($curl, CURLOPT_SSLKEY, $options['ssl_key']);
|
||||
} else {
|
||||
throw new InvalidArgumentException("Certificate files that do not exist. --- [{$options['ssl_key']}]");
|
||||
}
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
|
||||
curl_setopt($curl, CURLOPT_HEADER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
list($content, $status) = [curl_exec($curl), curl_getinfo($curl), curl_close($curl)];
|
||||
return (intval($status["http_code"]) === 200) ? $content : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* POST数据过滤处理
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
private static function build($data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_string($value) && class_exists('CURLFile', false) && stripos($value, '@') === 0) {
|
||||
$filename = realpath(trim($value, '@'));
|
||||
if ($filename && file_exists($filename)) {
|
||||
$data[$key] = new \CURLFile($filename);
|
||||
}
|
||||
// 参数初始化
|
||||
$this->config = new Config($options);
|
||||
$this->params = new Config($_REQUEST);
|
||||
$this->appid = $this->config->get('appid');
|
||||
// 推送消息处理
|
||||
if ($_SERVER['REQUEST_METHOD'] == "POST") {
|
||||
$this->postxml = file_get_contents("php://input");
|
||||
$this->encryptType = $this->params->get('encrypt_type');
|
||||
if ($this->encryptType == 'aes') {
|
||||
if (empty($options['encodingaeskey'])) {
|
||||
throw new InvalidArgumentException("Missing Config -- [encodingaeskey]");
|
||||
}
|
||||
if (!class_exists('Prpcrypt', false)) {
|
||||
require __DIR__ . '/Prpcrypt.php';
|
||||
}
|
||||
$prpcrypt = new \Prpcrypt($this->config->get('encodingaeskey'));
|
||||
$result = Tools::fromXml($this->postxml);
|
||||
$array = $prpcrypt->decrypt($result['Encrypt']);
|
||||
if (intval($array[0]) > 0) {
|
||||
throw new InvalidResponseException($array[1], $array[0]);
|
||||
}
|
||||
list($this->postxml, $this->appid) = [$array[1], $array[2]];
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存配置与存储
|
||||
* @param string $name 缓存名称
|
||||
* @param string $value 缓存内容
|
||||
* @param int $expired 缓存时间(0表示永久缓存)
|
||||
* @throws LocalCacheException
|
||||
*/
|
||||
public static function setCache($name, $value = '', $expired = 3600)
|
||||
{
|
||||
$cache_file = self::getCacheName($name);
|
||||
$content = serialize(['name' => $name, 'value' => $value, 'expired' => time() + intval($expired)]);
|
||||
if (!file_put_contents($cache_file, $content)) {
|
||||
throw new LocalCacheException('local cache error.', '0');
|
||||
$this->receive = new Config(Tools::fromXml($this->postxml));
|
||||
} elseif ($_SERVER['REQUEST_METHOD'] == "GET" && $this->checkSignature()) {
|
||||
@ob_clean();
|
||||
exit($this->params->get('echostr'));
|
||||
} else {
|
||||
throw new InvalidResponseException('Invalid interface request.', '0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存内容
|
||||
* @param string $name 缓存名称
|
||||
* @return null|mixed
|
||||
*/
|
||||
public static function getCache($name)
|
||||
{
|
||||
$cache_file = self::getCacheName($name);
|
||||
if (file_exists($cache_file) && ($content = file_get_contents($cache_file))) {
|
||||
$data = unserialize($content);
|
||||
if (isset($data['expired']) && (intval($data['expired']) === 0 || intval($data['expired']) >= time())) {
|
||||
return $data['value'];
|
||||
}
|
||||
self::delCache($name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存文件
|
||||
* @param string $name 缓存名称
|
||||
* 验证来自微信服务器
|
||||
* @param string $str
|
||||
* @return bool
|
||||
*/
|
||||
public static function delCache($name)
|
||||
private function checkSignature($str = '')
|
||||
{
|
||||
$cache_file = self::getCacheName($name);
|
||||
return file_exists($cache_file) ? unlink($cache_file) : true;
|
||||
$nonce = $this->params->get('nonce');
|
||||
$timestamp = $this->params->get('timestamp');
|
||||
$msg_signature = $this->params->get('msg_signature');
|
||||
$signature = empty($msg_signature) ? $this->params->get('signature') : $msg_signature;
|
||||
$tmpArr = [$this->config->get('token'), $timestamp, $nonce, $str];
|
||||
sort($tmpArr, SORT_STRING);
|
||||
if (sha1(implode($tmpArr)) == $signature) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用缓存目录
|
||||
* @param string $name
|
||||
* 获取公众号推送对象
|
||||
* @return array
|
||||
*/
|
||||
public function getReceive()
|
||||
{
|
||||
return $this->receive->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复消息
|
||||
* @param array $data 消息内容
|
||||
* @param bool $return 是否返回XML内容
|
||||
* @return string
|
||||
* @throws InvalidDecryptException
|
||||
*/
|
||||
public function reply(array $data = [], $return = false)
|
||||
{
|
||||
$xml = Tools::toXml(empty($data) ? $this->message : $data);
|
||||
if ($this->encryptType == 'aes') {
|
||||
if (!class_exists('Prpcrypt', false)) {
|
||||
require __DIR__ . '/Prpcrypt.php';
|
||||
}
|
||||
$prpcrypt = new \Prpcrypt($this->config->get('encodingaeskey'));
|
||||
// 如果是第三方平台,加密得使用 component_appid
|
||||
$component_appid = $this->config->get('component_appid');
|
||||
$appid = empty($component_appid) ? $this->appid : $component_appid;
|
||||
$array = $prpcrypt->encrypt($xml, $appid);
|
||||
if ($array[0] > 0) {
|
||||
throw new InvalidDecryptException('Encrypt Error.', '0');
|
||||
}
|
||||
list($timestamp, $encrypt) = [time(), $array[1]];
|
||||
$nonce = rand(77, 999) * rand(605, 888) * rand(11, 99);
|
||||
$tmpArr = [$this->config->get('token'), $timestamp, $nonce, $encrypt];
|
||||
sort($tmpArr, SORT_STRING);
|
||||
$signature = sha1(implode($tmpArr));
|
||||
$format = "<xml><Encrypt><![CDATA[%s]]></Encrypt><MsgSignature><![CDATA[%s]]></MsgSignature><TimeStamp>%s</TimeStamp><Nonce><![CDATA[%s]]></Nonce></xml>";
|
||||
$xml = sprintf($format, $encrypt, $signature, $timestamp, $nonce);
|
||||
}
|
||||
if ($return) {
|
||||
return $xml;
|
||||
}
|
||||
@ob_clean();
|
||||
echo $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前微信OPENID
|
||||
* @return string
|
||||
*/
|
||||
private static function getCacheName($name)
|
||||
public function getOpenid()
|
||||
{
|
||||
if (empty(self::$cache_path)) {
|
||||
self::$cache_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'Cache' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
self::$cache_path = rtrim(self::$cache_path, '/\\') . DIRECTORY_SEPARATOR;
|
||||
file_exists(self::$cache_path) || mkdir(self::$cache_path, 0755, true);
|
||||
return self::$cache_path . md5($name);
|
||||
return $this->receive->get('FromUserName');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前推送消息内容
|
||||
* @return string
|
||||
*/
|
||||
public function getMsgType()
|
||||
{
|
||||
return $this->receive->get('MsgType');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前推送消息ID
|
||||
* @return string
|
||||
*/
|
||||
public function getMsgId()
|
||||
{
|
||||
return $this->receive->get('MsgId');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前推送时间
|
||||
* @return integer
|
||||
*/
|
||||
public function getMsgTime()
|
||||
{
|
||||
return $this->receive->get('CreateTime');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前推送公众号
|
||||
* @return string
|
||||
*/
|
||||
public function getToOpenid()
|
||||
{
|
||||
return $this->receive->get('ToUserName');
|
||||
}
|
||||
}
|
317
Wechat/Contracts/Tools.php
Normal file
317
Wechat/Contracts/Tools.php
Normal file
@ -0,0 +1,317 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | WeChatDeveloper
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: http://think.ctolog.com
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// +----------------------------------------------------------------------
|
||||
// | github开源项目:https://github.com/zoujingli/WeChatDeveloper
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace Wechat\Contracts;
|
||||
|
||||
use Wechat\Exceptions\InvalidArgumentException;
|
||||
use Wechat\Exceptions\InvalidResponseException;
|
||||
use Wechat\Exceptions\LocalCacheException;
|
||||
|
||||
/**
|
||||
* 网络请求支持
|
||||
* Class Request
|
||||
* @package Wechat
|
||||
*/
|
||||
class Tools
|
||||
{
|
||||
/**
|
||||
* 缓存路径
|
||||
* @var null
|
||||
*/
|
||||
public static $cache_path = null;
|
||||
|
||||
/**
|
||||
* 根据文件后缀获取文件MINE
|
||||
* @param array $ext 文件后缀
|
||||
* @param array $mine 文件后缀MINE信息
|
||||
* @return string
|
||||
* @throws LocalCacheException
|
||||
*/
|
||||
public static function getExtMine($ext, $mine = [])
|
||||
{
|
||||
$mines = self::getMines();
|
||||
foreach (is_string($ext) ? explode(',', $ext) : $ext as $e) {
|
||||
$mine[] = isset($mines[strtolower($e)]) ? $mines[strtolower($e)] : 'application/octet-stream';
|
||||
}
|
||||
return join(',', array_unique($mine));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有文件扩展的mine
|
||||
* @return array
|
||||
* @throws LocalCacheException
|
||||
*/
|
||||
private static function getMines()
|
||||
{
|
||||
$mines = self::getCache('all_ext_mine');
|
||||
if (empty($mines)) {
|
||||
$content = file_get_contents('http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types');
|
||||
preg_match_all('#^([^\s]{2,}?)\s+(.+?)$#ism', $content, $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $match) {
|
||||
foreach (explode(" ", $match[2]) as $ext) {
|
||||
$mines[$ext] = $match[1];
|
||||
}
|
||||
}
|
||||
self::setCache('all_ext_mine', $mines);
|
||||
}
|
||||
return $mines;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建CURL文件对象
|
||||
* @param $filename
|
||||
* @param string $mimetype
|
||||
* @param string $postname
|
||||
* @return \CURLFile|string
|
||||
* @throws LocalCacheException
|
||||
*/
|
||||
public static function createCurlFile($filename, $mimetype = '', $postname = '')
|
||||
{
|
||||
$basename = $postname ?: basename($filename);
|
||||
$basemine = $mimetype ?: self::getExtMine(pathinfo($filename, 4));
|
||||
if (function_exists('curl_file_create')) {
|
||||
return curl_file_create($filename, $basemine, $basename);
|
||||
}
|
||||
return "@{$filename};filename={$basename};type={$basemine}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 以get访问模拟访问
|
||||
* @param string $url 访问URL
|
||||
* @param array $query GET数
|
||||
* @param array $options
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function get($url, $query = [], $options = [])
|
||||
{
|
||||
$options['query'] = $query;
|
||||
return self::doRequest('get', $url, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以post访问模拟访问
|
||||
* @param string $url 访问URL
|
||||
* @param array $data POST数据
|
||||
* @param array $options
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function post($url, $data = [], $options = [])
|
||||
{
|
||||
$options['data'] = $data;
|
||||
return self::doRequest('post', $url, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转XML内容
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public static function toXml($data)
|
||||
{
|
||||
return "<xml>" . self::_data_to_xml($data) . "</xml>";
|
||||
}
|
||||
|
||||
/**
|
||||
* XML内容生成
|
||||
* @param array $data 数据
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
private static function _data_to_xml($data, $content = '')
|
||||
{
|
||||
foreach ($data as $key => $val) {
|
||||
$content .= "<{$key}>";
|
||||
if (is_array($val) || is_object($val)) {
|
||||
$content .= self::_data_to_xml($val);
|
||||
} elseif (is_string($val)) {
|
||||
$content .= '<![CDATA[' . preg_replace("/[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]/", '', $val) . ']]>';
|
||||
} else {
|
||||
$content .= $val;
|
||||
}
|
||||
$content .= "</{$key}>";
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析XML内容到数组
|
||||
* @param string $xml
|
||||
* @return array
|
||||
*/
|
||||
public static function fromXml($xml)
|
||||
{
|
||||
return json_decode(self::toJson(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转xml内容
|
||||
* @param array $data
|
||||
* @return null|string|string
|
||||
*/
|
||||
public static function toJson($data)
|
||||
{
|
||||
return preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function ($matches) {
|
||||
return mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE");
|
||||
}, json_encode($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析JSON内容到数组
|
||||
* @param string $json
|
||||
* @return array
|
||||
* @throws InvalidResponseException
|
||||
*/
|
||||
public static function fromJson($json)
|
||||
{
|
||||
$result = json_decode($json, true);
|
||||
if (empty($result)) {
|
||||
throw new InvalidResponseException('invalid response.', '0');
|
||||
}
|
||||
if (!empty($result['errcode'])) {
|
||||
throw new InvalidResponseException($result['errmsg'], $result['errcode'], $result);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* CURL模拟网络请求
|
||||
* @param string $method 请求方法
|
||||
* @param string $url 请求方法
|
||||
* @param array $options 请求参数[headers,data,ssl_cer,ssl_key]
|
||||
* @return bool|string
|
||||
*/
|
||||
protected static function doRequest($method, $url, $options = [])
|
||||
{
|
||||
$curl = curl_init();
|
||||
// GET参数设置
|
||||
if (!empty($options['query'])) {
|
||||
$url .= (stripos($url, '?') !== false ? '&' : '?') . http_build_query($options['query']);
|
||||
}
|
||||
// POST数据设置
|
||||
if (strtolower($method) === 'post') {
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, self::build($options['data']));
|
||||
}
|
||||
// CURL头信息设置
|
||||
if (!empty($options['headers'])) {
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $options['headers']);
|
||||
}
|
||||
// 证书文件设置
|
||||
if (!empty($options['ssl_cer'])) {
|
||||
if (file_exists($options['ssl_cer'])) {
|
||||
curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
|
||||
curl_setopt($curl, CURLOPT_SSLCERT, $options['ssl_cer']);
|
||||
} else {
|
||||
throw new InvalidArgumentException("Certificate files that do not exist. --- [{$options['ssl_cer']}]");
|
||||
}
|
||||
}
|
||||
// 证书文件设置
|
||||
if (!empty($options['ssl_key'])) {
|
||||
if (file_exists($options['ssl_key'])) {
|
||||
curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
|
||||
curl_setopt($curl, CURLOPT_SSLKEY, $options['ssl_key']);
|
||||
} else {
|
||||
throw new InvalidArgumentException("Certificate files that do not exist. --- [{$options['ssl_key']}]");
|
||||
}
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
|
||||
curl_setopt($curl, CURLOPT_HEADER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
list($content, $status) = [curl_exec($curl), curl_getinfo($curl), curl_close($curl)];
|
||||
return (intval($status["http_code"]) === 200) ? $content : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* POST数据过滤处理
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
private static function build($data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_string($value) && class_exists('CURLFile', false) && stripos($value, '@') === 0) {
|
||||
$filename = realpath(trim($value, '@'));
|
||||
if ($filename && file_exists($filename)) {
|
||||
$data[$key] = new \CURLFile($filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存配置与存储
|
||||
* @param string $name 缓存名称
|
||||
* @param string $value 缓存内容
|
||||
* @param int $expired 缓存时间(0表示永久缓存)
|
||||
* @throws LocalCacheException
|
||||
*/
|
||||
public static function setCache($name, $value = '', $expired = 3600)
|
||||
{
|
||||
$cache_file = self::getCacheName($name);
|
||||
$content = serialize(['name' => $name, 'value' => $value, 'expired' => time() + intval($expired)]);
|
||||
if (!file_put_contents($cache_file, $content)) {
|
||||
throw new LocalCacheException('local cache error.', '0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存内容
|
||||
* @param string $name 缓存名称
|
||||
* @return null|mixed
|
||||
*/
|
||||
public static function getCache($name)
|
||||
{
|
||||
$cache_file = self::getCacheName($name);
|
||||
if (file_exists($cache_file) && ($content = file_get_contents($cache_file))) {
|
||||
$data = unserialize($content);
|
||||
if (isset($data['expired']) && (intval($data['expired']) === 0 || intval($data['expired']) >= time())) {
|
||||
return $data['value'];
|
||||
}
|
||||
self::delCache($name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存文件
|
||||
* @param string $name 缓存名称
|
||||
* @return bool
|
||||
*/
|
||||
public static function delCache($name)
|
||||
{
|
||||
$cache_file = self::getCacheName($name);
|
||||
return file_exists($cache_file) ? unlink($cache_file) : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用缓存目录
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
private static function getCacheName($name)
|
||||
{
|
||||
if (empty(self::$cache_path)) {
|
||||
self::$cache_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'Cache' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
self::$cache_path = rtrim(self::$cache_path, '/\\') . DIRECTORY_SEPARATOR;
|
||||
file_exists(self::$cache_path) || mkdir(self::$cache_path, 0755, true);
|
||||
return self::$cache_path . md5($name);
|
||||
}
|
||||
}
|
@ -57,8 +57,8 @@ class Wechat
|
||||
if (empty($options['appid'])) {
|
||||
throw new InvalidArgumentException("Missing Config -- [appid]");
|
||||
}
|
||||
if (empty($options['secret'])) {
|
||||
throw new InvalidArgumentException("Missing Config -- [secret]");
|
||||
if (empty($options['appsecret'])) {
|
||||
throw new InvalidArgumentException("Missing Config -- [appsecret]");
|
||||
}
|
||||
$this->config = new Config($options);
|
||||
}
|
||||
@ -75,15 +75,15 @@ class Wechat
|
||||
return $this->access_token;
|
||||
}
|
||||
$cacheKey = $this->config->get('appid') . '_accesstoken';
|
||||
$this->access_token = Request::getCache($cacheKey);
|
||||
$this->access_token = Tools::getCache($cacheKey);
|
||||
if (!empty($this->access_token)) {
|
||||
return $this->access_token;
|
||||
}
|
||||
list($appid, $secret) = [$this->config->get('appid'), $this->config->get('secret')];
|
||||
list($appid, $secret) = [$this->config->get('appid'), $this->config->get('appsecret')];
|
||||
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$secret}";
|
||||
$result = Request::fromJson(Request::get($url));
|
||||
$result = Tools::fromJson(Tools::get($url));
|
||||
if (!empty($result['access_token'])) {
|
||||
Request::setCache($cacheKey, $result['access_token'], 6000);
|
||||
Tools::setCache($cacheKey, $result['access_token'], 6000);
|
||||
}
|
||||
return $result['access_token'];
|
||||
}
|
||||
@ -95,7 +95,7 @@ class Wechat
|
||||
public function delAccessToken()
|
||||
{
|
||||
$this->access_token = '';
|
||||
return Request::delCache($this->config->get('appid') . '_accesstoken');
|
||||
return Tools::delCache($this->config->get('appid') . '_accesstoken');
|
||||
}
|
||||
|
||||
|
||||
@ -107,7 +107,7 @@ class Wechat
|
||||
protected function httpGetForJson($url)
|
||||
{
|
||||
try {
|
||||
return Request::fromJson(Request::get($url));
|
||||
return Tools::fromJson(Tools::get($url));
|
||||
} catch (InvalidResponseException $e) {
|
||||
if (!$this->isTry && in_array($e->getCode(), ['40014', '40001', '41001', '42001'])) {
|
||||
$this->delAccessToken();
|
||||
@ -127,7 +127,7 @@ class Wechat
|
||||
protected function httpPostForJson($url, array $data, $buildToJson = true)
|
||||
{
|
||||
try {
|
||||
return Request::fromJson(Request::post($url, $buildToJson ? Request::toJson($data) : $data));
|
||||
return Tools::fromJson(Tools::post($url, $buildToJson ? Tools::toJson($data) : $data));
|
||||
} catch (InvalidResponseException $e) {
|
||||
if (!$this->isTry && in_array($e->getCode(), ['40014', '40001', '41001', '42001'])) {
|
||||
$this->delAccessToken();
|
||||
|
41
Wechat/Exceptions/InvalidDecryptException.php
Normal file
41
Wechat/Exceptions/InvalidDecryptException.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | WeChatDeveloper
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: http://think.ctolog.com
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// +----------------------------------------------------------------------
|
||||
// | github开源项目:https://github.com/zoujingli/WeChatDeveloper
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace Wechat\Exceptions;
|
||||
|
||||
/**
|
||||
* 返回异常
|
||||
* Class InvalidResponseException
|
||||
* @package Wechat
|
||||
*/
|
||||
class InvalidDecryptException extends \Exception
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $raw = [];
|
||||
|
||||
/**
|
||||
* InvalidDecryptException constructor.
|
||||
* @param string $message
|
||||
* @param integer $code
|
||||
* @param array $raw
|
||||
*/
|
||||
public function __construct($message, $code, $raw = [])
|
||||
{
|
||||
parent::__construct($message, intval($code));
|
||||
$this->raw = $raw;
|
||||
}
|
||||
|
||||
}
|
@ -28,8 +28,8 @@ class InvalidResponseException extends \Exception
|
||||
|
||||
/**
|
||||
* InvalidResponseException constructor.
|
||||
* @param $message
|
||||
* @param $code
|
||||
* @param string $message
|
||||
* @param integer $code
|
||||
* @param array $raw
|
||||
*/
|
||||
public function __construct($message, $code, $raw = [])
|
||||
|
@ -29,8 +29,8 @@ class LocalCacheException extends \Exception
|
||||
|
||||
/**
|
||||
* InvalidResponseException constructor.
|
||||
* @param $message
|
||||
* @param $code
|
||||
* @param string $message
|
||||
* @param integer $code
|
||||
* @param array $raw
|
||||
*/
|
||||
public function __construct($message, $code, $raw = [])
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
namespace Wechat;
|
||||
|
||||
use Wechat\Contracts\Request;
|
||||
use Wechat\Contracts\Tools;
|
||||
use Wechat\Contracts\Wechat;
|
||||
use Wechat\Exceptions\InvalidResponseException;
|
||||
|
||||
@ -40,7 +40,7 @@ class Media extends Wechat
|
||||
}
|
||||
$url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type={$type}";
|
||||
$this->registerApi($url, __FUNCTION__, func_get_args());
|
||||
return $this->httpPostForJson($url, ['media' => Request::createCurlFile($filename)], false);
|
||||
return $this->httpPostForJson($url, ['media' => Tools::createCurlFile($filename)], false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,7 +54,7 @@ class Media extends Wechat
|
||||
{
|
||||
$url = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id={$media_id}";
|
||||
$this->registerApi($url, __FUNCTION__, func_get_args());
|
||||
return Request::get($url);
|
||||
return Tools::get($url);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,7 +99,7 @@ class Media extends Wechat
|
||||
{
|
||||
$url = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN";
|
||||
$this->registerApi($url, __FUNCTION__, func_get_args());
|
||||
return $this->httpPostForJson($url, ['media' => Request::createCurlFile($filename)], false);
|
||||
return $this->httpPostForJson($url, ['media' => Tools::createCurlFile($filename)], false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,7 +118,7 @@ class Media extends Wechat
|
||||
}
|
||||
$url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=ACCESS_TOKEN&type={$type}";
|
||||
$this->registerApi($url, __FUNCTION__, func_get_args());
|
||||
return $this->httpPostForJson($url, ['media' => Request::createCurlFile($filename), 'description' => Request::toJson($description)], false);
|
||||
return $this->httpPostForJson($url, ['media' => Tools::createCurlFile($filename), 'description' => Tools::toJson($description)], false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
164
Wechat/Receive.php
Normal file
164
Wechat/Receive.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | WeChatDeveloper
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2018 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: http://think.ctolog.com
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// +----------------------------------------------------------------------
|
||||
// | github开源项目:https://github.com/zoujingli/WeChatDeveloper
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace Wechat;
|
||||
|
||||
use Wechat\Contracts\Request;
|
||||
|
||||
/**
|
||||
* 公众号推送管理
|
||||
* Class Receive
|
||||
* @package Wechat
|
||||
*/
|
||||
class Receive extends Request
|
||||
{
|
||||
|
||||
/**
|
||||
* 转发多客服消息
|
||||
* @param string $account
|
||||
* @return $this
|
||||
*/
|
||||
public function transferCustomerService($account = '')
|
||||
{
|
||||
$this->message = [
|
||||
'CreateTime' => time(),
|
||||
'ToUserName' => $this->getOpenid(),
|
||||
'FromUserName' => $this->getToOpenid(),
|
||||
'MsgType' => 'transfer_customer_service',
|
||||
];
|
||||
empty($account) ?: $this->message['TransInfo'] = ['KfAccount' => $account];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文本消息
|
||||
* @param string $content 文本内容
|
||||
* @return $this
|
||||
*/
|
||||
public function text($content = '')
|
||||
{
|
||||
$this->message = [
|
||||
'MsgType' => 'text',
|
||||
'CreateTime' => time(),
|
||||
'Content' => $content,
|
||||
'ToUserName' => $this->getOpenid(),
|
||||
'FromUserName' => $this->getToOpenid(),
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置回复图文
|
||||
* @param array $newsData
|
||||
* @return $this
|
||||
*/
|
||||
public function news($newsData = [])
|
||||
{
|
||||
$this->message = [
|
||||
'CreateTime' => time(),
|
||||
'MsgType' => 'mpnews',
|
||||
'Articles' => $newsData,
|
||||
'ToUserName' => $this->getOpenid(),
|
||||
'FromUserName' => $this->getToOpenid(),
|
||||
'ArticleCount' => count($newsData),
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图片消息
|
||||
* @param string $mediaId 图片媒体ID
|
||||
* @return $this
|
||||
*/
|
||||
public function image($mediaId = '')
|
||||
{
|
||||
$this->message = [
|
||||
'MsgType' => 'image',
|
||||
'CreateTime' => time(),
|
||||
'ToUserName' => $this->getOpenid(),
|
||||
'FromUserName' => $this->getToOpenid(),
|
||||
'Image' => ['MediaId' => $mediaId],
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语音回复消息
|
||||
* @param string $mediaid 语音媒体ID
|
||||
* @return $this
|
||||
*/
|
||||
public function voice($mediaid = '')
|
||||
{
|
||||
$this->message = [
|
||||
'CreateTime' => time(),
|
||||
'MsgType' => 'voice',
|
||||
'ToUserName' => $this->getOpenid(),
|
||||
'FromUserName' => $this->getToOpenid(),
|
||||
'Voice' => ['MediaId' => $mediaid],
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置视频回复消息
|
||||
* @param string $mediaid 视频媒体ID
|
||||
* @param string $title 视频标题
|
||||
* @param string $description 视频描述
|
||||
* @return $this
|
||||
*/
|
||||
public function video($mediaid = '', $title = '', $description = '')
|
||||
{
|
||||
$this->message = [
|
||||
'CreateTime' => time(),
|
||||
'MsgType' => 'video',
|
||||
'ToUserName' => $this->getOpenid(),
|
||||
'FromUserName' => $this->getToOpenid(),
|
||||
'Video' => [
|
||||
'Title' => $title,
|
||||
'MediaId' => $mediaid,
|
||||
'Description' => $description,
|
||||
],
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置音乐回复消息
|
||||
* @param string $title 音乐标题
|
||||
* @param string $desc 音乐描述
|
||||
* @param string $musicurl 音乐地址
|
||||
* @param string $hgmusicurl 高清音乐地址
|
||||
* @param string $thumbmediaid 音乐图片缩略图的媒体id(可选)
|
||||
* @return $this
|
||||
*/
|
||||
public function music($title, $desc, $musicurl, $hgmusicurl = '', $thumbmediaid = '')
|
||||
{
|
||||
$this->message = [
|
||||
'CreateTime' => time(),
|
||||
'MsgType' => 'music',
|
||||
'ToUserName' => $this->getOpenid(),
|
||||
'FromUserName' => $this->getToOpenid(),
|
||||
'Music' => [
|
||||
'Title' => $title,
|
||||
'Description' => $desc,
|
||||
'MusicUrl' => $musicurl,
|
||||
'HQMusicUrl' => $hgmusicurl,
|
||||
],
|
||||
];
|
||||
if ($thumbmediaid) {
|
||||
$this->message['Music']['ThumbMediaId'] = $thumbmediaid;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user