Completed
Branch cake3 (07b67b)
by Romain
02:07
created

GcmComponent::send()   C

Complexity

Conditions 13
Paths 36

Size

Total Lines 44
Code Lines 24

Duplication

Lines 12
Ratio 27.27 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 12
loc 44
rs 5.1234
cc 13
eloc 24
nc 36
nop 3

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
21
/**
22
 * Gcm Component
23
 *
24
 */
25
class GcmComponent extends Component
26
{
27
28
	/**
29
	 * Default config
30
	 *
31
	 * @var array
32
	 */
33
	protected $_defaultConfig = [
34
		'api' => [
35
			'key' => null,
36
			'url' => 'https://gcm-http.googleapis.com/gcm/send'
37
		],
38
		'parameters' => [
39
			'collapse_key' => null,
40
			'priority' => 'normal',
41
			'delay_while_idle' => false,
42
			'dry_run' => false,
43
			'time_to_live' => 0,
44
			'restricted_package_name' => null
45
		]
46
	];
47
48
	/**
49
	 * Error code and message.
50
	 *
51
	 * @var array
52
	 */
53
	protected $_errorMessages = [];
54
55
	/**
56
	 * Response of the request
57
	 *
58
	 * @var object
59
	 */
60
	protected $_response = null;
61
62
	/**
63
	 * Constructor
64
	 *
65
	 * @param ComponentRegistry $collection A ComponentRegistry
0 ignored issues
show
Bug introduced by
There is no parameter named $collection. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
66
	 * @param array $config Array of configuration settings
67
	 */
68
	public function __construct(ComponentRegistry $registry, array $config = [])
69
	{
70
		$this->_errorMessages = [
71
			'400' => __('Error 400. The request could not be parsed as JSON.'),
72
			'401' => __('Error 401. Unable to authenticating the sender account.'),
73
			'500' => __('Error 500. Internal Server Error.'),
74
			'503' => __('Error 503. Service Unavailable.')
75
		];
76
	}
77
78
	/**
79
	 * send method
80
	 *
81
	 * @param string|array $ids
82
	 * @param array $payload
83
	 * @param array $parameters
84
	 * @return boolean
85
	 */
86
	public function send($ids = false, array $payload = [], array $parameters = [])
87
	{
88
		if (is_string($ids)) {
89
			$ids = (array)$ids;
90
		}
91
92
		if ($ids === false || !is_array($ids) || empty($ids)) {
93
			throw new \LogicException(__('Ids must be a string or an array.'));
94
		}
95
96
		if (!is_array($payload)) {
97
			throw new \LogicException(__('Data must be an array.'));
98
		}
99
100
		if (!is_array($parameters)) {
101
			throw new \LogicException(__('Parameters must be an array.'));
102
		}
103
104 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...
105
			$payload['notification'] = $this->_checkNotification($payload['notification']);
106
			if (!$payload['notification']) {
107
				throw new \LogicException(__("Unable to check notification."));
108
			}
109
		}
110
111 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...
112
			$payload['data'] = $this->_checkData($payload['data']);
113
			if (!$payload['data']) {
114
				throw new \LogicException(__("Unable to check data."));
115
			}
116
		}
117
118
		$parameters = $this->_checkParameters($parameters);
119
		if (!$parameters) {
120
			throw new \ErrorException(__('Unable to check parameters.'));
121
		}
122
123
		$message = $this->_buildMessage($ids, $payload, $parameters);
124
		if ($message === false) {
125
			throw new \ErrorException(__('Unable to build the message.'));
126
		}
127
128
		return $this->_executePush($message);
0 ignored issues
show
Documentation introduced by
$message is of type string, but the function expects a false|object<CakeGcm\Controller\Component\json>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
129
	}
130
131
	/**
132
	 * sendNotification method
133
	 *
134
	 * @param string|array $ids
135
	 * @param array $notification
136
	 * @param array $parameters
137
	 * @return void
138
	 */
139
	public function sendNotification($ids = false, array $notification = [], array $parameters = []) 
140
	{
141
		return $this->send($ids, ['notification' => $notification], $parameters);
142
	}
143
144
	/**
145
	 * sendData method
146
	 *
147
	 * @param string|array $ids
148
	 * @param array $data
149
	 * @param array $parameters
150
	 * @return void
151
	 */
152
	public function sendData($ids = false, array $data = [], array $parameters = []) 
