Completed
Branch develop (f935f6)
by Romain
01:43
created

Fcm::getTokens()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
namespace ker0x\Push\Adapter\Fcm;
3
4
use Cake\Core\Configure;
5
use Cake\Core\InstanceConfigTrait;
6
use Cake\Http\Client;
7
use Cake\Utility\Hash;
8
use ker0x\Push\Adapter\Exception\InvalidAdapterException;
9
use ker0x\Push\Adapter\Fcm\Message\Exception\InvalidDataException;
10
use ker0x\Push\Adapter\Fcm\Message\Exception\InvalidNotificationException;
11
use ker0x\Push\Adapter\Fcm\Message\Exception\InvalidTokenException;
12
use ker0x\Push\Adapter\Fcm\Message\Options;
13
use ker0x\Push\Adapter\Fcm\Message\OptionsBuilder;
14
15
class Fcm
16
{
17
18
    use InstanceConfigTrait;
19
20
    /**
21
     * Array for devices's token
22
     *
23
     * @var array
24
     */
25
    protected $tokens = [];
26
27
    /**
28
     * Array for the notification
29
     *
30
     * @var array
31
     */
32
    protected $notification = [];
33
34
    /**
35
     * Array of data
36
     *
37
     * @var array
38
     */
39
    protected $data = [];
40
41
    /**
42
     * Array of request options
43
     *
44
     * @var array
45
     */
46
    protected $options = [];
47
48
    /**
49
     * Array of payload
50
     *
51
     * @var array
52
     */
53
    protected $payload = [];
54
55
    /**
56
     * Response of the request
57
     *
58
     * @var \Cake\Http\Client\Response
59
     */
60
    protected $response;
61
62
    /**
63
     * Default config
64
     *
65
     * @var array
66
     */
67
    protected $_defaultConfig = [
68
        'options' => [
69
            'collapse_key' => null,
70
            'priority' => 'normal',
71
            'dry_run' => false,
72
            'time_to_live' => 0,
73
            'restricted_package_name' => null
74
        ],
75
        'http' => []
76
    ];
77
78
    /**
79
     * List of keys allowed to be used in notification array.
80
     *
81
     * @var array
82
     */
83
    protected $_allowedNotificationKeys = [
84
        'title',
85
        'body',
86
        'icon',
87
        'sound',
88
        'badge',
89
        'tag',
90
        'color',
91
        'click_action',
92
        'body_loc_key',
93
        'body_loc_args',
94
        'title_loc_key',
95
        'title_loc_args',
96
    ];
97
98
    /**
99
     * FcmAdapter constructor.
100
     *
101
     * @throws \ker0x\Push\Adapter\Exception\InvalidAdapterException
102
     */
103
    public function __construct()
104
    {
105
        $config = Configure::read('Push.adapters.Fcm');
106
        $this->config($config);
107
108
        if ($this->config('api.key') === null) {
109
            throw InvalidAdapterException::noApiKey();
110
        }
111
    }
112
113
    /**
114
     * Getter for tokens
115
     *
116
     * @return array
117
     */
118
    public function getTokens()
119
    {
120
        return $this->tokens;
121
    }
122
123
    /**
124
     * Setter for tokens
125
     *
126
     * @param array $tokens Array of devices's token
127
     * @return $this
128
     */
129
    public function setTokens(array $tokens)
130
    {
131
        $this->_checkTokens($tokens);
132
        $this->tokens = $tokens;
133
134
        return $this;
135
    }
136
137
    /**
138
     * Getter for payload notification
139
     *
140
     * @return array
141
     */
142
    public function getNotification()
143
    {
144
        return $this->notification;
145
    }
146
147
    /**
148
     * Setter for payload notification
149
     *
150
     * Authorized keys for the notification are:
151
     *
152
     * - `title` Indicates notification title.
153
     * - `body` Indicates notification body text.
154
     * - `badge` Indicates the badge on the client app home icon. (iOS)
155
     * - `icon` Indicates notification icon. (Android)
156
     * - `sound` Indicates a sound to play when the device receives a notification.
157
     * - `tag` Indicates whether each notification results in a new entry in the
158
     *   notification drawer on Android. (Android)
159
     * - `color` Indicates color of the icon, expressed in #rrggbb format. (Android)
160
     * - `click_action` Indicates the action associated with a user click on the notification.
161
     * - `body_loc_key` Indicates the key to the body string for localization.
162
     * - `body_loc_args` Indicates the string value to replace format specifiers in the
163
     *   body string for localization.
164
     * - `title_loc_key` Indicates the key to the title string for localization.
165
     * - `title_loc_args` Indicates the string value to replace format specifiers in
166
     *   the title string for localization.
167
     *
168
     * @param array $notification Array of keys for the notification
169
     * @return $this
170
     */
171
    public function setNotification(array $notification)
172
    {
173
        $this->_checkNotification($notification);
174
        if (!isset($notification['icon'])) {
175
            $notification['icon'] = 'myicon';
176
        }
177
        $this->notification = $notification;
178
179
        return $this;
180
    }
181
182
    /**
183
     * Getter for payload data
184
     *
185
     * @return array
186
     */
187
    public function getData()
188
    {
189
        return $this->data;
190
    }
191
192
    /**
193
     * Setter for payload data
194
     *
195
     * @param array $data Array of data for the push
196
     * @return $this
197
     */
198
    public function setData(array $data)
199
    {
200
        $this->_checkData($data);
201
        foreach ($data as $key => $value) {
202
            if (is_bool($value)) {
203
                $value = ($value) ? 'true' : 'false';
204
            }
205
            $data[$key] = (string)$value;
206
        }
207
        $this->data = $data;
208
209
        return $this;
210
    }
211
212
    /**
213
     * Getter for payload options
214
     *
215
     * @return array
216
     */
217
    public function getOptions()
218
    {
219
        return $this->options;
220
    }
221
222
    /**
223
     * Setter for payload options
224
     *
225
     * Authorized keys for options's array are:
226
     *
227
     * - `collapse_key` This parameter identifies a group of messages.
228
     * - `priority` Sets the priority of the message.
229
     * - `content_available` When a notification or message is sent and
230
     *   this is set to true, an inactive client app is awoken.
231
     * - `time_to_live` This parameter specifies how long (in seconds)
232
     *   the message should be kept in FCM storage if the device is offline.
233
     * - `restricted_package_name` This parameter specifies the package name
234
     *   of the application where the registration tokens must match in order
235
     *   to receive the message.
236
     * - `dry_run` This parameter, when set to true, allows developers to test
237
     *   a request without actually sending a message.
238
     *
239
     * @param array|OptionsBuilder $options Options for the push
240
     * @return $this
241
     */
242
    public function setOptions($options)
243
    {
244
        $this->options = (new Options($options))->build();
245
246
        return $this;
247
    }
248
249
    /**
250
     * Getter for payload
251
     *
252
     * @return array
253
     */
254
    public function getPayload()
255
    {
256
        $notification = $this->getNotification();
257
        if (!empty($notification)) {
258
            $this->payload['notification'] = $notification;
259
        }
260
261
        $data = $this->getData();
262
        if (!empty($data)) {
263
            $this->payload['data'] = $data;
264
        }
265
266
        return $this->payload;
267
    }
268
269
    /**
270
     * Check tokens's array
271
     *
272
     * @param array $tokens Token's array
273
     * @return void
274
     * @throws \ker0x\Push\Adapter\Fcm\Message\Exception\InvalidTokenException
275
     */
276
    private function _checkTokens($tokens)
277
    {
278
        if (empty($tokens) || count($tokens) > 1000) {
279
            throw new InvalidTokenException("Array must contain at least 1 and at most 1000 tokens.");
280
        }
281
    }
282
283
    /**
284
     * Check notification's array
285
     *
286
     * @param array $notification Notification's array
287
     * @return void
288
     * @throws \ker0x\Push\Adapter\Fcm\Message\Exception\InvalidNotificationException
289
     */
290
    private function _checkNotification($notification)
291
    {
292
        if (empty($notification) || !isset($notification['title'])) {
293
            throw new InvalidNotificationException("Array must contain at least a key title.");
294
        }
295
296
        $notAllowedKeys = [];
297
        foreach ($notification as $key => $value) {
298
            if (!in_array($key, $this->_allowedNotificationKeys)) {
299
                $notAllowedKeys[] = $key;
300
            }
301
        }
302
303
        if (!empty($notAllowedKeys)) {
304
            $notAllowedKeys = implode(', ', $notAllowedKeys);
305
            throw new InvalidNotificationException("The following keys are not allowed: {$notAllowedKeys}");
306
        }
307
    }
308
309
    /**
310
     * Check data's array
311
     *
312
     * @param array $data Datas's array
313
     * @return void
314
     * @throws \ker0x\Push\Adapter\Fcm\Message\Exception\InvalidDataException
315
     */
316
    private function _checkData($data)
317
    {
318
        if (empty($data)) {
319
            throw new InvalidDataException("Array can not be empty.");
320
        }
321
    }
322
323
    /**
324
     * Build the message
325
     *
326
     * @return string
327
     */
328
    protected function _getMessage()
329
    {
330
        $tokens = $this->getTokens();
331
        $message = (count($tokens) > 1) ? ['registration_ids' => $tokens] : ['to' => current($tokens)];
332
333
        $payload = $this->getPayload();
334
        if (!empty($payload)) {
335
            $message += $payload;
336
        }
337
338
        $options = $this->getOptions();
339
        if (!empty($options)) {
340
            $message += $options;
341
        }
342
343
        return json_encode($message);
344
    }
345
346
    /**
347
     * Return options for the HTTP request
348
     *
349
     * @return array $options
350
     */
351
    protected function _getHttpOptions()
352
    {
353
        $options = Hash::merge($this->config('http'), [
354
            'type' => 'json',
355
            'headers' => [
356
                'Authorization' => 'key=' . $this->config('api.key'),
357
                'Content-Type' => 'application/json'
358
            ]
359
        ]);
360
361
        return $options;
362
    }
363
}
364