Passed
Push — master ( dc4356...41e10c )
by Alexander
03:41
created

HttpRequest::setLocale()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 11
rs 10
1
<?php
2
3
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2023 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Components\Http\Resources;
24
25
use Locale;
26
use LogicException;
27
use Syscodes\Components\Http\URI;
28
use Syscodes\Components\Support\Str;
29
use Syscodes\Components\Http\Loaders\Files;
30
use Syscodes\Components\Http\Loaders\Inputs;
31
use Syscodes\Components\Http\Loaders\Server;
32
use Syscodes\Components\Http\Loaders\Headers;
33
use Syscodes\Components\Http\Loaders\Parameters;
34
use Syscodes\Components\Http\Helpers\RequestClientIP;
35
36
/**
37
 * Allows that HTTP request  loading to initialize the system.
38
 */
39
trait HttpRequest
40
{
41
	/**
42
	 * Get the http method parameter.
43
	 * 
44
	 * @var bool $httpMethodParameterOverride
45
	 */
46
	protected static $httpMethodParameterOverride = false;
47
48
	/**
49
	 * Holds the global active request instance.
50
	 *
51
	 * @var bool $requestURI
52
	 */
53
	protected static $requestURI;
54
55
	/**
56
	 * Get the custom parameters.
57
	 * 
58
	 * @var \Syscodes\Components\Http\Loaders\Parameters $attributes
59
	 */
60
	public $attributes;
61
62
	/**
63
	 * The base URL.
64
	 * 
65
	 * @var string $baseUrl
66
	 */
67
	protected $baseUrl;
68
69
	/**
70
	 * Get the client ip.
71
	 * 
72
	 * @var mixed $clientIp
73
	 */
74
	protected $clientIp;
75
76
	/**
77
	 * Gets cookies ($_COOKIE).
78
	 * 
79
	 * @var \Syscodes\Components\Http\Loaders\Inputs $cookies
80
	 */
81
	public $cookies;
82
83
	/**
84
	 * Gets the string with format JSON.
85
	 * 
86
	 * @var string|resource|object|null $content
87
	 */
88
	protected $content;
89
90
	/**
91
	 * The default Locale this request.
92
	 * 
93
	 * @var string $defaultLocale
94
	 */
95
	protected $defaultLocale = 'en';
96
	
97
	/**
98
	 * Gets files request ($_FILES).
99
	 * 
100
	 * @var \Syscodes\Components\Http\Loaders\Files $files
101
	 */
102
	public $files;
103
	
104
	/**
105
	 * Get the headers request ($_SERVER).
106
	 * 
107
	 * @var \Syscodes\Components\Http\Loaders\Headers $headers
108
	 */
109
	public $headers;
110
111
	/**
112
	 * The current language of the application.
113
	 * 
114
	 * @var string $languages
115
	 */
116
	protected $languages;
117
	
118
	/**
119
	 * Get the locale.
120
	 * 
121
	 * @var string $locale
122
	 */
123
	protected $locale;
124
	
125
	/** 
126
	 * The method name.
127
	 * 
128
	 * @var string $method
129
	 */
130
	protected $method;
131
132
	/**
133
	 * Query string parameters ($_GET).
134
	 * 
135
	 * @var \Syscodes\Components\Http\Loaders\Parameters $query
136
	 */
137
	public $query;
138
139
	/**
140
	 * Request body parameters ($_POST).
141
	 * 
142
	 * @var \Syscodes\Components\Http\Loaders\Parameters $request
143
	 */
144
	public $request;
145
146
	/**
147
	 * The detected uri and server variables ($_SERVER).
148
	 * 
149
	 * @var \Syscodes\Components\Http\Loaders\Server $server
150
	 */
151
	public $server;
152
153
	/** 
154
	 * List of routes uri.
155
	 *
156
	 * @var \Syscodes\Components\Http\URI $uri 
157
	 */
158
	public $uri;
159
160
	/**
161
	 * Stores the valid locale codes.
162
	 * 
163
	 * @var array $validLocales
164
	 */
165
	protected $validLocales = [];
166
167
	/**
168
	 * Constructor. Create new the Request class.
169
	 * 
170
	 * @param  array  $query
171
	 * @param  array  $request
172
	 * @param  array  $attributes
173
	 * @param  array  $cookies
174
	 * @param  array  $files
175
	 * @param  array  $server
176
	 * @param  string|resource|null $content  
177
	 * 
178
	 * @return void
179
	 */
180
	public function __construct(
181
		array $query = [],
182
		array $request = [],
183
		array $attributes = [],
184
		array $cookies = [],
185
		array $files = [],
186
		array $server = [],
187
		$content = null
188
	) {
189
		$this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
190
		
191
		$this->detectLocale();
192
	}
193
194
	/**
195
     * Enables support for the _method request parameter to determine the intended HTTP method.
196
     * 
197
     * @return void
198
     */
199
    public static function enabledHttpMethodParameterOverride(): void
200
    {
201
        self::$httpMethodParameterOverride = true;
202
    }
203
	
204
	/**
205
	 * Checks whether support for the _method request parameter is enabled.
206
	 * 
207
	 * @return bool
208
	 */
209
	public static function getHttpMethodParameterOverride(): bool
210
	{
211
		return self::$httpMethodParameterOverride;
212
	}
213
214
	/**
215
	 * Creates an Syscodes request from of the Request class instance.
216
	 * 
217
	 * @param  \Syscodes\Components\Http\Request  $request
218
	 * 
219
	 * @return static
220
	 */
221
	public static function createFromRequest($request): static
222
	{
223
		$newRequest = (new static)->duplicate(
224
			$request->query->all(), $request->request->all(), $request->attributes->all(),
225
			$request->cookies->all(), $request->files->all(), $request->server->all()
226
		);
227
		
228
		$newRequest->headers->replace($request->headers->all());
229
		
230
		$newRequest->content = $request->content;
0 ignored issues
show
Bug Best Practice introduced by
The property $content is declared protected in Syscodes\Components\Http\Request. Since you implement __get, consider adding a @property or @property-read.
Loading history...
231
		
232
		if ($newRequest->isJson()) {
0 ignored issues
show
Bug introduced by
It seems like isJson() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

232
		if ($newRequest->/** @scrutinizer ignore-call */ isJson()) {
Loading history...
233
			$newRequest->request = $newRequest->json();
0 ignored issues
show
Bug introduced by
It seems like json() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

233
			/** @scrutinizer ignore-call */ 
234
   $newRequest->request = $newRequest->json();
Loading history...
234
		}
235
		
236
		return $newRequest;
237
	}
238
239
	/**
240
	 * Creates a new request with value from PHP's super global.
241
	 * 
242
	 * @return static
243
	 */
244
	public static function createFromRequestGlobals(): static
245
	{
246
		$request = static::createFromRequestFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);
247
248
		if (Str::startsWith($request->headers->get('CONTENT_TYPE', ''), 'application/x-www-form-urlencoded')
249
		    && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])) {
250
			parse_str($request->getContent(), $data);
0 ignored issues
show
Bug introduced by
It seems like getContent() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

250
			parse_str($request->/** @scrutinizer ignore-call */ getContent(), $data);
Loading history...
251
			$request->request = new Inputs($data);
252
		}