153
	{
154
		return $this->send($ids, ['data' => $data], $parameters);
155
	}
156
157
	/**
158
	 * response method
159
	 *
160
	 * @return void
161
	 */
162
	public function response()
163
	{
164
		if (array_key_exists($this->_response->code, $this->_errorMessages)) {
165
			return $this->_errorMessages[$this->_response->code];
166
		}
167
168
		return json_decode($this->_response->body, true);
169
	}
170
171
	/**
172
	 * _executePush method
173
	 *
174
	 * @param json $message
175
	 * @return boolean
176
	 */
177
	protected function _executePush($message = false)
178
	{
179
		if ($message === false) {
180
			return false;
181
		}
182
183
		if ($this->_config['api']['key'] === null) {
184
			throw new \ErrorException(__('No API key set. Push not triggered'));
185
		}
186
187
		$http = new Client();
188
		$this->_response = $http->post($this->_config['api']['url'], $message, [
189
			'type' => 'json',
190
			'header' => [
191
				'Authorization' => 'key=' . $this->config['api']['key'],
0 ignored issues
show
Bug introduced by
The property config does not seem to exist. Did you mean _defaultConfig?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
192
				'Content-Type' => 'application/json'
193
			]
194
		]);
195
196
		if ($this->_response->code === '200') {
197
			return true;
198
		}
199
200
		return false;
201
	}
202
203
	/**
204
	 * _buildMessage method
205
	 *
206
	 * @param array $ids
207
	 * @param array $payload
208
	 * @param array $parameters
209
	 * @return json
210
	 */
211
	protected function _buildMessage($ids = false, $payload = false, $parameters = false)
212
	{
213
		if ($ids === false) {
214
			return false;
215
		}
216
217
		$message = ['registration_ids' => $ids];
218
219
		if (!empty($payload)) {
220
			$message += $payload;
221
		}
222
223
		if (!empty($parameters)) {
224
			$message += $parameters;
225
		}
226
227
		return json_encode($message);
228
	}
229
230
	/**
231
	 * _checkNotification method
232
	 *
233
	 * @param array $notification
234
	 * @return array $notification
235
	 */
236
	protected function _checkNotification($notification = false) 
237
	{
238
		if ($notification === false) {
239
			return false;
240
		}
241
242
		if (!is_array($notification)) {
243
			throw new \LogicException("Notification must be an array.");
244
		}
245
246
		if (empty($notification) || !isset($notification['title'])) {
247
			throw new \LogicException("Notification's array must contain at least a key title.");
248
		}
249
250
		if (!isset($notification['icon'])) {
251
			$notification['icon'] = 'myicon';
252
		}
253
254
		return $notification;
255
	}
256
257
	/**
258
	 * _checkData method
259
	 *
260
	 * @param array $data
261
	 * @return array $data
262
	 */
263
	public function _checkData($data = false) 
264
	{
265
		if ($data === false) {
266
			return false;
267
		}
268
269
		if (!is_array($data)) {
270
			throw new \LogicException("Data must ba an array.");
271
		}
272
273
		if (empty($data)) {
274
			throw new \LogicException("Data's array can't be empty.");
275
		}
276
277
		// Convert all data into string
278
		foreach ($data as $key => $value) {
279
			$data[$key] = (string)$value;
280
		}
281
282
		return $data;
283
	}
284
285
	/**
286
	 * _checkParameters method
287
	 *
288
	 * @param array $parameters
289
	 * @return array $parameters
290
	 */
291
	protected function _checkParameters($parameters = false)
292
	{
293
		if ($parameters === false) {
294
			return false;
295
		}
296
297
		$parameters = Hash::merge($this->_config['parameters'], $parameters);
298
		$parameters = array_filter($parameters);
299
300
		if (isset($parameters['time_to_live']) && !is_int($parameters['time_to_live'])) {
301
			$parameters['time_to_live'] = (int)$parameters['time_to_live'];
302
		}
303
304
		if (isset($parameters['delay_while_idle']) && !is_bool($parameters['delay_while_idle'])) {
305
			$parameters['delay_while_idle'] = (bool)$parameters['delay_while_idle'];
306
		}
307
308
		if (isset($parameters['dry_run']) && !is_bool($parameters['dry_run'])) {
309
			$parameters['dry_run'] = (bool)$parameters['dry_run'];
310
		}
311
312
		return $parameters;
313
	}
314
}
315