Completed
Push — develop ( e37abb...bc4310 )
by Michael
35:06
created

ServerRequest::initQueryParams()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.4285
cc 3
eloc 5
nc 2
nop 1
crap 3
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 17
	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 $_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 17
		if ($body === null) {
67 17
			$body = new Stream(fopen('php://input', 'r'));
68 17
		}
69
70 17
		$this->serverParams = $_SERVER;
71 17
		$this->cookieParams = $_COOKIE;
72 17
		$this->queryParams = $this->initQueryParams($this->serverParams);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->initQueryParams($this->serverParams) can be null. However, the property $queryParams is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
73 17
		$this->postParams = $_POST;
74 17
		$this->filesParams = $_FILES;
75 17
		$this->uploadedFiles = $this->initUploadedFiles($this->filesParams);
76 17
		$this->parsedBody = null;
77 17
		$this->attributes = [];
78
79 17
		parent::__construct($this->initMethod($method), $this->initUri($uri), $version, $this->initHeaders($headers), $body);
80 17
	}
81
82
	/**
83
	 * Initialize the method.
84
	 *
85
	 * @param string $method
86
	 * @return string the method.
87
	 */
88 17
	private function initMethod($method)
89
	{
90 17
		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 17
	private function initUri($uri)
100
	{
101 17
		if ($uri !== null) {
102 1
			return $uri;
103
		}
104
105 17
		$scheme = isset($this->getServerParams()['HTTPS']) ? 'https://' : 'http://';
106 17
		$host = isset($this->getServerParams()['HTTP_HOST']) ? $scheme . $this->getServerParams()['HTTP_HOST'] : '';
107 17
		$path = isset($this->getServerParams()['REQUEST_URI']) ? $this->getServerParams()['REQUEST_URI'] : '';
108
109 17
		return new URI($host . $path);
110
	}
111
112
	/**
113
	 * Initialize the headers.
114
	 *
115
	 * @param array $headers
116
	 * @return array the headers.
117
	 */
118 17
	private function initHeaders($headers)
119
	{
120 17
		return $headers ?: getallheaders();
121
	}
122
123
	/**
124
	 * Initialize the headers.
125
	 *
126
	 * @param string $uri
0 ignored issues
show
Bug introduced by
There is no parameter named $uri. 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...
127
	 * @return array the headers.
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
128
	 */
129 17
	private function initQueryParams($serverParams)
130
	{
131 17
		if (!isset($serverParams['REQUEST_URI']) || !($query = parse_url($serverParams['REQUEST_URI'], \PHP_URL_QUERY))) {
132 1
			return [];
133
		}
134
135 17
		parse_str($query, $result);
136
137 17
		return $result;
138
	}
139
140
	/**
141
	 * Initialize the uploaded files.
142
	 *
143
	 * @param array $files
144
	 * @return array the uploaded files.
145
	 */
146 17
	private function initUploadedFiles(array $files)
147
	{
148 17
		$result = [];
149
150 17
		foreach ($files as $key => $value) {
151 17
			$result[$key] = $this->parseUploadedFiles($value);
152 17
		}
153
154 17
		return $result;
155
	}
156
157
	/**
158
	 * Parse uploaded files.
159
	 *
160
	 * @param array $files
161
	 * @return UploadedFile|array uploaded files.
162
	 */
163 17
	private function parseUploadedFiles($files)
164
	{
165
		// Empty
166 17
		$first = reset($files);
167
168
		// Single
169 17
		if (!is_array($first)) {
170 17
			return $this->parseSingleUploadedFiles($files);
171
		}
172
173
		// Multiple
174 17
		if (count(array_filter(array_keys($first), 'is_string')) === 0) {
175 17
			return $this->parseMultipleUploadedFiles($files);
176
		}
177
178
		// Namespace
179 17
		return $this->initUploadedFiles($files);
180
	}
181
182
	/**
183
	 * Parse single uploaded file.
184
	 *
185
	 * @param array $file
186
	 * @return UploadedFile single uploaded file.
187
	 */
188 17
	private function parseSingleUploadedFiles(array $file)
189
	{
190 17
		return new UploadedFile($file['name'], $file['type'], $file['tmp_name'], $file['error'], $file['size']);
191
	}
192
193
	/**
194
	 * Parse multiple uploaded files.
195
	 *
196
	 * @param array $files
197
	 * @return UploadedFiles[] multiple uploaded files.
198
	 */
199 17
	private function parseMultipleUploadedFiles(array $files)
200
	{
201 17
		$count = count($files['name']);
202 17
		$result = [];
203
204 17
		for ($i = 0; $i < $count; $i++) {
205 17
			$result[] = new UploadedFile($files['name'][$i], $files['type'][$i], $files['tmp_name'][$i], $files['error'][$i], $files['size'][$i]);
206 17
		}
207
208 17
		return $result;
209
	}
210
211
	/**
212
	 * {@inheritdoc}
213
	 */
214 17
	public function getServerParams()
215
	{
216 17
		return $this->serverParams;
217
	}
218
219
	/**
220
	 * {@inheritdoc}
221
	 */
222 1
	public function getCookieParams()
223
	{
224 1
		return $this->cookieParams;
225
	}
226
227
	/**
228
	 * Set the cookie params.
229
	 *
230
	 * @param array $cookieParams
231
	 * @return $this
232
	 */
233 1
	private function setCookieParams(array $cookieParams)
234
	{
235 1
		$this->cookieParams = $cookieParams;
236
237 1
		return $this;
238
	}
239
240
	/**
241
	 * {@inheritdoc}
242
	 */
243 1
	public function withCookieParams(array $cookieParams)
244
	{
245 1
		$result = clone $this;
246
247 1
		return $result->setCookieParams($cookieParams);
248
	}
249
250
	/**
251
	 * {@inheritdoc}
252
	 */
253 2
	public function getQueryParams()
254
	{
255 2
		return $this->queryParams;
256
	}
257
258
	/**
259
	 * Set the query params.
260
	 *
261
	 * @param array $queryParams
262
	 * @return $this
263
	 */
264 1
	private function setQueryParams(array $queryParams)
265
	{
266 1
		$this->queryParams = $queryParams;
267
268 1
		return $this;
269
	}
270
271
	/**
272
	 * {@inheritdoc}
273
	 */
274 1
	public function withQueryParams(array $queryParams)
275
	{
276 1
		$result = clone $this;
277
278 1
		return $result->setQueryParams($queryParams);
279
	}
280
281
	/**
282
	 * {@inheritdoc}
283
	 */
284 2
	public function getUploadedFiles()
285
	{
286 2
		return $this->uploadedFiles;
287
	}
288
289
	/**
290
	 * Set the uploaded files.
291
	 *
292
	 * @param array $uploadedFiles
293
	 * @return $this
294
	 */
295 1
	private function setUploadedFiles(array $uploadedFiles)
296
	{
297 1
		$this->uploadedFiles = $uploadedFiles;
298
299 1
		return $this;
300
	}
301
302
	/**
303
	 * {@inheritdoc}
304
	 */
305 1
	public function withUploadedFiles(array $uploadedFiles)
306
	{
307 1
		$result = clone $this;
308
309 1
		return $result->setUploadedFiles($uploadedFiles);
310
	}
311
312
	/**
313
	 * {@inheritdoc}
314
	 */
315 4
	public function getParsedBody()
316
	{
317 4
		if ($this->parsedBody !== null) {
318 1
			return $this->parsedBody;
319
		}
320
321 3
		$contentType = $this->getHeaderLine('Content-Type');
322
323 3
		if ($this->getMethod() === 'POST' && ($contentType === 'application/x-www-form-urlencoded' || $contentType === 'multipart/form-data')) {
324 1
			return $this->postParams;
325
		}
326
327 2
		if ($contentType === 'application/json') {
328 1
			return json_decode((string) $this->getBody());
329
		}
330
331 1
		return null;
332
	}
333
334
	/**
335
	 * Set the parsed body.
336
	 *
337
	 * @param null|array|object $parsedBody
338
	 * @return $this
339
	 */
340 1
	private function setParsedBody($parsedBody)
341
	{
342 1
		$this->parsedBody = $parsedBody;
343
344 1
		return $this;
345
	}
346
347
	/**
348
	 * {@inheritdoc}
349
	 */
350 1
	public function withParsedBody($parsedBody)
351
	{
352 1
		$result = clone $this;
353
354 1
		return $result->setParsedBody($parsedBody);
355
	}
356
357
	/**
358
	 * {@inheritdoc}
359
	 */
360 1
	public function getAttributes()
361
	{
362 1
		return $this->attributes;
363
	}
364
365
	/**
366
	 * {@inheritdoc}
367
	 */
368 3
	public function getAttribute($name, $default = null)
369
	{
370 3
		return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
371
	}
372
373
	/**
374
	 * Set the attribute.
375
	 *
376
	 * @param string $name
377
	 * @param mixed $value
378
	 * @return $this
379
	 */
380 1
	private function setAttribute($name, $value)
381
	{
382 1
		$this->attributes[$name] = $value;
383
384 1
		return $this;
385
	}
386
387
	/**
388
	 * {@inheritdoc}
389
	 */
390 1
	public function withAttribute($name, $value)
391
	{
392 1
		$result = clone $this;
393
394 1
		return $result->setAttribute($name, $value);
395
	}
396
397
	/**
398
	 * Remove the attribute.
399
	 *
400
	 * @param string $name
401
	 * @return $this
402
	 */
403 1
	private function removeAttribute($name)
404
	{
405 1
		unset($this->attributes[$name]);
406
407 1
		return $this;
408
	}
409
410
	/**
411
	 * {@inheritdoc}
412
	 */
413 1
	public function withoutAttribute($name)
414
	{
415 1
		$result = clone $this;
416
417 1
		return $result->removeAttribute($name);
418
	}
419
}
420