253
254
		return $request;
255
	}
256
257
	/**
258
	 * Creates a new request from a factory.
259
	 * 
260
	 * @param  array  $query
261
	 * @param  array  $request
262
	 * @param  array  $attributes
263
	 * @param  array  $cookies
264
	 * @param  array  $files
265
	 * @param  array  $server
266
	 * 
267
	 * @return static
268
	 */
269
	private static function createFromRequestFactory(
270
		array $query = [], 
271
		array $request = [],
272
		array $attributes = [] ,
273
		array $cookies = [], 
274
		array $files = [], 
275
		array $server = []
276
	): static {
277
		if (self::$requestURI) {
278
			$request = (self::$requestURI)($query, $request, [], $cookies, $files, $server);
279
280
			if ( ! $request instanceof self) {
281
				throw new LogicException('The Request active must return an instance of Syscodes\Components\Http\Request');
282
			}
283
284
			return $request;
285
		}
286
287
		return new static($query, $request, $attributes, $cookies, $files, $server);
288
	}
289
290
	/**
291
	 * Returns the factory request currently being used.
292
	 *
293
	 * @param  \Syscodes\Components\Http\Request|callable|null  $request  
294
	 *
295
	 * @return void
296
	 */
297
	public static function setFactory(?callable $request): void
298
	{
299
		self::$requestURI = $request;
0 ignored issues
show
Documentation Bug introduced by
It seems like $request can also be of type callable. However, the property $requestURI is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
300
	}
