Completed
Push — develop ( 548ee0...0abce4 )
by Romain
02:09
created

Gcm::_executePush()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
namespace ker0x\CakeGCM\Lib;
3
4
use Cake\Core\InstanceConfigTrait;
5
use Cake\Network\Http\Client;
6
use Cake\Utility\Hash;
7
use \Exception;
8
9
class Gcm {
10
11
    use InstanceConfigTrait;
12
13
    /**
14
     * Default config
15
     *
16
     * @var array
17
     */
18
    protected $_defaultConfig = [
19
        'api' => [
20
            'key' => null,
21
            'url' => 'https://gcm-http.googleapis.com/gcm/send'
22
        ],
23
        'parameters' => [
24
            'collapse_key' => null,
25
            'priority' => 'normal',
26
            'delay_while_idle' => false,
27
            'dry_run' => false,
28
            'time_to_live' => 0,
29
            'restricted_package_name' => null
30
        ],
31
        'http' => []
32
    ];
33
34
    /**
35
     * List of parameters available to use in notification messages.
36
     *
37
     * @var array
38
     */
39
    protected $_allowedNotificationParameters = [
40
        'title',
41
        'body',
42
        'icon',
43
        'sound',
44
        'badge',
45
        'tag',
46
        'color',
47
        'click_action',
48
        'body_loc_key',
49
        'body_loc_args',
50
        'title_loc_key',
51
        'title_loc_args'
52
    ];
53
54
    /**
55
     * Error code and message.
56
     *
57
     * @var array
58
     */
59
    protected $_errorMessages = [];
60
61
    /**
62
     * Response of the request
63
     *
64
     * @var object
65
     */
66
    protected $_response = null;
67
68
    /**
69
     * Constructor
70
     *
71
     * @param array $config Array of configuration settings
72
     */
73
    public function __construct(array $config = [])
74
    {
75
        $this->config($config);
76
77
        $this->_errorMessages = [
78
            '400' => __('Error 400. The request could not be parsed as JSON.'),
79
            '401' => __('Error 401. Unable to authenticating the sender account.'),
80
            '500' => __('Error 500. Internal Server Error.'),
81
            '503' => __('Error 503. Service Unavailable.')
82
        ];
83
    }
84
85
    /**
86
     * Send a downstream message to one or more devices
87
     *
88
     * @param mixed $ids Devices'ids
89
     * @param array $payload The notification and/or some datas
90
     * @param array $parameters Parameters for the GCM request
91
     * @throws Exception
92
     * @return bool
93
     */
94
    public function send($ids = null, array $payload = [], array $parameters = [])
95
    {
96
        $ids = $this->_checkIds($ids);
97
98
        if (!is_array($payload)) {
99
            throw new Exception(__('Payload must be an array.'));
100
        }
101
102
        if (isset($payload['notification'])) {
103
            $payload['notification'] = $this->_checkNotification($payload['notification']);
104
        }
105
106
        if (isset($payload['data'])) {
107
            $payload['data'] = $this->_checkData($payload['data']);
108
        }
109
110
        $parameters = $this->_checkParameters($parameters);
111
112
        $message = $this->_buildMessage($ids, $payload, $parameters);
113
114
        return $this->_executePush($message);
115
    }
116
117
    /**
118
     * Shortcut to send notification
119
     *
120
     * @param mixed $ids Devices'ids
121
     * @param array $notification The notification
122
     * @param array $parameters Parameters for the GCM request
123
     * @return bool
124
     */
125
    public function sendNotification($ids = null, array $notification = [], array $parameters = [])
126
    {
127
        return $this->send($ids, ['notification' => $notification], $parameters);
128
    }
129
130
    /**
131
     * Shortcut to send datas
132
     *
133
     * @param mixed $ids Devices'ids
134
     * @param array $data Some datas
135
     * @param array $parameters Parameters for the GCM request
136
     * @return bool
137
     */
138
    public function sendData($ids = null, array $data = [], array $parameters = [])
139
    {
140
        return $this->send($ids, ['data' => $data], $parameters);
141
    }
142
143
    /**
144
     * Return the response of the push
145
     *
146
     * @return string
147
     */
148
    public function response()
149
    {
150
        if (array_key_exists($this->_response->code, $this->_errorMessages)) {
151
            return $this->_errorMessages[$this->_response->code];
152
        }
153
154
        return json_decode($this->_response->body, true);
155
    }
156
157
    /**
158
     * Send the message throught a POST request to the GCM servers
159
     *
160
     * @param string $message The message to send
161
     * @throws Exception
162
     * @return bool
163
     */
164
    protected function _executePush($message)
165
    {
166
        $options = $this->_getHttpOptions();
167
168
        $http = new Client();
169
        $this->_response = $http->post($this->config('api.url'), $message, $options);
170
171
        return ($this->_response->code === '200') ? true : false;
172
    }
173
174
    /**
175
     * Build the message from the ids, payload and parameters
176
     *
177
     * @param array|string $ids Devices'ids
178
     * @param array $payload The notification and/or some datas
179
     * @param array $parameters Parameters for the GCM request
180
     * @return string
181
     */
182
    protected function _buildMessage($ids, $payload, $parameters)
183
    {
184
        $message = (count($ids) > 1) ? ['registration_ids' => $ids] : ['to' => current($ids)];
185
186
        if (!empty($payload)) {
187
            $message += $payload;
188
        }
189
190
        if (!empty($parameters)) {
191
            $message += $parameters;
192
        }
193
194
        return json_encode($message);
195
    }
196
197
    /**
198
     * Check if the ids are correct
199
     *
200
     * @param mixed $ids Devices'ids
201
     * @throws Exception
202
     * @return array
203
     */
204
    protected function _checkIds($ids)
205
    {
206
        if (is_string($ids)) {
207
            $ids = (array)$ids;
208
        }
209
210
        if (is_null($ids) || !is_array($ids) || empty($ids)) {
211
            throw new Exception(__('Ids must be a string or an array with at least 1 token.'));
212
        }
213
214
        if (is_array($ids) && count($ids) > 1000) {
215
            throw new Exception(__('Ids must contain at least 1 and at most 1000 registration tokens.'));
216
        }
217
218
        return $ids;
219
    }
220
221
    /**
222
     * Check if the notification array is correctly build
223
     *
224
     * @param array $notification The notification
225
     * @throws Exception
226
     * @return array $notification
227
     */
228
    protected function _checkNotification(array $notification = [])
229
    {
230
        if (!is_array($notification)) {
231
            throw new Exception('Notification must be an array.');
232
        }
233
234
        if (empty($notification) || !isset($notification['title'])) {
235
            throw new Exception('Notification\'s array must contain at least a key title.');
236
        }
237
238
        if (!isset($notification['icon'])) {
239
            $notification['icon'] = 'myicon';
240
        }
241
242
        foreach ($notification as $key => $value) {
243
            if (!in_array($key, $this->_allowedNotificationParameters)) {
244
                throw new Exception("The key {$key} is not allowed in notifications.");
245
            }
246
        }
247
248
        return $notification;
249
    }
250
251
    /**
252
     * Check if the data array is correctly build
253
     *
254
     * @param array $data Some datas
255
     * @throws Exception
256
     * @return array $data
257
     */
258
    protected function _checkData(array $data = [])
259
    {
260
        if (!is_array($data)) {
261
            throw new Exception('Data must ba an array.');
262
        }
263
264
        if (empty($data)) {
265
            throw new Exception('Data\'s array can\'t be empty.');
266
        }
267
268
        // Convert all data into string
269
        foreach ($data as $key => $value) {
270
            $data[$key] = (string)$value;
271
        }
272
273
        return $data;
274
    }
275
276
    /**
277
     * Check the parameters for the message
278
     *
279
     * @param array $parameters Parameters for the GCM request
280
     * @throws Exception
281
     * @return array $parameters
282
     */
283
    protected function _checkParameters(array $parameters = [])
284
    {
285
        if (!is_array($parameters)) {
286
            throw new Exception(__('Parameters must be an array.'));
287
        }
288
289
        $parameters = Hash::merge($this->config('parameters'), $parameters);
290
291
        if (isset($parameters['time_to_live']) && !is_int($parameters['time_to_live'])) {
292
            $parameters['time_to_live'] = (int)$parameters['time_to_live'];
293
        }
294
295
        if (isset($parameters['delay_while_idle']) && !is_bool($parameters['delay_while_idle'])) {
296
            $parameters['delay_while_idle'] = (bool)$parameters['delay_while_idle'];
297
        }
298
299
        if (isset($parameters['dry_run']) && !is_bool($parameters['dry_run'])) {
300
            $parameters['dry_run'] = (bool)$parameters['dry_run'];
301
        }
302
303
        return $parameters;
304
    }
305
306
    /**
307
     * Return options for the HTTP request
308
     *
309
     * @throws Exception
310
     * @return array $options
311
     */
312
    protected function _getHttpOptions()
313
    {
314
        if ($this->config('api.key') === null) {
315
            throw new Exception(__('No API key set. Push not triggered'));
316
        }
317
318
        $options = Hash::merge($this->config('http'), [
319
            'type' => 'json',
320
            'headers' => [
321
                'Authorization' => 'key=' . $this->config('api.key'),
322
                'Content-Type' => 'application/json'
323
            ]
324
        ]);
325
326
        return $options;
327
    }
328
}
329