Completed
Push — master ( 16c8a5...965897 )
by Michael
02:58
created

ServerRequest   C

Complexity

Total Complexity 43

Size/Duplication

Total Lines 369
Duplicated Lines 0 %

Coupling/Cohesion

Components 7
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 43
c 1
b 0
f 1
lcom 7
cbo 4
dl 0
loc 369
ccs 102
cts 102
cp 1
rs 6.8695

26 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 2
A initMethod() 0 4 3
B initUri() 0 12 5
A initUploadedFiles() 0 10 2
A parseUploadedFiles() 0 18 3
A parseSingleUploadedFiles() 0 4 1
A parseMultipleUploadedFiles() 0 11 2
A getServerParams() 0 4 1
A getCookieParams() 0 4 1
A setCookieParams() 0 6 1
A withCookieParams() 0 6 1
A getQueryParams() 0 4 1
A setQueryParams() 0 6 1
A withQueryParams() 0 6 1
A getUploadedFiles() 0 4 1
A setUploadedFiles() 0 6 1
A withUploadedFiles() 0 6 1
B getParsedBody() 0 18 6
A setParsedBody() 0 6 1
A withParsedBody() 0 6 1
A getAttributes() 0 4 1
A getAttribute() 0 4 2
A setAttribute() 0 6 1
A withAttribute() 0 6 1
A removeAttribute() 0 6 1
A withoutAttribute() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like ServerRequest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ServerRequest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file is part of the miBadger package.
5
 *
6
 * @author Michael Webbers <[email protected]>
7
 * @license http://opensource.org/licenses/Apache-2.0 Apache v2 License
8
 * @version 1.0.0
9
 */
10
11
namespace miBadger\Http;
12
13
use Psr\Http\Message\ServerRequestInterface;
14
use Psr\Http\Message\StreamInterface;
15
use Psr\Http\Message\UriInterface;
16
17
/**
18
 * The server request class
19
 *
20
 * @see http://www.php-fig.org/psr/psr-7/
21
 * @since 1.0.0
22
 */