301
302
	/**
303
	 * Sets the parameters for this request.
304
	 * 
305
	 * @param  array  $query
306
	 * @param  array  $request
307
	 * @param  array  $attributes
308
	 * @param  array  $cookies
309
	 * @param  array  $files
310
	 * @param  array  $server
311
	 * 
312
	 * @return void
313
	 */
314
	public function initialize(
315
		array $query = [], 
316
		array $request = [],
317
		array $attributes = [],
318
		array $cookies = [], 
319
		array $files = [], 
320
		array $server = [], 
321
		$content = null
322
	): void {
323
		$this->query = new Inputs($query);
324
		$this->request = new Inputs($request);
325
		$this->attributes = new Parameters($attributes);
326
		$this->cookies = new Inputs($cookies);
327
		$this->files = new Files($files);
328
		$this->server = new Server($server);
329
		$this->headers = new Headers($this->server->all());
330
331
		// Variables initialized
332
		$this->uri = new URI;
333
		$this->method = null;
334
		$this->baseUrl = null;
335
		$this->content = $content;
336
		$this->pathInfo = null;
0 ignored issues
show
Bug Best Practice introduced by
The property pathInfo does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
337
		$this->languages = null;
338
		$this->acceptableContentTypes = null;
0 ignored issues
show
Bug Best Practice introduced by
The property acceptableContentTypes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
339
		$this->validLocales = config('app.supportedLocales');
0 ignored issues
show
Documentation Bug introduced by
It seems like config('app.supportedLocales') can also be of type Syscodes\Components\Config\Configure. However, the property $validLocales is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
340
		$this->clientIp = new RequestClientIP($this->server->all());
341
	}
342
343
	/**
344
	 * Clones a request and overrides some of its parameters.
345
	 * 
346
	 * @param  array|null  $query
347
	 * @param  array|null  $request
348
	 * @param  array|null  $attributes
349
	 * @param  array|null  $cookies
350
	 * @param  array|null  $files
351
	 * @param  array|null  $server
352
	 * 
353
	 * @return static
354
	 */
355
	public function duplicate(
356
		array $query = null, 
357
		array $request = null,
358
		array $attributes = null,
359
		array $cookies = null,
360
		array $files = null,
361
		array $server = null
362
	): static {
363
		$duplicate = clone $this;
364
365
		if (null !== $query) {
366
			$duplicate->query = new Inputs($query);
367
		}
368
369
		if (null !== $request) {
370
			$duplicate->request = new Inputs($request);
371
		}
372
373
		if (null !== $attributes) {
374
			$duplicate->attributes = new Parameters($attributes);
375
		}
376
377
		if (null !== $cookies) {
378
			$duplicate->cookies = new Inputs($cookies);
379
		}
380
381
		if (null !== $files) {
382
			$duplicate->files = new Files($files);
383
		}
384
385
		if (null !== $server) {
386
			$duplicate->server  = new Server($server);
387
			$duplicate->headers = new Headers($duplicate->server->all());
388
		}
389
390
		$duplicate->uri = new URI;
391
		$duplicate->locale = null;
392
		$duplicate->method = null;
393
		$duplicate->baseUrl = null;
394
		$duplicate->pathInfo = null;
0 ignored issues
show
Bug Best Practice introduced by
The property pathInfo does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
395
		$duplicate->validLocales = config('app.supportedLocales');
0 ignored issues
show
Documentation Bug introduced by
It seems like config('app.supportedLocales') can also be of type Syscodes\Components\Config\Configure. However, the property $validLocales is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
396
		$duplicate->clientIp = new RequestClientIP($duplicate->server->all());
397
398
		return $duplicate;		
399
	}
400
401
	/**
402
	 * Handles setting up the locale, auto-detecting of language.
403
	 * 
404
	 * @return void
405
	 */
406
	public function detectLocale(): void
407
	{
408
		$this->languages = $this->defaultLocale = config('app.locale');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->defaultLocale = config('app.locale') can also be of type Syscodes\Components\Config\Configure. However, the property $languages is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
Documentation Bug introduced by
It seems like config('app.locale') can also be of type Syscodes\Components\Config\Configure. However, the property $defaultLocale is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
409
410
		$this->setLocale($this->validLocales[0]);
411
	}
412
413
	/**
414
	 * Returns the default locale as set.
415
	 * 
416
	 * @return string
417
	 */
418
	public function getDefaultLocale(): string
419
	{
420
		return $this->defaultLocale;
421
	}
