##背景

前段时间迁移小米推送部分代码,这部分之前是其他人负责的。读了代码,发现了两点:

  1. 所有接口的实现除了url和传参基本都是一致的
  2. android和ios的设备需要分别推送一次

刚好这段时间了解了一下php的重载概念【动态的创建类属型和方法,不同于java等的参数不同方法名相通的概念,具体的概念可以RTFM】和curl的并发方式,所以

  • 对于以上第一点:是否可以利用php的重载概念,用魔术方法call()动态的变更接口调用的url参数和请求参数,去实现对小米推送不同接口的调用,也就是说我们所有的调用逻辑都是实现在call这一个方法中,却对外提供小米推送不同接口的调用方法,这样就会极大的简化代码
  • 对于以上第二点:是否可以利用curl的并行方式一次性推送给android和ios设备,这样理论上我们可以减少一次推送调用的时间,因为我们不需要再去等待给一类设备推送完了,再去推送给另一类设备

具体实现

####接下来,我们开始造代码。首先,构造一个mipush的实体,实体的成员属性包括:运行环境、初始化的一些配置参数、所实现的小米接口的信息(接口uri和参数等)####

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
* 运行环境 develop:开发环境 product:生产环境
* @var string
*/
private $_environment = 'develop';
/**
* 设备系统类型 android ios
* @var string
*/
private $_osType = '';
/**
* 小米推送域名
* @var string
*/
private $_host = '';
/**
* 请求头
* @var string
*/
private $_headers = '';
/**
* 接口url
* @var string
*/
private $_url = '';
/**
* 调用的接口方法名称
* @var array
*/
private $_function = [];
/**
* 请求参数
* @var array
*/
private $_data = [];
/**
* 小米推送设置
* @var array
*/
private $_options = [
'title' => '消息通知自定义title',
'restricted_package_name' => '',
'pass_through' => 0, // 0 通知栏消息 1 透传
'notify_type' => -1, // -1:默认所有,1:使用默认提示音提示,2:使用默认震动提示,4:使用默认led灯光提示
'time_to_send' => 0, // 定时推送 单位毫秒 默认0
];
/**
* 运行环境配置
* @var array
*/
private static $_environmentConfig = [
'domain' => [
'product' => 'https://api.xmpush.xiaomi.com/',
'develop' => 'https://sandbox.xmpush.xiaomi.com/'
],
];
/**
* 小米推送接口信息定义
*
* url/请求参数
* @var array
*/
private $_functionDefine = [
'regid' => [
'uri' => 'v3/message/regid',
'arguments' => [
'registration_id' => [
'type' => 'array',
'must' => 'y'
],
'description' => [
'type' => 'string',
'must' => 'y'
],
'params' => [//自定义参数
'type' => 'array',
'must' => 'n'
],
]
],
'userAccount' => [
'uri' => 'v2/message/user_account',
'arguments' => [
'user_account' => [
'type' => 'array',
'must' => 'y'
],
'description' => [
'type' => 'string',
'must' => 'y'
],
'params' => [//自定义参数
'type' => 'array',
'must' => 'n'
],
]
],
'alias' => [
'uri' => 'v3/message/alias',
'arguments' => [
'alias' => [
'type' => 'array',
'must' => 'y'
],
'description' => [
'type' => 'string',
'must' => 'y'
],
'params' => [//自定义参数
'type' => 'array',
'must' => 'n'
],
]
],
'topic' => [
'uri' => 'v3/message/topic',
'arguments' => [
'topics' => [
'type' => 'array',
'must' => 'y'
],
'description' => [
'type' => 'string',
'must' => 'y'
],
'params' => [//自定义参数
'type' => 'array',
'must' => 'n'
],
]
],
'multiTopic' => [
'uri' => 'v3/message/multi_topic',
'arguments' => [
'topics' => [
'type' => 'array',
'must' => 'y'
],
'topics_op' => [// UNION并集,INTERSECTION交集,EXCEPT差集
'type' => 'string',
'must' => 'y'
],
'description' => [
'type' => 'string',
'must' => 'y'
],
'params' => [//自定义参数
'type' => 'array',
'must' => 'n'
],
]
],
'all' => [
'uri' => 'v3/message/all',
'arguments' => [
'description' => [
'type' => 'string',
'must' => 'y'
],
'params' => [//自定义参数
'type' => 'array',
'must' => 'n'
],
]
],
];

####mipush实体的构造函数:实现对一系列配置的初始化####

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* 初始化配置
*
* @param $string $os_type 系统类型
* @param $string $config 配置
* @param array $options 设置 [
* 'title' => 'string,标题',
* 'pass_through' => 'tinyint,0通知栏消息,1透传,默认0'
* 'notify_type' => 'tinyint,-1,1,2,3,4',
* 'time_to_send' => 'int, 定时推送, 毫秒'
* ]
* @param string $environment 环境
*/
public function __construct($os_type='', $config=array(), $options=array(), $environment='')
{
/* init environment */
if ($environment) {
$this->_environment = $environment;
}
if ($os_type === 'ios') {
$this->_host = self::$_environmentConfig['domain'][$this->_environment];// ios
}else{
$this->_host = self::$_environmentConfig['domain']['product'];// android
}
/* init option */
$this->_headers = [];
$this->_headers[] = 'Authorization: key=' . $config['secret'];
if ($os_type === 'android') {
$this->_options['restricted_package_name'] = $config['package_name'];
}
foreach ($this->_options as $k => &$v) {
if (in_array($k, $options)) {
$v = $options[$k];
}
}
}

