Skip to content

Commit 4438c8b

Browse files
author
胡东文
committed
Merge branch 'callback' into 'master'
Callback See merge request !3
2 parents 1bb21ec + efcb094 commit 4438c8b

File tree

3 files changed

+243
-0
lines changed

3 files changed

+243
-0
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
}
1414
],
1515
"require": {
16+
"bobchengbin/yii2-xml-request-parser": "v0.0.2",
1617
"lspbupt/yii2-curl": "*"
1718
},
1819
"autoload": {

src/actions/CallbackAction.php

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<?php
2+
namespace lspbupt\wechat\actions;
3+
use Yii;
4+
use yii\base\Action;
5+
use yii\base\InvalidConfigException;
6+
use yii\helpers\ArrayHelper;
7+
use yii\web\Response;
8+
use Closure;
9+
use \lspbupt\wechat\helpers\XmlResponseFormatter;
10+
//微信公众号回调接口
11+
12+
class CallbackAction extends Action
13+
{
14+
public $token;
15+
public $processFunc;
16+
public function init()
17+
{
18+
parent::init();
19+
$this->controller->enableCsrfValidation = false;
20+
if (empty($this->processFunc)) {
21+
throw new InvalidConfigException("请配置处理函数");
22+
}
23+
if (empty($this->token)) {
24+
throw new InvalidConfigException("请配置token");
25+
}
26+
if(!($this->processFunc instanceof Closure)) {
27+
throw new InvalidConfigException("处理函数必须是Closure");
28+
}
29+
//自动处理xml,将rawBody中的xml直接转换为Yii::$app->request->post()
30+
Yii::$app->request->parsers = [
31+
'application/xml' => [
32+
'class' => '\bobchengbin\Yii2XmlRequestParser\XmlRequestParser',
33+
'priority' => 'tag',
34+
],
35+
'text/xml' => [
36+
'class' => '\bobchengbin\Yii2XmlRequestParser\XmlRequestParser',
37+
'priority' => 'tag',
38+
],
39+
];
40+
Yii::$app->response->formatters = [
41+
Response::FORMAT_XML => [
42+
'class' => '\lspbupt\wechat\helpers\XmlResponseFormatter'
43+
]
44+
];
45+
Yii::$app->response->format = Response::FORMAT_RAW;
46+
}
47+
48+
public function run()
49+
{
50+
$arr = Yii::$app->request->get();
51+
$arr['token'] = $this->token;
52+
if(self::checkSign($arr)) {
53+
$postArr = Yii::$app->request->post();
54+
if(empty($postArr)) {
55+
return ArrayHelper::getValue($arr, "echostr", "");
56+
}
57+
$data = call_user_func($this->processFunc, $postArr);
58+
return $data;
59+
}
60+
return "Token Error!";
61+
}
62+
63+
public static function checkSign($arr)
64+
{
65+
$signature = ArrayHelper::getValue($arr, "signature", "");
66+
$timestamp = ArrayHelper::getValue($arr, "timestamp", "");
67+
$nonce = ArrayHelper::getValue($arr, "nonce", "");
68+
$token = ArrayHelper::getValue($arr, "token", "");
69+
return $signature == self::getSign($token, $timestamp, $nonce);
70+
}
71+
72+
public static function getSign($token, $timestamp, $nonce)
73+
{
74+
$tmpArr = array($token, $timestamp, $nonce);
75+
sort($tmpArr, SORT_STRING);
76+
$tmpStr = implode($tmpArr);
77+
return sha1($tmpStr);
78+
}
79+
80+
public static function getCdataArr($str)
81+
{
82+
return [$str, XmlResponseFormatter::CDATA => true];
83+
}
84+
85+
public static function baseMsg($type, $dataArr, $from, $to, $createTime)
86+
{
87+
Yii::$app->response->format = Response::FORMAT_XML;
88+
empty($createTime) && $createTime = (string)time();
89+
empty($from) && $from = Yii::$app->request->post("ToUserName", "");
90+
empty($to) && $to = Yii::$app->request->post("FromUserName", "");
91+
return [
92+
'ToUserName' => self::getCdataArr($to),
93+
'FromUserName' => self::getCdataArr($from),
94+
'CreateTime' => $createTime,
95+
'MsgType' => self::getCdataArr(strtolower($type)),
96+
$type => $dataArr,
97+
];
98+
}
99+
100+
public static function replyTextMsg($content, $from=null, $to=null, $createTime=null)
101+
{
102+
$arr = self::baseMsg("Text", self::getCdataArr($content), $from, $to, $createTime);
103+
$arr['Content'] = $arr['Text'];
104+
unset($arr['Text']);
105+
return $arr;
106+
}
107+
108+
public static function replyImageMsg($mediaId, $from=null, $to=null, $createTime=null)
109+
{
110+
$data = ['MediaId' => self::getCdataArr($mediaId)];
111+
return self::baseMsg("Image", $data, $from, $to, $createTime);
112+
}
113+
114+
public static function replyVoiceMsg($mediaId, $from=null, $to=null, $createTime=null)
115+
{
116+
$data = ['MediaId' => self::getCdataArr($mediaId)];
117+
return self::baseMsg("Voice", $data, $from, $to, $createTime);
118+
}
119+
120+
public static function replyVideoMsg($mediaId, $titile = "", $desc="", $from=null, $to=null, $createTime=null)
121+
{
122+
$data = [
123+
'MediaId' => self::getCdataArr($mediaId),
124+
'Title' => self::getCdataArr($title),
125+
'Description' => self::getCdataArr($desc),
126+
];
127+
return self::baseMsg("Video", $data, $from, $to, $createTime);
128+
}
129+
130+
public static function replyMusicMsg($title="", $desc="", $musicUrl="", $hqUrl="", $thumbMediaId="", $from=null, $to=null, $createTime=null)
131+
{
132+
$data = [
133+
'Title' => self::getCdataArr($title),
134+
'Description' => self::getCdataArr($desc),
135+
//音乐链接
136+
'MusicURL' => self::getCdataArr($musicUrl),
137+
//高质量音乐链接,WIFI环境优先使用该链接播放音乐
138+
'HQMusicUrl' => self::getCdataArr($hqUrl),
139+
// 缩略图的媒体id
140+
'ThumbMediaId' => self::getCdataArr($thumbMediaId),
141+
];
142+
return self::baseMsg("Music", $data, $from, $to, $createTime);
143+
}
144+
145+
public static function replyNewsMsg($items, $from=null, $to=null, $createTime=null)
146+
{
147+
$arr = self::baseMsg("News", [], $from, $to, $createTime);
148+
unset($arr['News']);
149+
$articles = [];
150+
$keys = ['Title', 'Description', 'Url', "PicUrl"];
151+
foreach($items as $item) {
152+
$temp = [];
153+
foreach($keys as $key) {
154+
$temp[$key] = self::getCdataArr(ArrayHelper::getValue($item, $key, ""));
155+
}
156+
$articles[] = $temp;
157+
}
158+
$arr['ArticleCount'] = count($articles);
159+
$arr['Articles'] = $articles;
160+
return $arr;
161+
}
162+
163+
public static function replySingleMsg($title="", $desc="", $url="", $picUrl="", $from=null, $to=null, $createTime=null)
164+
{
165+
$items = [
166+
[
167+
'Title' => $title,
168+
'Description' => $desc,
169+
'PicUrl' => $picUrl,
170+
'Url' => $url,
171+
],
172+
];
173+
return self::replyNewsMsg($items, $from, $to, $createTime);
174+
}
175+
}

src/helpers/XmlResponseFormatter.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
namespace lspbupt\wechat\helpers;
3+
4+
use DOMElement;
5+
use DOMText;
6+
use yii\helpers\StringHelper;
7+
use yii\base\Arrayable;
8+
use DOMCdataSection;
9+
10+
class XmlResponseFormatter extends \yii\web\XmlResponseFormatter{
11+
public $rootTag = "xml"; // 这里我就可以把 rootTag 的默认值修改成 xml 了
12+
13+
/**
14+
* 如果需要使用 CDATA 那就需要把原来的数据转成数组,并且数组含有以下key
15+
* ,我们就把这个节点添加成一个 DOMCdataSection
16+
*/
17+
const CDATA = '---cdata---'; // 这个是是否使用CDATA 的下标
18+
19+
/**
20+
* @param DOMElement $element
21+
* @param mixed $data
22+
*/
23+
protected function buildXml($element, $data)
24+
{
25+
if (is_array($data) ||
26+
($data instanceof \Traversable && $this->useTraversableAsArray && !$data instanceof Arrayable)
27+
) {
28+
foreach ($data as $name => $value) {
29+
if (is_int($name) && is_object($value)) {
30+
$this->buildXml($element, $value);
31+
} elseif (is_array($value) || is_object($value)) {
32+
$child = new DOMElement($this->getValidXmlElementName($name));
33+
$element->appendChild($child);
34+
// 主要就是修改这一个点,如果值是一个数组,并且含有 CDATA 的,那么就直接创建一个 CdataSection 节点,
35+
// 而不把它本身当作列表再回调。
36+
if(array_key_exists(self::CDATA, $value)){
37+
$child->appendChild(new DOMCdataSection((string) $value[0]));
38+
}else{
39+
$this->buildXml($child, $value);
40+
}
41+
} else {
42+
$child = new DOMElement($this->getValidXmlElementName($name));
43+
$element->appendChild($child);
44+
$child->appendChild(new DOMText($this->formatScalarValue($value)));
45+
}
46+
}
47+
} elseif (is_object($data)) {
48+
if ($this->useObjectTags) {
49+
$child = new DOMElement(StringHelper::basename(get_class($data)));
50+
$element->appendChild($child);
51+
} else {
52+
$child = $element;
53+
}
54+
if ($data instanceof Arrayable) {
55+
$this->buildXml($child, $data->toArray());
56+
} else {
57+
$array = [];
58+
foreach ($data as $name => $value) {
59+
$array[$name] = $value;
60+
}
61+
$this->buildXml($child, $array);
62+
}
63+
} else {
64+
$element->appendChild(new DOMText($this->formatScalarValue($data)));
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)