Completed
Push — master ( 9a3a3c...24148b )
by Michael
14s
created

StatsJsonView   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 324
Duplicated Lines 2.47 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 37
lcom 1
cbo 2
dl 8
loc 324
ccs 104
cts 104
cp 1
rs 9.44
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A isAuthorizedRaw() 0 4 1
A isRecent() 0 4 1
C render() 0 63 10
A setSource() 0 4 1
A buildResponseData() 0 27 5
A processSingleSource() 0 30 4
C sanitizeData() 8 84 14

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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\BaseJsonView;
13
14
/**
15
 * JSON response for requesting the stats data.
16
 */
17
class StatsJsonView extends BaseJsonView
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'];
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
122
		// If we have the entire database, we have to loop within each group to put it all together
123 5
		foreach ($items as $group)
124
		{
125 5
			$this->totalItems = 0;
126
127 5
			foreach ($group as $item)
128
			{
129 5
				foreach ($this->dataSources as $source)
130
				{
131 5
					if (isset($item[$source]) && $item[$source] !== null)
132
					{
133
						// Special case, if the server is empty then change the key to "unknown"
134 5
						if ($source === 'server_os' && empty($item[$source]))
135
						{
136 3
							$item[$source] = 'unknown';
137
						}
138
139 5
						${$source}[$item[$source]] = $item['count'];
140
141 5
						$this->totalItems += $item['count'];
142
					}
143
				}
144
			}
145
		}
146
147
		$data = [
148 5
			'php_version' => $php_version,
149 5
			'db_type'     => $db_type,
150 5
			'db_version'  => $db_version,
151 5
			'cms_version' => $cms_version,
152 5
			'server_os'   => $server_os,
153
		];
154
155 5
		$responseData = $this->buildResponseData($data);
156
157 5
		$responseData['total'] = $this->totalItems;
158
159 5
		$this->addData('data', $responseData);
160
161 5
		return parent::render();
162
	}
163
164
	/**
165
	 * Set the data source.
166
	 *
167
	 * @param   string  $source  Data source to return.
168
	 *
169
	 * @return  void
170
	 */
171 5
	public function setSource(string $source): void
172
	{
173 5
		$this->source = $source;
174 5
	}
175
176
	/**
177
	 * Process the raw data into the response data format.
178
	 *
179
	 * @param   array  $data  The raw data array.
180
	 *
181
	 * @return  array
182
	 */
183 8
	private function buildResponseData(array $data): array
184
	{
185 8
		$responseData = [];
186
187 8
		foreach ($data as $key => $value)
188
		{
189 8
			foreach ($value as $name => $count)
190
			{
191 8
				if ($name)
192
				{
193 8
					$responseData[$key][] = [
194 8
						'name'  => $name,
195 8
						'count' => $count,
196
					];
197
				}
198
			}
199
		}
200
201 8
		unset($data);
202
203 8
		if (!$this->authorizedRaw)
204
		{
205 6
			$responseData = $this->sanitizeData($responseData);
206
		}
207
208 8
		return $responseData;
209
	}
210
211
	/**
212
	 * Process the response for a single data source.
213
	 *
214
	 * @param   array  $items  The source items to process.
215
	 *
216
	 * @return  string  The rendered view.
217
	 */
218 3
	private function processSingleSource(array $items): string
219
	{
220
		$data = [
221 3
			${$this->source} = [],
222
		];
223
224 3
		$this->totalItems = 0;
225
226 3
		foreach ($items as $item)
227
		{
228
			// Special case, if the server is empty then change the key to "unknown"
229 3
			if ($this->source === 'server_os' && empty(trim($item[$this->source])))
230
			{
231 1
				$item[$this->source] = 'unknown';
232
			}
233
234 3
			$data[$this->source][$item[$this->source]] = $item['count'];
235 3
			$this->totalItems += $item['count'];
236
		}
237
238 3
		unset($generator);
239
240 3
		$responseData = $this->buildResponseData($data);
241
242 3
		$responseData['total'] = $this->totalItems;
243
244 3
		$this->addData('data', $responseData);
245
246 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...
247
	}
248
249
	/**
250
	 * Sanitize the response data into summarized groups.
251
	 *
252
	 * @param   array  $responseData  The response data to sanitize.
253
	 *
254
	 * @return  array
255
	 */
256 6
	private function sanitizeData(array $responseData): array
257
	{
258 6
		foreach ($responseData as $key => $dataGroup)
259
		{
260 6
			switch ($key)
261
			{
262 6
				case 'php_version':
263 4
				case 'db_version':
264 4
				case 'cms_version':
265
					// We're going to group by minor version branch here and convert to a percentage
266 5
					$counts = [];
267
268 5
					foreach ($dataGroup as $row)
269
					{
270 5
						$exploded = explode('.', $row['name']);
271 5
						$version  = $exploded[0] . '.' . ($exploded[1] ?? '0');
272
273
						// If the container does not exist, add it
274 5
						if (!isset($counts[$version]))
275
						{
276 5
							$counts[$version] = 0;
277
						}
278
279 5
						$counts[$version] += $row['count'];
280
					}
281
282 5
					$sanitizedData = [];
283
284 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...
285
					{
286 5
						$sanitizedData[$version] = round(($count / $this->totalItems) * 100, 2);
287
					}
288
289 5
					$responseData[$key] = $sanitizedData;
290
291 5
					break;
292
293 4
				case 'server_os':
294
					// We're going to group by operating system here
295 4
					$counts = [];
296
297 4
					foreach ($dataGroup as $row)
298
					{
299 4
						$fullOs = explode(' ', $row['name']);
300 4
						$os     = $fullOs[0];
301
302
						// If the container does not exist, add it
303 4
						if (!isset($counts[$os]))
304
						{
305 4
							$counts[$os] = 0;
306
						}
307
308 4
						$counts[$os] += $row['count'];
309
					}
310
311 4
					$sanitizedData = [];
312
313 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...
314
					{
315 4
						$sanitizedData[$os] = round(($count / $this->totalItems) * 100, 2);
316
					}
317
318 4
					$responseData[$key] = $sanitizedData;
319
320 4
					break;
321
322 3
				case 'db_type':
323
				default:
324
					// For now, group by the object name and figure out the percentages
325 3
					$sanitizedData = [];
326
327 3
					foreach ($dataGroup as $row)
328
					{
329 3
						$sanitizedData[$row['name']] = round(($row['count'] / $this->totalItems) * 100, 2);
330
					}
331
332 3
					$responseData[$key] = $sanitizedData;
333
334 3
					break;
335
			}
336
		}
337
338 6
		return $responseData;
339
	}
340
}
341