Completed
Push — master ( 55ea86...34c5f6 )
by Michael
01:51
created

StatsJsonView::setSource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
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\Models\StatsModel;
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
	 * The data source to return.
35
	 *
36
	 * @var  string
37
	 */
38
	private $source = '';
39
40
	/**
41
	 * Count of the number of items.
42
	 *
43
	 * @var  integer
44
	 */
45
	private $totalItems = 0;
46
47
	/**
48
	 * Instantiate the view.
49
	 *
50
	 * @param   StatsModel  $model  The model object.
51
	 */
52
	public function __construct(StatsModel $model)
53
	{
54
		$this->model = $model;
0 ignored issues
show
Bug introduced by
The property model does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
55
	}
56
57
	/**
58
	 * Set whether the raw data should be returned.
59
	 *
60
	 * @param   bool  $authorizedRaw  Flag if the response should return the raw data.
61
	 *
62
	 * @return  void
63
	 */
64 1
	public function isAuthorizedRaw(bool $authorizedRaw)
65
	{
66 1
		$this->authorizedRaw = $authorizedRaw;
67 1
	}
68
69
	/**
70
	 * Method to render the view.
71
	 *
72
	 * @return  string  The rendered view.
73
	 */
74
	public function render()
75
	{
76
		$items = $this->model->getItems($this->source);
77
78
		// Null out the model now to free some memory
79
		$this->model = null;
80
81
		if ($this->source)
82
		{
83
			return $this->processSingleSource($items);
84
		}
85
86
		$php_version = [];
87
		$db_type     = [];
88
		$db_version  = [];
89
		$cms_version = [];
90
		$server_os   = [];
91
92
		// If we have the entire database, we have to loop within each group to put it all together
93
		foreach ($items as $item)
94
		{
95
			$this->totalItems++;
96
97
			foreach ($this->dataSources as $source)
98
			{
99
				if (isset($item[$source]) && !is_null($item[$source]))
100
				{
101
					// Special case, if the server is empty then change the key to "unknown"
102
					if ($source === 'server_os' && empty($item[$source]))
103
					{
104
						if (!isset(${$source}['unknown']))
105
						{
106
							${$source}['unknown'] = 0;
107
						}
108
109
						${$source}['unknown']++;
110
					}
111 View Code Duplication
					else
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
					{
113
						if (!isset(${$source}[$item[$source]]))
114
						{
115
							${$source}[$item[$source]] = 0;
116
						}
117
118
						${$source}[$item[$source]]++;
119
					}
120
				}
121
			}
122
		}
123
124
		$data = [
125
			'php_version' => $php_version,
126
			'db_type'     => $db_type,
127
			'db_version'  => $db_version,
128
			'cms_version' => $cms_version,
129
			'server_os'   => $server_os,
130
		];
131
132
		$responseData = $this->buildResponseData($data);
133
134
		$responseData['total'] = $this->totalItems;
135
136
		$this->addData('data', $responseData);
137
138
		return parent::render();
139
	}
140
141
	/**
142
	 * Set the data source.
143
	 *
144
	 * @param   string  $source  Data source to return.
145
	 *
146
	 * @return  void
147
	 */
148 1
	public function setSource(string $source)
149
	{
150 1
		$this->source = $source;
151 1
	}
152
153
	/**
154
	 * Process the raw data into the response data format.
155
	 *
156
	 * @param   array  $data  The raw data array.
157
	 *
158
	 * @return  array
159
	 */
160
	private function buildResponseData(array $data) : array
161
	{
162
		$responseData = [];
163
164
		foreach ($data as $key => $value)
165
		{
166
			foreach ($value as $name => $count)
167
			{
168
				if ($name)
169
				{
170
					$responseData[$key][] = [
171
						'name'  => $name,
172
						'count' => $count
173
					];
174
				}
175
			}
176
		}
177
178
		unset($data);
179
180
		if (!$this->authorizedRaw)
181
		{
182
			$responseData = $this->sanitizeData($responseData);
183
		}
184
185
		return $responseData;
186
	}
