Completed
Branch cake3 (9cfbb5)
by Romain
02:11
created

GcmComponent::_checkParameters()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 19
rs 8.2222
cc 7
eloc 10
nc 8
nop 1
1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4
 * Copyright (c) Romain Monteil
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright (c) Romain Monteil
11
 * @link          http://cakephp.org CakePHP(tm) Project
12
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
13
 */
14
namespace CakeGcm\Controller\Component;
15
16
use Cake\Controller\Component;
17
use Cake\Controller\ComponentRegistry;
18
use Cake\Network\Http\Client;
19
use Cake\Utility\Hash;
20
use \Exception;
21
22
/**
23
 * Gcm Component
24
 *
25
 */
26
class GcmComponent extends Component
27
{
28
29
	/**
30
	 * Default config
31
	 *
32
	 * @var array
33
	 */
34
	protected $_defaultConfig = [
35
		'api' => [
36
			'key' => null,
37
			'url' => 'https://gcm-http.googleapis.com/gcm/send'
38
		],
39
		'parameters' => [
40
			'collapse_key' => null,
41
			'priority' => 'normal',
42
			'delay_while_idle' => false,
43
			'dry_run' => false,
44
			'time_to_live' => 0,
45
			'restricted_package_name' => null
46
		]
47
	];
48
49
	/**
50
	 * List of parameters available to use in notification messages.
51
	 *
52
	 * @var array
53
	 */
54
	protected $_allowedNotificationParameters = [
55
		'title',
56
		'body',
57
		'icon',
58
		'sound',
59
		'badge',
60
		'tag',
61
		'color',
62
		'click_action',
63
		'body_loc_key',
64
		'body_loc_args',
65
		'title_loc_key',
66
		'title_loc_args'
67
	];
68
69
	/**
70
	 * Error code and message.
71
	 *
72
	 * @var array
73
	 */
74
	protected $_errorMessages = [];
75
76
	/**
77
	 * Response of the request
78
	 *
79
	 * @var object
80
	 */
81
	protected $_response = null;
82
83
	/**
84
	 * Constructor
85
	 *
86
	 * @param ComponentRegistry $registry A ComponentRegistry
87
	 * @param array $config Array of configuration settings
88
	 */
89
	public function __construct(ComponentRegistry $registry, array $config = [])
90
	{
91
		parent::__construct($registry, $config);
92
		$this->_errorMessages = [
93
			'400' => __('Error 400. The request could not be parsed as JSON.'),
94
			'401' => __('Error 401. Unable to authenticating the sender account.'),
95
			'500' => __('Error 500. Internal Server Error.'),
96
			'503' => __('Error 503. Service Unavailable.')
97
		];
98
	}
99
100
	/**
101
	 * send method
102
	 *
103
	 * @param string|array $ids
104
	 * @param array $payload
105
	 * @param array $parameters
106
	 * @throws Exception
107
	 * @return boolean
108
	 */
109
	public function send($ids = null, array $payload = [], array $parameters = [])
110
	{
111
		if (is_string($ids)) {
112
			$ids = (array)$ids;
113
		}
114
115
		if (is_null($ids) || !is_array($ids) || empty($ids)) {
116
			throw new Exception(__('Ids must be a string or an array with at least 1 token.'));
117
		}
118
119
		if (is_array($ids) && count($ids) > 1000) {
120
			throw new Exception(__('Ids must contain at least 1 and at most 1000 registration tokens.'));
121
		}
122
123
		if (!is_array($payload)) {
124
			throw new Exception(__('Payload must be an array.'));
125
		}
126
127
		if (!is_array($parameters)) {
128
			throw new Exception(__('Parameters must be an array.'));
129
		}
130
131 View Code Duplication
		if (isset($payload['notification'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
132
			$payload['notification'] = $this->_checkNotification($payload['notification']);
133
			if (!$payload['notification']) {
134
				throw new Exception(__("Unable to check notification."));
135
			}
136
		}
137
138 View Code Duplication
		if (isset($payload['data'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
139
			$payload['data'] = $this->_checkData($payload['data']);
140
			if (!$payload['data']) {
141
				throw new Exception(__("Unable to check data."));
142
			}
143
		}
144
145
		$parameters = $this->_checkParameters($parameters);
146
		$message = $this->_buildMessage($ids, $payload, $parameters);
147
148
		return $this->_executePush($message);
149
	}
150
151
	/**
152
	 * sendNotification method
153
	 *
154
	 * @param string|array $ids
155
	 * @param array $notification
156
	 * @param array $parameters
157
	 * @return boolean
158
	 */
159
	public function sendNotification($ids = null, array $notification = [], array $parameters = [])
160
	{
161
		return $this->send($ids, ['notification' => $notification], $parameters);
162
	}
163
164
	/**
165
	 * sendData method
166
	 *
167
	 * @param string|array $ids
168
	 * @param array $data
169
	 * @param array $parameters
170
	 * @return boolean
171
	 */
172
	public function sendData($ids = null, array $data = [], array $parameters = [])
173
	{
174
		return $this->send($ids, ['data' => $data], $parameters);
175
	}
176
177
	/**
178
	 * response method
179
	 *
180
	 * @return string
181
	 */
182
	public function response()
183
	{
184
		if (array_key_exists($this->_response->code, $this->_errorMessages)) {
185
			return $this->_errorMessages[$this->_response->code];
186
		}
187
188
		return json_decode($this->_response->body, true);
189
	}
190
191
	/**
192
	 * _executePush method
193
	 *
194
	 * @param string $message
195
	 * @throws Exception
196
	 * @return boolean
197
	 */
198
	protected function _executePush($message)
199
	{
200
		if ($this->config('api.key') === null) {
201
			throw new Exception(__('No API key set. Push not triggered'));
202
		}
203
204
		$http = new Client();
205
		$this->_response = $http->post($this->config('api.url'), $message, [
206
			'type' => 'json',
207
			'headers' => [
208
				'Authorization' => 'key=' . $this->config('api.key'),
209
				'Content-Type' => 'application/json'
210
			]
211
		]);
212
213
		if ($this->_response->code === '200') {
214
			return true;
215
		}
216
217
		return false;
218
	}
219
220
	/**
221
	 * _buildMessage method
222
	 *
223
	 * @param array|string $ids
224
	 * @param array $payload
225
	 * @param array $parameters
226
	 * @return string
227
	 */
228
	protected function _buildMessage($ids, $payload, $parameters)
229
	{
230
		$message = (count($ids) > 1) ? ['registration_ids' => $ids] : ['to' => current($ids)];
231
232
		if (!empty($payload)) {
233
			$message += $payload;
234
		}
235
236
		if (!empty($parameters)) {
237
			$message += $parameters;
238
		}
239
240
		return json_encode($message);
241
	}
242
243
	/**
244
	 * _checkNotification method
245
	 *
246
	 * @param array $notification
247
	 * @throws Exception
248
	 * @return array $notification
249
	 */
250
	protected function _checkNotification(array $notification = [])
251
	{
252
		if (!is_array($notification)) {
253
			throw new Exception('Notification must be an array.');
254
		}
255
256
		if (empty($notification) || !isset($notification['title'])) {
257
			throw new Exception('Notification\'s array must contain at least a key title.');
258
		}
259
260
		if (!isset($notification['icon'])) {
261
			$notification['icon'] = 'myicon';
262
		}
263
264
		foreach ($notification as $key => $value) {
265
			if (!in_array($key, $this->_allowedNotificationParameters)) {
266
				throw new Exception("The key {$key} is not allowed in notifications.");
267
			}
268
		}
269
270
		return $notification;
271
	}
272
273
	/**
274
	 * _checkData method
275
	 *
276
	 * @param array $data
277
	 * @throws Exception
278
	 * @return array $data
279
	 */
280
	public function _checkData(array $data = [])
281
	{
282
		if (!is_array($data)) {
283
			throw new Exception('Data must ba an array.');
284
		}
285
286
		if (empty($data)) {
287
			throw new Exception('Data\'s array can\'t be empty.');
288
		}
289
290
		// Convert all data into string
291
		foreach ($data as $key => $value) {
292
			$data[$key] = (string)$value;
293
		}
294
295
		return $data;
296
	}
297
298
	/**
299
	 * _checkParameters method
300
	 *
301
	 * @param array $parameters
302
	 * @return array $parameters
303
	 */
304
	protected function _checkParameters(array $parameters = [])
305
	{
306
		$parameters = Hash::merge($this->config('parameters'), $parameters);
307
		$parameters = array_filter($parameters);
308
309
		if (isset($parameters['time_to_live']) && !is_int($parameters['time_to_live'])) {
310
			$parameters['time_to_live'] = (int)$parameters['time_to_live'];
311
		}
312
313
		if (isset($parameters['delay_while_idle']) && !is_bool($parameters['delay_while_idle'])) {
314
			$parameters['delay_while_idle'] = (bool)$parameters['delay_while_idle'];
315
		}
316
317
		if (isset($parameters['dry_run']) && !is_bool($parameters['dry_run'])) {
318
			$parameters['dry_run'] = (bool)$parameters['dry_run'];
319
		}
320
321
		return $parameters;
322
	}
323
}
324