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