####mipush实体的魔法方法__call:动态实现对小米推送接口的参数验证和调用,以至于我们未来实现新的小米推送接口及实现配置就可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
* 魔术方法
*
* 重载对象方法
* @param string $name 小米推送方法名称
* @param array $arguments 请求参数
* @return mixed void||object
*/
public function __call($name,$arguments)
{
$arguments = $arguments[0];
$this->_function = $this->_functionDefine[$name];
$this->_url = $this->_host . $this->_function['uri'];
$this->dataCheck($arguments);
switch ($name) {
case 'regid':
$this->_data['registration_id'] = $arguments['registration_id'];
break;
case 'userAccount':
$this->_data['user_account'] = implode(',', $arguments['user_account']);
break;
case 'alias':
$this->_data['alias'] = implode(',', $arguments['alias']);
break;
case 'topic':
$this->_data['topic'] = $arguments['topic'];
break;
case 'multiTopic':
$this->_data['topics'] = implode(";$;", $arguments['topics']);
$this->_data['topic_op'] = $arguments['topic_op'];
break;
case 'all':
$this->_data['topics'] = implode(";$;", $topics);
$this->_data['topic_op'] = 'UNION';
break;
default:
throw new \Exception('Sorry, This function is useless in this version', 404);
break;
}
$this->_data['description'] = $arguments['description'];
if($arguments['params']) {
foreach ($arguments['params'] as $k => $v) {
$this->_data['extra.'.$k] = $v;// 自定义参数
}
}
if ($this->_osType === 'android') {
$this->_data = array_merge($this->_data, $this->_options);
}
}

定义完小米推送实体后,我们只需要通过mipush这个实体,实例化生产不同设备类型的对象,再由curl并行的方式发起推送即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 并行推送
*
* @param Mipush $mipush_ios ios端实体
* @param Mipush $mipush_android android端实体
* @return array 推送结果
*/
private static function curlRequest(Mipush $mipush_ios, Mipush $mipush_android)
{
$ch_ios = curl_init();
$ch_android = curl_init();
curl_setopt($ch_ios, CURLOPT_URL, $mipush_ios->_url);
curl_setopt($ch_ios, CURLOPT_POST, 1);
curl_setopt($ch_ios, CURLOPT_POSTFIELDS, $mipush_ios->_data);
curl_setopt($ch_ios, CURLOPT_HTTPHEADER, $mipush_ios->_headers);
curl_setopt($ch_ios, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch_android, CURLOPT_URL, $mipush_android->_url);
curl_setopt($ch_android, CURLOPT_POST, 1);
curl_setopt($ch_android, CURLOPT_POSTFIELDS, $mipush_android->_data);
curl_setopt($ch_android, CURLOPT_HTTPHEADER, $mipush_android->_headers);
curl_setopt($ch_android, CURLOPT_RETURNTRANSFER, 1);
$mh = curl_multi_init();
curl_multi_add_handle($mh, $ch_ios);
curl_multi_add_handle($mh, $ch_android);
$running=null;
do {
curl_multi_exec($mh,$running);
} while($running > 0);
$result['ios'] = json_decode(curl_multi_getcontent($ch_ios), true);
$result['android'] = json_decode(curl_multi_getcontent($ch_android), true);
curl_multi_remove_handle($mh, $ch_ios);
curl_multi_remove_handle($mh, $ch_android);
curl_multi_close($mh);
return $result;
}

以上就是这些了,通过以上的方式我们就用很少的代码封装了一个小米sdk,目前只实现了按regid(登记id),alias(别名),user_account(用户账号),topic(标签), multi_topic(多标签),all(全体)推送。


##如何使用?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
composer require tigerb/easy-mipush
使用格式:
try {
Push::init(
['secret' => 'string,必传,ios密钥'],
['secret' => 'string,必传,android密钥', 'package_name' => 'string,必传,android包名']
[
'title' => 'string,非必传,消息通知自定义title',
'pass_through' => 'int,非必传,0通知栏消息,1透传,默认0',
'notify_type' => 'int,非必传,-1:默认所有,1:使用默认提示音提示,2:使用默认震动提示,4:使用默认led灯光提示',
'time_to_send' => 'int,非必传,定时推送,单位毫秒,默认0',
],
'string,develop开发环境,product生产环境, 默认develop'
);
$res = Push::toUse('string,小米push方法名', 'array, 该方法对应的参数');
echo json_encode($res, JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
echo $e->getMessage();
}
使用示例:
use Mipush\Push;
require './vendor/autoload.php';
try {
Push::init(
['secret' => 'test'],
['secret' => 'test', 'package_name' => 'com.test'],
[
'title' => 'test',
'pass_through' => 0,
'notify_type' => -1,
'time_to_send' => 0,
],
'develop'
);
$res = Push::toUse('userAccount', [
'user_account' => [1],
'description' => 'test'
]);
echo json_encode($res, JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
echo $e->getMessage();
}

最后,第一次写这种文章,写的不是很好,写的不对的地方谢谢大家留言指证出来,其次,还没验证过这里使用魔术方法的性能消耗,因为之前有看过鸟哥的文章不赞成使用魔术方法,欢迎大家留言指证,谢谢。

完整代码地址: https://github.com/TIGERB/easy-mipush

Easy PHP:一个极速轻量级的PHP全栈框架