422
423
	/**
424
	 * Gets the current locale, with a fallback to the default.
425
	 * 
426
	 * @return string 
427
	 */
428
	public function getLocale(): string
429
	{
430
		return $this->languages ?: $this->defaultLocale;
431
	}
432
433
	/**
434
	 * Sets the locale string for this request.
435
	 * 
436
	 * @param  string  $locale
437
	 * 
438
	 * @return static
439
	 */
440
	public function setLocale(string $locale): static
441
	{
442
		if ( ! in_array($locale, $this->validLocales, true)) {
443
			$locale = $this->defaultLocale;
444
		}
445
		
446
		$this->languages = $locale;
447
448
		Locale::setDefault($locale);
449
			
450
		return $this;
451
	}
452
453
	/**
454
	 * Returns the host name.
455
	 * 
456
	 * @return string
457
	 */
458
	public function getHost(): string
459
	{
460
		if ($forwardedHost = $this->server->get('HTTP_X_FORWARDED_HOST')) {
461
			$host = $forwardedHost[0];
462
		} elseif ( ! $host = $this->headers->get('HOST')) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $host is correct as $this->headers->get('HOST') targeting Syscodes\Components\Http\Loaders\Headers::get() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
463
			if ( ! $host = $this->server->get('SERVER_NAME')) {
464
				$host = $this->server->get('REMOTE_ADDR', '');
465
			}
466
		}
467
468
		$host = strtolower(preg_replace('/:\d+$/', '', trim(($host))));
469
		
470
		return $this->uri->setHost($host);
471
	}
472
473
	/**
474
	 * Returns the port on which the request is made.
475
	 * 
476
	 * @return int
477
	 */
478
	public function getPort(): int
479
	{
480
		if ( ! $this->server->get('HTTP_HOST')) {
481
			return $this->server->get('SERVER_PORT');
482
		}
483
		
484
		return 'https' === $this->getScheme() ? $this->uri->setPort(443) : $this->uri->setPort(80);
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'https' === $this...$this->uri->setPort(80) returns the type string which is incompatible with the type-hinted return integer.
Loading history...
485
	}
486
487
	/**
488
	 * Gets the request's scheme.
489
	 * 
490
	 * @return string
491
	 */
492
	public function getScheme(): string
493
	{
494
		return $this->secure() ? $this->uri->setScheme('https') : $this->uri->setScheme('http');
0 ignored issues
show
Bug introduced by
It seems like secure() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

494
		return $this->/** @scrutinizer ignore-call */ secure() ? $this->uri->setScheme('https') : $this->uri->setScheme('http');
Loading history...
495
	}
496
497
	/**
498
	 * Get the user.
499
	 * 
500
	 * @return string|null
501
	 */
502
	public function getUser(): ?string
503
	{
504
		$user = $this->uri->setUser(
505
			$this->headers->get('PHP_AUTH_USER')
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->headers->get('PHP_AUTH_USER') targeting Syscodes\Components\Http\Loaders\Headers::get() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
506
		);
507
508
		return $user;
509
	}
510
511
	/**
512
	 * Get the password.
513
	 * 
514
	 * @return string|null
515
	 */
516
	public function getPassword(): ?string
517
	{
518
		$password = $this->uri->setPassword(
519
			$this->headers->get('PHP_AUTH_PW')
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->headers->get('PHP_AUTH_PW') targeting Syscodes\Components\Http\Loaders\Headers::get() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
520
		);
521
522
		return $password;
523
	}
524
525
	/**
526
	 * Gets the user info.
527
	 * 
528
	 * @return string|null
529
	 */
530
	public function getUserInfo(): ?string
531
	{
532
		return $this->uri->getUserInfo();
533
	}
534
535
	/**
536
	 * Returns the HTTP host being requested.
537
	 * 
538
	 * @return string
539
	 */
540
	public function getHttpHost(): string
541
	{
542
		$scheme = $this->getScheme();
543
		$port   = $this->getPort();
544
545
		if (('http' === $scheme && 80 === $port) || ('https' === $scheme && 443 === $port))	{
546
			return $this->getHost();
547
		}
548
549
		return $this->getHost().':'.$port;
550
	}
551
552
	/**
553
	 * Gets the scheme and HTTP host.
554
	 * 
555
	 * @return string
556
	 */
557
	public function getSchemeWithHttpHost(): string
558
	{
559
		return $this->getScheme().'://'.$this->getHttpHost();
560
	}
561
}