Completed
Push — master ( 6ad9be...50614b )
by George
02:18
created

StatsJsonView::processSingleSource()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 6.4976

Importance

Changes 0
Metric Value
dl 0
loc 47
ccs 19
cts 25
cp 0.76
rs 8.5341
c 0
b 0
f 0
cc 6
nc 6
nop 1
crap 6.4976
1
<?php
2
/**
3
 * Joomla! Statistics Server
4
 *
5
 * @copyright  Copyright (C) 2013 - 2017 Open Source Matters, Inc. All rights reserved.
6
 * @license    http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License Version 2 or Later
7
 */
8
9
namespace Joomla\StatsServer\Views\Stats;
10
11
use Joomla\StatsServer\Repositories\StatisticsRepository;
12
use Joomla\View\JsonView;
13
14
/**
15
 * JSON response for requesting the stats data.
16
 */
17
class StatsJsonView extends JsonView
18
{
19
	/**
20
	 * Flag if the response should return the raw data.
21
	 *
22
	 * @var  boolean
23
	 */
24
	private $authorizedRaw = false;
25
26
	/**
27
	 * Array holding the valid data sources.
28
	 *
29
	 * @var  array
30
	 */
31
	private $dataSources = ['php_version', 'db_type', 'db_version', 'cms_version', 'server_os', 'cms_php_version', 'db_type_version'];
32
33
	/**
34
	 * Flag if the response should return the recently updated data.
35
	 *
36
	 * @var  boolean
37
	 */
38
	private $recent = false;
39
40
	/**
41
	 * Statistics repository.
42
	 *
43
	 * @var  StatisticsRepository
44
	 */
45
	private $repository;
46
47
	/**
48
	 * The data source to return.
49
	 *
50
	 * @var  string
51
	 */
52
	private $source = '';
53
54
	/**
55
	 * Count of the number of items.
56
	 *
57
	 * @var  integer
58
	 */
59
	private $totalItems = 0;
60
61
	/**
62
	 * Instantiate the view.
63
	 *
64
	 * @param   StatisticsRepository  $repository  Statistics repository.
65
	 */
66 8
	public function __construct(StatisticsRepository $repository)
67
	{
68 8
		$this->repository = $repository;
69 8
	}
70
71
	/**
72
	 * Set whether the raw data should be returned.
73
	 *
74
	 * @param   bool  $authorizedRaw  Flag if the response should return the raw data.
75
	 *
76
	 * @return  void
77
	 */
78 4
	public function isAuthorizedRaw(bool $authorizedRaw): void
79
	{
80 4
		$this->authorizedRaw = $authorizedRaw;
81 4
	}
82
83
	/**
84
	 * Set whether the recently updated data should be returned.
85
	 *
86
	 * @param   bool  $recent  Flag if the response should return the recently updated data.
87
	 *
88
	 * @return  void
89
	 */
90 1
	public function isRecent(bool $recent): void
91
	{
92 1
		$this->recent = $recent;
93 1
	}
94
95
	/**
96
	 * Method to render the view.
97
	 *
98
	 * @return  string  The rendered view.
99
	 */
100 8
	public function render()
101
	{
102 8
		if ($this->recent)
103
		{
104 1
			$items = $this->repository->getRecentlyUpdatedItems();
105
		}
106
		else
107
		{
108 7
			$items = $this->repository->getItems($this->source);
109
		}
110
111 8
		if ($this->source)
112
		{
113 3
			return $this->processSingleSource($items);
114
		}
115
116 5
		$php_version     = [];
117 5
		$db_type         = [];
118 5
		$db_version      = [];
119 5
		$cms_version     = [];
120 5
		$server_os       = [];
121 5
		$cms_php_version = [];
122 5
		$db_type_version = [];
123
124
		// If we have the entire database, we have to loop within each group to put it all together
125 5
		foreach ($items as $group)
126
		{
127 5
			$this->totalItems = 0;
128
129 5
			foreach ($group as $item)
130
			{
131 5
				foreach ($this->dataSources as $source)
132
				{
133 5
					switch ($source)
134
					{
135 5
						case 'server_os':
136 5
							if (isset($item[$source]) && $item[$source] !== null)
137
							{
138
								// Special case, if the server is empty then change the key to "unknown"
139 5
								if (empty($item[$source]))
140
								{
141 3
									$item[$source] = 'unknown';
142
								}
143
144 5
								${$source}[$item[$source]] = $item['count'];
145
146 5
								$this->totalItems += $item['count'];
147
							}
148
149 5
							break;
150
151 5 View Code Duplication
						case 'cms_php_version':
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...
152 5
							if ((isset($item['cms_version']) && $item['cms_version'] !== null) && (isset($item['php_version']) && $item['php_version'] !== null))
153
							{
154 2
								$index = $item['cms_version'] . ' - ' . $item['php_version'];
155 2
								$cms_php_version[$index] = $item['count'];
156
157 2
								$this->totalItems += $item['count'];
158
							}
159
160 5
							break;
161
162 5 View Code Duplication
						case 'db_type_version':
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...
163 5
							if ((isset($item['db_type']) && $item['db_type'] !== null) && (isset($item['db_version']) && $item['db_version'] !== null))
164
							{
165 2
								$index = $item['db_type'] . ' - ' . $item['db_version'];
166 2
								$db_type_version[$index] = $item['count'];
167
168 2
								$this->totalItems += $item['count'];
169
							}
170
171 5
							break;
172
173
						default:
174 5
							if (isset($item[$source]) && $item[$source] !== null)
175
							{
176 5
								${$source}[$item[$source]] = $item['count'];
177 5
								$this->totalItems += $item['count'];
178
							}
179
180 5
							break;
181
					}
182
				}
183
			}
184
		}
185
186
		$data = [
187 5
			'php_version'     => $php_version,
188 5
			'db_type'         => $db_type,
189 5
			'db_version'      => $db_version,
190 5
			'cms_version'     => $cms_version,
191 5
			'server_os'       => $server_os,
192 5
			'cms_php_version' => $cms_php_version,
193 5
			'db_type_version' => $db_type_version,
194
		];
195
196 5
		$responseData = $this->buildResponseData($data);
197
198 5
		$responseData['total'] = $this->totalItems;
199
200 5
		$this->addData('data', $responseData);
201
202 5
		return parent::render();
203
	}
204
205
	/**
206
	 * Set the data source.
207
	 *
208
	 * @param   string  $source  Data source to return.
209
	 *
210
	 * @return  void
211
	 */
212 5
	public function setSource(string $source): void
213
	{
214 5
		$this->source = $source;
215 5
	}
216
217
	/**
218
	 * Process the raw data into the response data format.
219
	 *
220
	 * @param   array  $data  The raw data array.
221
	 *
222
	 * @return  array
223
	 */
224 8
	private function buildResponseData(array $data): array
225
	{
226 8
		$responseData = [];
227
228 8
		foreach ($data as $key => $value)
229
		{
230 8
			foreach ($value as $name => $count)
231
			{
232 8
				if ($name)
233
				{
234 8
					$responseData[$key][] = [
235 8
						'name'  => $name,
236 8
						'count' => $count,
237
					];
238
				}
239
			}
240
		}
241
242 8
		unset($data);
243
244 8
		if (!$this->authorizedRaw)
245
		{
246 6
			$responseData = $this->sanitizeData($responseData);
247
		}
248
249 8
		return $responseData;
250
	}
251
252
	/**
253
	 * Process the response for a single data source.
254
	 *
255
	 * @param   array  $items  The source items to process.
256
	 *
257
	 * @return  string  The rendered view.
258
	 */
259 3
	private function processSingleSource(array $items): string
260
	{
261
		$data = [
262 3
			${$this->source} = [],
263
		];
264
265 3
		$this->totalItems = 0;
266
267 3
		foreach ($items as $item)
268
		{
269 3
			switch ($this->source)
270
			{
271 3
				case 'server_os':
272
					// Special case, if the server is empty then change the key to "unknown"
273 1
					if (empty(trim($item[$this->source])))
274
					{
275 1
						$item[$this->source] = 'unknown';
276
					}
277 1
					$data[$this->source][$item[$this->source]] = $item['count'];
278 1
					break;
279
280 2
				case 'cms_php_version':
281
					$index = $item['cms_version'] . ' - ' . $item['php_version'];
282
					$data[$this->source][$index] = $item['count'];
283
					break;
284
285 2
				case 'db_type_version':
286
					$index = $item['db_type'] . ' - ' . $item['db_version'];
287
					$data[$this->source][$index] = $item['count'];
288
					break;
289
290
				default:
291 2
					$data[$this->source][$item[$this->source]] = $item['count'];
292 2
					break;
293
			}
294
295 3
			$this->totalItems += $item['count'];
296
		}
297
298 3
		$responseData = $this->buildResponseData($data);
299
300 3
		$responseData['total'] = $this->totalItems;
301
302 3
		$this->addData('data', $responseData);
303
304 3
		return parent::render();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (render() instead of processSingleSource()). Are you sure this is correct? If so, you might want to change this to $this->render().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
305
	}
306
307
	/**
308
	 * Sanitize the response data into summarized groups.
309
	 *
310
	 * @param   array  $responseData  The response data to sanitize.
311
	 *
312
	 * @return  array
313
	 */
314 6
	private function sanitizeData(array $responseData): array
315
	{
316 6
		foreach ($responseData as $key => $dataGroup)
317
		{
318 6
			switch ($key)
319
			{
320 6
				case 'php_version':
321 4
				case 'db_version':
322 4
				case 'cms_version':
323
					// We're going to group by minor version branch here and convert to a percentage
324 5
					$counts = [];
325
326 5
					foreach ($dataGroup as $row)
327
					{
328 5
						$exploded = explode('.', $row['name']);
329 5
						$version  = $exploded[0] . '.' . ($exploded[1] ?? '0');
330
331
						// If the container does not exist, add it
332 5
						if (!isset($counts[$version]))
333
						{
334 5
							$counts[$version] = 0;
335
						}
336
337 5
						$counts[$version] += $row['count'];
338
					}
339
340 5
					$sanitizedData = [];
341
342 5 View Code Duplication
					foreach ($counts as $version => $count)
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...
343
					{
344 5
						$sanitizedData[$version] = round(($count / $this->totalItems) * 100, 2);
345
					}
346
347 5
					$responseData[$key] = $sanitizedData;
348
349 5
					break;
350
351 4
				case 'server_os':
352
					// We're going to group by operating system here
353 4
					$counts = [];
354
355 4
					foreach ($dataGroup as $row)
356
					{
357 4
						$fullOs = explode(' ', $row['name']);
358 4
						$os     = $fullOs[0];
359
360
						// If the container does not exist, add it
361 4
						if (!isset($counts[$os]))
362
						{
363 4
							$counts[$os] = 0;
364
						}
365
366 4
						$counts[$os] += $row['count'];
367
					}
368
369 4
					$sanitizedData = [];
370
371 4 View Code Duplication
					foreach ($counts as $os => $count)
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...
372
					{
373 4
						$sanitizedData[$os] = round(($count / $this->totalItems) * 100, 2);
374
					}
375
376 4
					$responseData[$key] = $sanitizedData;
377
378 4
					break;
379
380 3
				case 'db_type':
381 1
				case 'cms_php_version':
382 1
				case 'db_type_version':
383
				default:
384
					// For now, group by the object name and figure out the percentages
385 3
					$sanitizedData = [];
386
387 3
					foreach ($dataGroup as $row)
388
					{
389 3
						$sanitizedData[$row['name']] = round(($row['count'] / $this->totalItems) * 100, 2);
390
					}
391
392 3
					$responseData[$key] = $sanitizedData;
393
394 3
					break;
395
			}
396
		}
397
398 6
		return $responseData;
399
	}
400
}
401