23
class ServerRequest extends Request implements ServerRequestInterface
24
{
25
	/** @var array The server parameters. */
26
	private $serverParams;
27
28
	/** @var array The cookie parameters. */
29
	private $cookieParams;
30
31
	/** @var array The query parameters. */
32
	private $queryParams;
33
34
	/** @var array The post parameters. */
35
	private $postParams;
36
37
	/** @var array The files parameters. */
38
	private $filesParams;
39
40
	/** @var array The uploaded files. */
41
	private $uploadedFiles;
42
43
	/** @var null|array|object The parsed body. */
44
	private $parsedBody;
45
46
	/** @var array The attributes. */
47
	private $attributes;
48
49
	/**
50
	 * Construct a Request object with the given method, uri, version, headers & body.
51
	 *
52
	 * @global array $_SERVER The server parameters.
53
	 * @global array $_COOKIE The cookie parameters.
54
	 * @global array $_GET The query parameters.
55
	 * @global array $_POST The post parameters.
56
	 * @global array $_FILES The files parameters.
57
	 *
58
	 * @param string $method = ''
59
	 * @param UriInterface|null $uri = null
60
	 * @param string $version = self::DEFAULT_VERSION
61
	 * @param array $headers = []
62
	 * @param StreamInterface|null $body = null
63
	 */
64 16
	public function __construct($method = '', UriInterface $uri = null, $version = self::DEFAULT_VERSION, array $headers = [], StreamInterface $body = null)
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__construct uses the super-global variable $_COOKIE which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__construct uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__construct uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__construct uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
65
	{
66 16
		if ($body === null) {
67 16
			$body = new Stream(fopen('php://input', 'r'));
68 16
		}
69
70 16
		$this->serverParams = $_SERVER;
71 16
		$this->cookieParams = $_COOKIE;
72 16
		$this->queryParams = $_GET;
73 16
		$this->postParams = $_POST;
74 16
		$this->filesParams = $_FILES;
75 16
		$this->uploadedFiles = $this->initUploadedFiles($this->filesParams);
76 16
		$this->parsedBody = null;
77 16
		$this->attributes = [];
78
79 16
		parent::__construct($this->initMethod($method), $this->initUri($uri), $version, $headers, $body);
80 16
	}
81
82
	/**
83
	 * Initialize the method.
84
	 *
85
	 * @param string $method
86
	 * @return string the method.
87
	 */
88 16
	private function initMethod($method)
89
	{
90 16
		return $method === '' && isset($this->getServerParams()['REQUEST_METHOD']) ? $this->getServerParams()['REQUEST_METHOD'] : $method;
91
	}
92
93
	/**
94
	 * Initialize the URI.
95
	 *
96
	 * @param UriInterface|null $uri
97
	 * @return UriInterface the URI.
98
	 */
99 16
	private function initUri($uri)
100
	{
101 16
		if ($uri !== null) {
102 1
			return $uri;
103
		}
104
105 16
		$scheme = isset($this->getServerParams()['HTTPS']) ? 'https://' : 'http://';
106 16
		$host = isset($this->getServerParams()['HTTP_HOST']) ? $scheme . $this->getServerParams()['HTTP_HOST'] : '';
107 16
		$path = isset($this->getServerParams()['REQUEST_URI']) ? $this->getServerParams()['REQUEST_URI'] : '';
108
109 16
		return new URI($host . $path);
110
	}
111
112
	/**
113
	 * Initialize the uploaded files.
114
	 *
115
	 * @param array $files
116
	 * @return array the uploaded files.
117
	 */
118 16
	private function initUploadedFiles(array $files)
119
	{
120 16
		$result = [];
121
122 16
		foreach ($files as $key => $value) {
123 16
			$result[$key] = $this->parseUploadedFiles($value);
124 16
		}
125
126 16
		return $result;
127
	}
128
129
	/**
130
	 * Parse uploaded files.
131
	 *
132
	 * @param array $files
133
	 * @return UploadedFile|array uploaded files.
134
	 */
135 16
	private function parseUploadedFiles($files)
136
	{
137
		// Empty
138 16
		$first = reset($files);
139
140
		// Single
141 16
		if (!is_array($first)) {
142 16
			return $this->parseSingleUploadedFiles($files);
143
		}
144
145
		// Multiple
146 16
		if (count(array_filter(array_keys($first), 'is_string')) === 0) {
147 16
			return $this->parseMultipleUploadedFiles($files);
148
		}
149
150
		// Namespace
151 16
		return $this->initUploadedFiles($files);
152
	}
153
154
	/**
155
	 * Parse single uploaded file.
156
	 *
157
	 * @param array $file
158
	 * @return UploadedFile single uploaded file.
159
	 */
160 16
	private function parseSingleUploadedFiles(array $file)
161
	{
162 16
		return new UploadedFile($file['name'], $file['type'], $file['tmp_name'], $file['error'], $file['size']);
163
	}
164
165
	/**
166
	 * Parse multiple uploaded files.
167
	 *
168
	 * @param array $files
169
	 * @return UploadedFiles[] multiple uploaded files.
170
	 */
171 16
	private function parseMultipleUploadedFiles(array $files)
172
	{
173 16
		$count = count($files['name']);
174 16
		$result = [];
175
176 16
		for ($i = 0; $i < $count; $i++) {
177 16
			$result[] = new UploadedFile($files['name'][$i], $files['type'][$i], $files['tmp_name'][$i], $files['error'][$i], $files['size'][$i]);
178 16
		}
179
180 16
		return $result;
181
	}
182
183
	/**
184
	 * {@inheritdoc}
185
	 */
186 16
	public function getServerParams()
187
	{
188 16
		return $this->serverParams;
189
	}
190
191
	/**
192
	 * {@inheritdoc}
193
	 */
194 1
	public function getCookieParams()
195
	{
196 1
		return $this->cookieParams;
197
	}
198
199
	/**
200
	 * Set the cookie params.
201
	 *
202
	 * @param array $cookieParams
203
	 * @return $this
204
	 */
205 1
	private function setCookieParams(array $cookieParams)
206
	{
207 1
		$this->cookieParams = $cookieParams;
208
209 1
		return $this;
210
	}
211
212
	/**
213
	 * {@inheritdoc}
214
	 */
215 1
	public function withCookieParams(array $cookieParams)
216
	{
217 1
		$result = clone $this;
218
219 1
		return $result->setCookieParams($cookieParams);
220
	}
221
222
	/**
223
	 * {@inheritdoc}
224
	 */
225 1
	public function getQueryParams()
226
	{
227 1
		return $this->queryParams;
228
	}
229
230
	/**
231
	 * Set the query params.
232
	 *
233
	 * @param array $queryParams
234
	 * @return $this
235
	 */
236 1
	private function setQueryParams(array $queryParams)
237
	{
238 1
		$this->queryParams = $queryParams;
239
240 1
		return $this;
241
	}
242
243
	/**
244
	 * {@inheritdoc}
245
	 */
246 1
	public function withQueryParams(array $queryParams)
247
	{
248 1
		$result = clone $this;
249
250 1
		return $result->setQueryParams($queryParams);
251
	}
252
253
	/**
254
	 * {@inheritdoc}
255
	 */
256 2
	public function getUploadedFiles()
257
	{
258 2
		return $this->uploadedFiles;
259
	}
260
261
	/**
262
	 * Set the uploaded files.
263
	 *
264
	 * @param array $uploadedFiles
265
	 * @return $this
266
	 */
267 1
	private function setUploadedFiles(array $uploadedFiles)
268
	{
269 1
		$this->uploadedFiles = $uploadedFiles;
270
271 1
		return $this;
272
	}
273
274
	/**
275
	 * {@inheritdoc}
276
	 */
277 1
	public function withUploadedFiles(array $uploadedFiles)
278
	{
279 1
		$result = clone $this;
280
281 1
		return $result->setUploadedFiles($uploadedFiles);
282
	}
283
284
	/**
285
	 * {@inheritdoc}
286
	 */
287 4
	public function getParsedBody()
288
	{
289 4
		if ($this->parsedBody !== null) {
290 1
			return $this->parsedBody;
291
		}
292
293 3
		$contentType = $this->getHeaderLine('Content-Type');
294
295 3
		if ($this->getMethod() === 'POST' && ($contentType === 'application/x-www-form-urlencoded' || $contentType === 'multipart/form-data')) {
296 1
			return $this->postParams;
297
		}
298
299 2
		if ($contentType === 'application/json') {
300 1
			return json_decode((string) $this->getBody());
301
		}
302
303 1
		return null;
304
	}
305
306
	/**
307
	 * Set the parsed body.
308
	 *
309
	 * @param null|array|object $parsedBody
310
	 * @return $this
311
	 */
312 1
	private function setParsedBody($parsedBody)
313
	{
314 1
		$this->parsedBody = $parsedBody;
315
316 1
		return $this;
317
	}
318
319
	/**
320
	 * {@inheritdoc}
321
	 */
322 1
	public function withParsedBody($parsedBody)
323
	{
324 1
		$result = clone $this;
325
326 1
		return $result->setParsedBody($parsedBody);
327
	}
328
329
	/**
330
	 * {@inheritdoc}
331
	 */
332 1
	public function getAttributes()
333
	{
334 1
		return $this->attributes;
335
	}
336
337
	/**
338
	 * {@inheritdoc}
339
	 */
340 3
	public function getAttribute($name, $default = null)
341
	{
342 3
		return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
343
	}
344
345
	/**
346
	 * Set the attribute.
347
	 *
348
	 * @param string $name
349
	 * @param mixed $value
350
	 * @return $this
351
	 */
352 1
	private function setAttribute($name, $value)
353
	{
354 1
		$this->attributes[$name] = $value;
355
356 1
		return $this;
357
	}
358
359
	/**
360
	 * {@inheritdoc}
361
	 */
362 1
	public function withAttribute($name, $value)
363
	{
364 1
		$result = clone $this;
365
366 1
		return $result->setAttribute($name, $value);
367
	}
368
369
	/**
370
	 * Remove the attribute.
371
	 *
372
	 * @param string $name
373
	 * @return $this
374
	 */
375 1
	private function removeAttribute($name)
376
	{
377 1
		unset($this->attributes[$name]);
378
379 1
		return $this;
380
	}
381
382
	/**
383
	 * {@inheritdoc}
384
	 */
385 1
	public function withoutAttribute($name)
386
	{
387 1
		$result = clone $this;
388
389 1
		return $result->removeAttribute($name);
390
	}
391
}
392