187
188
	/**
189
	 * Process the response for a single data source.
190
	 *
191
	 * @param   \Generator  $items  The source items to process.
192
	 *
193
	 * @return  string  The rendered view.
194
	 */
195
	private function processSingleSource(\Generator $items) : string
196
	{
197
		$data = [
198
			${$this->source} = [],
199
		];
200
201
		foreach ($items as $item)
202
		{
203
			$this->totalItems++;
204
205
			foreach ($this->dataSources as $source)
206
			{
207
				if (isset($item[$source]) && !is_null($item[$source]))
208
				{
209
					// Special case, if the server is empty then change the key to "unknown"
210
					if ($source === 'server_os' && empty($item[$source]))
211
					{
212
						if (!isset($data[$source]['unknown']))
213
						{
214
							$data[$source]['unknown'] = 0;
215
						}
216
217
						$data[$source]['unknown']++;
218
					}
219 View Code Duplication
					else
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...
220
					{
221
						if (!isset($data[$source][$item[$source]]))
222
						{
223
							$data[$source][$item[$source]] = 0;
224
						}
225
226
						$data[$source][$item[$source]]++;
227
					}
228
				}
229
			}
230
		}
231
232
		unset($generator);
233
234
		$responseData = $this->buildResponseData($data);
235
236
		$responseData['total'] = $this->totalItems;
237
238
		$this->addData('data', $responseData);
239
240
		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...
241
	}
242
243
	/**
244
	 * Sanitize the response data into summarized groups.
245
	 *
246
	 * @param   array  $responseData  The response data to sanitize.
247
	 *
248
	 * @return  array
249
	 */
250
	private function sanitizeData(array $responseData) : array
251
	{
252
		foreach ($responseData as $key => $dataGroup)
253
		{
254
			switch ($key)
255
			{
256
				case 'php_version':
257
				case 'db_version':
258
				case 'cms_version':
259
					// We're going to group by minor version branch here and convert to a percentage
260
					$counts = [];
261
262
					foreach ($dataGroup as $row)
263
					{
264
						$exploded = explode('.', $row['name']);
265
						$version  = $exploded[0] . '.' . (isset($exploded[1]) ? $exploded[1] : '0');
266
267
						// If the container does not exist, add it
268
						if (!isset($counts[$version]))
269
						{
270
							$counts[$version] = 0;
271
						}
272
273
						$counts[$version] += $row['count'];
274
					}
275
276
					$sanitizedData = [];
277
278 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...
279
					{
280
						$sanitizedData[$version] = round(($count / $this->totalItems) * 100, 2);
281
					}
282
283
					$responseData[$key] = $sanitizedData;
284
285
					break;
286
287
				case 'server_os':
288
					// We're going to group by operating system here
289
					$counts = [];
290
291
					foreach ($dataGroup as $row)
292
					{
293
						$fullOs = explode(' ', $row['name']);
294
						$os     = $fullOs[0];
295
296
						// If the container does not exist, add it
297
						if (!isset($counts[$os]))
298
						{
299
							$counts[$os] = 0;
300
						}
301
302
						$counts[$os] += $row['count'];
303
					}
304
305
					$sanitizedData = [];
306
307 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...
308
					{
309
						$sanitizedData[$os] = round(($count / $this->totalItems) * 100, 2);
310
					}
311
312
					$responseData[$key] = $sanitizedData;
313
314
					break;
315
316
				case 'db_type':
317
				default:
318
					// For now, group by the object name and figure out the percentages
319
					$sanitizedData = [];
320
321
					foreach ($dataGroup as $row)
322
					{
323
						$sanitizedData[$row['name']] = round(($row['count'] / $this->totalItems) * 100, 2);
324
					}
325
326
					$responseData[$key] = $sanitizedData;
327
328
					break;
329
			}
330
		}
331
332
		return $responseData;
333
	}
334
}
335