Passed
Push — master ( 9acbb8...d979ac )
by Fabio
05:27
created

THttpRequest::resolveRequest()   B

Complexity

Conditions 10
Paths 28

Size

Total Lines 38
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 10.0363

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 25
c 1
b 0
f 0
nc 28
nop 1
dl 0
loc 38
rs 7.6666
ccs 13
cts 14
cp 0.9286
crap 10.0363

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * THttpRequest, THttpCookie, THttpCookieCollection, TUri class file
4
 *
5
 * @author Qiang Xue <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 */
9
10
namespace Prado\Web;
11
12
use Prado\Caching\TFileCacheDependency;
13
use Prado\Exceptions\TConfigurationException;
14
use Prado\Exceptions\TInvalidDataValueException;
15
use Prado\Exceptions\TPhpErrorException;
16
use Prado\Prado;
17
use Prado\TPropertyValue;
18
use Prado\TApplicationMode;
19
use Prado\TEventResults;
20
21
/**
22
 * THttpRequest class
23
 *
24
 * THttpRequest provides storage and access scheme for user request sent via HTTP.
25
 * It also encapsulates a uniform way to parse and construct URLs.
26
 *
27
 * User post data can be retrieved from THttpRequest by using it like an associative array.
28
 * For example, to test if a user supplies a variable named 'param1', you can use,
29
 * <code>
30
 *   if(isset($request['param1'])) ...
31
 *   // equivalent to:
32
 *   // if($request->contains('param1')) ...
33
 * </code>
34
 * To get the value of 'param1', use,
35
 * <code>
36
 *   $value=$request['param1'];
37
 *   // equivalent to:
38
 *   //   $value=$request->itemAt('param1');
39
 * </code>
40
 * To traverse the user post data, use
41
 * <code>
42
 *   foreach($request as $name=>$value) ...
43
 * </code>
44
 * Note, POST and GET variables are merged together in THttpRequest.
45
 * If a variable name appears in both POST and GET data, then POST data
46
 * takes precedence.
47
 *
48
 * To construct a URL that can be recognized by Prado, use {@link constructUrl()}.
49
 * The format of the recognizable URLs is determined according to
50
 * {@link setUrlManager UrlManager}. By default, the following two formats
51
 * are recognized:
52
 * <code>
53
 * /index.php?ServiceID=ServiceParameter&Name1=Value1&Name2=Value2
54
 * /index.php/ServiceID,ServiceParameter/Name1,Value1/Name2,Value2
55
 * </code>
56
 * The first format is called 'Get' while the second 'Path', which is specified
57
 * via {@link setUrlFormat UrlFormat}. For advanced users who want to use
58
 * their own URL formats, they can write customized URL management modules
59
 * and install the managers as application modules and set {@link setUrlManager UrlManager}.
60
 *
61
 * The ServiceID in the above URLs is as defined in the application configuration
62
 * (e.g. the default page service's service ID is 'page').
63
 * As a consequence, your GET variable names should not conflict with the service
64
 * IDs that your application supports.
65
 *
66
 * THttpRequest also provides the cookies sent by the user, user information such
67
 * as his browser capabilities, accepted languages, etc.
68
 *
69
 * By default, THttpRequest is registered with {@link TApplication} as the
70
 * request module. It can be accessed via {@link TApplication::getRequest()}.
71
 *
72
 * @author Qiang Xue <[email protected]>
73
 * @since 3.0
74
 */
75
class THttpRequest extends \Prado\TApplicationComponent implements \IteratorAggregate, \ArrayAccess, \Countable, \Prado\IModule
76
{
77
	public const CGIFIX__PATH_INFO = 1;
78
	public const CGIFIX__SCRIPT_NAME = 2;
79
	/**
80
	 * @var TUrlManager the URL manager module
81
	 */
82
	private $_urlManager;
83
	/**
84
	 * @var string the ID of the URL manager module
85
	 */
86
	private $_urlManagerID = '';
87
	/**
88
	 * @var string Separator used to separate GET variable name and value when URL format is Path.
89
	 */
90
	private $_separator = ',';
91
	/**
92
	 * @var string requested service ID
93
	 */
94
	private $_serviceID;
95
	/**
96
	 * @var string requested service parameter
97
	 */
98
	private $_serviceParam;
99
	/**
100
	 * @var THttpCookieCollection cookies sent from user
101
	 */
102
	private $_cookies;
103
	/**
104
	 * @var string requested URI (URL w/o host info)
105
	 */
106
	private $_requestUri;
107
	/**
108
	 * @var string path info of URL
109
	 */
110
	private $_pathInfo;
111
	/**
112
	 * @var bool whether the session ID should be kept in cookie only
113
	 */
114
	private $_cookieOnly;
115
	private $_urlFormat = THttpRequestUrlFormat::Get;
116
	private $_resolveMethod = THttpRequestResolveMethod::ParameterOrder;
117
	private $_services;
0 ignored issues
show
introduced by
The private property $_services is not used, and could be removed.
Loading history...
118
	private $_requestResolved = false;
119
	private $_enableCookieValidation = false;
120
	private $_cgiFix = 0;
121
	/**
122
	 * @var bool whether to cache the TUrlManager class (useful with a lot of TUrlMappings)
123
	 */
124
	private $_enableCache = false;
125
	/**
126
	 * @var string request URL
127
	 */
128
	private $_url;
129
130
	/**
131
	 * @var string module id
132
	 */
133
	private $_id;
134
135
	/**
136
	 * @var array contains all request variables
137
	 */
138
	private $_items = [];
139
140
	/**
141
	 * @return string id of this module
142
	 */
143 1
	public function getID()
144
	{
145 1
		return $this->_id;
146
	}
147
148
	/**
149
	 * @param string $value id of this module
150
	 */
151 1
	public function setID($value)
152
	{
153 1
		$this->_id = $value;
154 1
	}
155
156
	/**
157
	 * Initializes the module.
158
	 * This method is required by IModule and is invoked by application.
159
	 * @param \Prado\Xml\TXmlElement $config module configuration
160
	 */
161 42
	public function init($config)
162
	{
163
		// Fill in default request info when the script is run in command line
164 42
		if (php_sapi_name() === 'cli') {
165 42
			$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
166 42
			$_SERVER['REQUEST_METHOD'] = 'GET';
167 42
			$_SERVER['SERVER_NAME'] = 'localhost';
168 42
			$_SERVER['SERVER_PORT'] = 80;
169 42
			$_SERVER['HTTP_USER_AGENT'] = '';
170
		}
171
172
		// Info about server variables:
173
		// PHP_SELF contains real URI (w/ path info, w/o query string)
174
		// SCRIPT_NAME is the real URI for the requested script (w/o path info and query string)
175
		// QUERY_STRING is the string following the '?' in the ur (eg the a=x part in http://foo/bar?a=x)
176
		// REQUEST_URI contains the URI part entered in the browser address bar
177
		// SCRIPT_FILENAME is the file path to the executing script
178 42
		if (isset($_SERVER['REQUEST_URI'])) {
179 42
			$this->_requestUri = $_SERVER['REQUEST_URI'];
180
		} else {  // TBD: in this case, SCRIPT_NAME need to be escaped
181 1
			$this->_requestUri = $_SERVER['SCRIPT_NAME'] . (empty($_SERVER['QUERY_STRING']) ? '' : '?' . $_SERVER['QUERY_STRING']);
182
		}
183
184 42
		if ($this->_cgiFix & self::CGIFIX__PATH_INFO && isset($_SERVER['ORIG_PATH_INFO'])) {
185
			$this->_pathInfo = substr($_SERVER['ORIG_PATH_INFO'], strlen($_SERVER['SCRIPT_NAME']));
186 42
		} elseif (isset($_SERVER['PATH_INFO'])) {
187 42
			$this->_pathInfo = $_SERVER['PATH_INFO'];
188 1
		} elseif (strpos($_SERVER['PHP_SELF'], $_SERVER['SCRIPT_NAME']) === 0 && $_SERVER['PHP_SELF'] !== $_SERVER['SCRIPT_NAME']) {
189
			$this->_pathInfo = substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME']));
190
		} else {
191 1
			$this->_pathInfo = '';
192
		}
193
194 42
		$this->getApplication()->setRequest($this);
195
	}
196
197
	/**
198
	 * Strips slashes from input data.
199
	 * This method is applied when magic quotes is enabled.
200
	 * @param mixed $data by reference, input data to be processed
201
	 * @return mixed processed data
202
	 * @deprecated useless since get_magic_quotes_gpc() is unavailable from php 5.4
203
	 */
204
	public function stripSlashes(&$data)
205
	{
206
		return is_array($data) ? array_map([$this, 'stripSlashes'], $data) : stripslashes($data);
207
	}
208
209 42
	/**
210 42
	 * @return TUri the request URL
211
	 */
212
	public function getUrl()
213
	{
214
		if ($this->_url === null) {
215
			$secure = $this->getIsSecureConnection();
216
			$url = $secure ? 'https://' : 'http://';
217
			if (empty($_SERVER['HTTP_HOST'])) {
218 1
				$url .= $_SERVER['SERVER_NAME'];
219
				$port = $_SERVER['SERVER_PORT'];
220 1
				if (($port != 80 && !$secure) || ($port != 443 && $secure)) {
221
					$url .= ':' . $port;
222
				}
223
			} else {
224
				$url .= $_SERVER['HTTP_HOST'];
225
			}
226 3
			$url .= $this->getRequestUri();
227
			$this->_url = new TUri($url);
0 ignored issues
show
Documentation Bug introduced by
It seems like new Prado\Web\TUri($url) of type Prado\Web\TUri is incompatible with the declared type string of property $_url.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

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

Loading history...
228 3
		}
229 3
		return $this->_url;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_url also could return the type string which is incompatible with the documented return type Prado\Web\TUri.
Loading history...
230 3
	}
231 3
232 1
	/**
233 1
	 * Set true to cache the UrlManager instance. Consider to enable this cache
234 1
	 * when the application defines a lot of TUrlMappingPatterns
235 1
	 * @param bool $value true to cache urlmanager instance.
236
	 */
237
	public function setEnableCache($value)
238 3
	{
239
		$this->_enableCache = TPropertyValue::ensureBoolean($value);
240 3
	}
241 3
242
	/**
243 3
	 * @return bool true if urlmanager instance should be cached, false otherwise.
244
	 */
245
	public function getEnableCache()
246
	{
247
		return $this->_enableCache;
248
	}
249
250
	protected function getCacheKey()
251
	{
252
		return $this->getID();
253
	}
254
255
	/**
256
	 * Saves the current UrlManager instance to cache.
257
	 * @param mixed $manager
258
	 * @return bool true if UrlManager instance was cached, false otherwise.
259 13
	 */
260
	protected function cacheUrlManager($manager)
261 13
	{
262
		if ($this->getEnableCache()) {
263
			$cache = $this->getApplication()->getCache();
264
			if ($cache !== null) {
265
				$dependencies = null;
266
				if ($this->getApplication()->getMode() !== TApplicationMode::Performance) {
267
					if ($manager instanceof TUrlMapping && $fn = $manager->getConfigFile()) {
268
						$fn = Prado::getPathOfNamespace($fn, $this->getApplication()->getConfigurationFileExt());
269
						$dependencies = new TFileCacheDependency($fn);
270
					}
271
				}
272
				return $cache->set($this->getCacheKey(), $manager, 0, $dependencies);
273
			}
274 13
		}
275
		return false;
276 13
	}
277
278
	/**
279
	 * Loads UrlManager instance from cache.
280
	 * @return TUrlManager intance if load was successful, null otherwise.
281
	 */
282
	protected function loadCachedUrlManager()
283
	{
284
		if ($this->getEnableCache()) {
285
			$cache = $this->getApplication()->getCache();
286
			if ($cache !== null) {
287
				$manager = $cache->get($this->getCacheKey());
288
				if ($manager instanceof TUrlManager) {
289 13
					return $manager;
290
				}
291
			}
292
		}
293
		return null;
294
	}
295
296 13
	/**
297
	 * @return string the ID of the URL manager module
298 13
	 */
299
	public function getUrlManager()
300
	{
301
		return $this->_urlManagerID;
302
	}
303
304
	/**
305
	 * Sets the URL manager module.
306
	 * By default, {@link TUrlManager} is used for managing URLs.
307 13
	 * You may specify a different module for URL managing tasks
308
	 * by loading it as an application module and setting this property
309
	 * with the module ID.
310
	 * @param string $value the ID of the URL manager module
311
	 */
312
	public function setUrlManager($value)
313 3
	{
314
		$this->_urlManagerID = $value;
315 3
	}
316
317
	/**
318
	 * @return TUrlManager the URL manager module
319
	 */
320
	public function getUrlManagerModule()
321
	{
322
		if ($this->_urlManager === null) {
323
			if (($this->_urlManager = $this->loadCachedUrlManager()) === null) {
324
				if (empty($this->_urlManagerID)) {
325
					$this->_urlManager = new TUrlManager();
326 2
					$this->_urlManager->init(null);
327
				} else {
328 2
					$this->_urlManager = $this->getApplication()->getModule($this->_urlManagerID);
329 2
					if ($this->_urlManager === null) {
330
						throw new TConfigurationException('httprequest_urlmanager_inexist', $this->_urlManagerID);
331
					}
332
					if (!($this->_urlManager instanceof TUrlManager)) {
333
						throw new TConfigurationException('httprequest_urlmanager_invalid', $this->_urlManagerID);
334 13
					}
335
				}
336 13
				$this->cacheUrlManager($this->_urlManager);
337 13
			}
338 13
		}
339 11
		return $this->_urlManager;
340 11
	}
341
342 2
	/**
343 2
	 * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get.
344 1
	 */
345
	public function getUrlFormat()
346 2
	{
347 1
		return $this->_urlFormat;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_urlFormat returns the type string which is incompatible with the documented return type Prado\Web\THttpRequestUrlFormat.
Loading history...
348
	}
349
350 13
	/**
351
	 * Sets the format of URLs constructed and interpretted by the request module.
352
	 * A Get URL format is like index.php?name1=value1&name2=value2
353 13
	 * while a Path URL format is like index.php/name1,value1/name2,value.
354
	 * Changing the UrlFormat will affect {@link constructUrl} and how GET variables
355
	 * are parsed.
356
	 * @param THttpRequestUrlFormat $value the format of URLs.
357
	 */
358
	public function setUrlFormat($value)
359 13
	{
360
		$this->_urlFormat = TPropertyValue::ensureEnum($value, THttpRequestUrlFormat::class);
361 13
	}
362
363
	/**
364
	 * @return THttpRequestResolveMethod the method used to determine the service used to
365
	 * handle the request. Defaults to THttpRequestResolveMethod::ParameterOrder.
366
	 * @since 4.2.2
367
	 */
368
	public function getResolveMethod()
369
	{
370
		return $this->_resolveMethod;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_resolveMethod returns the type string which is incompatible with the documented return type Prado\Web\THttpRequestResolveMethod.
Loading history...
371
	}
372 2
373
	/**
374 2
	 * Sets the method used to determine the service used to handle the request.
375 2
	 * This can be done using the service order defined in application.xml or
376
	 * the order of the parameters received in the request.
377
	 * @param THttpRequestResolveMethod $value the format of URLs.
378
	 * @since 4.2.2
379
	 */
380 1
	public function setResolveMethod($value)
381
	{
382 1
		$this->_resolveMethod = TPropertyValue::ensureEnum($value, THttpRequestResolveMethod::class);
383
	}
384
385
	/**
386
	 * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to comma ','.
387
	 */
388
	public function getUrlParamSeparator()
389 1
	{
390
		return $this->_separator;
391 1
	}
392 1
393
	/**
394 1
	 * @param string $value separator used to separate GET variable name and value when URL format is Path.
395
	 * @throws TInvalidDataValueException if the separator is not a single character
396 1
	 */
397
	public function setUrlParamSeparator($value)
398
	{
399
		if (strlen($value) === 1) {
400
			$this->_separator = $value;
401 1
		} else {
402
			throw new TInvalidDataValueException('httprequest_separator_invalid');
403 1
		}
404
	}
405
406
	/**
407
	 * @return string request type, can be GET, POST, HEAD, or PUT
408
	 */
409
	public function getRequestType()
410
	{
411
		return $_SERVER['REQUEST_METHOD'] ?? null;
412
	}
413
414
	/**
415
	 * @param bool $mimetypeOnly whether to return only the mimetype (default: true)
416
	 * @return string content type (e.g. 'application/json' or 'text/html; encoding=gzip') or null if not specified
417
	 */
418
	public function getContentType($mimetypeOnly = true)
419
	{
420
		if (!isset($_SERVER['CONTENT_TYPE'])) {
421
			return null;
422
		}
423
424
		if ($mimetypeOnly === true && ($_pos = strpos(';', $_SERVER['CONTENT_TYPE'])) !== false) {
425
			return substr($_SERVER['CONTENT_TYPE'], 0, $_pos);
426 4
		}
427
428 4
		return $_SERVER['CONTENT_TYPE'];
429
	}
430
431
	/**
432
	 * @return bool if the request is sent via secure channel (https)
433
	 */
434 12
	public function getIsSecureConnection()
435
	{
436 12
		return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off');
437
	}
438
439
	/**
440
	 * @return string part of the request URL after script name and before question mark.
441
	 */
442 1
	public function getPathInfo()
443
	{
444 1
		return $this->_pathInfo;
445
	}
446
447
	/**
448
	 * @return string part of that request URL after the question mark
449
	 */
450
	public function getQueryString()
451
	{
452
		return $_SERVER['QUERY_STRING'] ?? null;
453
	}
454
455
	/**
456
	 * @return string the requested http procolol. Blank string if not defined.
457
	 */
458
	public function getHttpProtocolVersion()
459
	{
460
		return $_SERVER['SERVER_PROTOCOL'] ?? null;
461
	}
462
463
	/**
464
	 * @param null|int $case Either {@link CASE_UPPER} or {@link CASE_LOWER} or as is null (default)
465
	 * @return array
466
	 */
467
	public function getHeaders($case = null)
468
	{
469
		static $result;
470
471
		if ($result === null && function_exists('apache_request_headers')) {
472
			$result = apache_request_headers();
473
		} elseif ($result === null) {
474
			$result = [];
475
			foreach ($_SERVER as $key => $value) {
476
				if (strncasecmp($key, 'HTTP_', 5) !== 0) {
477
					continue;
478
				}
479
				$key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
480
				$result[$key] = $value;
481
			}
482
		}
483
484
		if ($case !== null) {
485
			return array_change_key_case($result, $case);
486 4
		}
487
488 4
		return $result;
489
	}
490
491
	/**
492
	 * @return string part of that request URL after the host info (including pathinfo and query string)
493
	 */
494
	public function getRequestUri()
495
	{
496
		return $this->_requestUri;
497
	}
498
499 2
	/**
500
	 * @param null|bool $forceSecureConnection whether to use HTTPS instead of HTTP even if the current request
501 2
	 * is sent via HTTP or vice versa
502 2
	 * 						null - keep current schema
503 2
	 * 						true - force https
504 2
	 * 						false - force http
505
	 * @return string schema and hostname of the requested URL
506
	 */
507 2
	public function getBaseUrl($forceSecureConnection = null)
508
	{
509
		$url = $this->getUrl();
510
		$scheme = ($forceSecureConnection) ? "https" : (($forceSecureConnection === null) ? $url->getScheme() : 'http');
511
		$host = $url->getHost();
512
		if (($port = $url->getPort())) {
513 6
			$host .= ':' . $port;
514
		}
515 6
		return $scheme . '://' . $host;
516
	}
517
518
	/**
519 6
	 * @return string entry script URL (w/o host part)
520
	 */
521
	public function getApplicationUrl()
522
	{
523
		if ($this->_cgiFix & self::CGIFIX__SCRIPT_NAME && isset($_SERVER['ORIG_SCRIPT_NAME'])) {
524
			return $_SERVER['ORIG_SCRIPT_NAME'];
525
		}
526
527
		return $_SERVER['SCRIPT_NAME'] ?? null;
528
	}
529
530 1
	/**
531
	 * @param null|bool $forceSecureConnection whether to use HTTPS instead of HTTP even if the current request
532 1
	 * is sent via HTTP or vice versa
533
	 * 						null - keep current schema
534
	 * 						true - force https
535
	 * 						false - force http
536
	 * @return string entry script URL (w/ host part)
537
	 */
538 6
	public function getAbsoluteApplicationUrl($forceSecureConnection = null)
539
	{
540 6
		return $this->getBaseUrl($forceSecureConnection) . $this->getApplicationUrl();
541
	}
542
543
	/**
544
	 * @return string application entry script file path (processed w/ realpath())
545
	 */
546 1
	public function getApplicationFilePath()
547
	{
548 1
		return realpath($_SERVER['SCRIPT_FILENAME'] ?? null);
0 ignored issues
show
Bug introduced by
It seems like $_SERVER['SCRIPT_FILENAME'] ?? null can also be of type null; however, parameter $path of realpath() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

548
		return realpath(/** @scrutinizer ignore-type */ $_SERVER['SCRIPT_FILENAME'] ?? null);
Loading history...
549
	}
550
551
	/**
552
	 * @return string server name
553
	 */
554 1
	public function getServerName()
555
	{
556 1
		return $_SERVER['SERVER_NAME'] ?? null;
557
	}
558
559
	/**
560
	 * @return int server port number
561
	 */
562 1
	public function getServerPort()
563
	{
564 1
		return $_SERVER['SERVER_PORT'] ?? null;
565
	}
566
567
	/**
568
	 * @return string URL referrer, null if not present
569
	 */
570
	public function getUrlReferrer()
571
	{
572
		return $_SERVER['HTTP_REFERER'] ?? null;
573
	}
574
575
	/**
576
	 * @return string server software
577
	 * @since 3.3.3
578
	 */
579
	public function getServerSoftware()
580
	{
581
		return $_SERVER['SERVER_SOFTWARE'] ?? null;
582
	}
583
584
	/**
585
	 * @return array user browser capabilities
586
	 * @see get_browser
587
	 */
588
	public function getBrowser()
589
	{
590
		try {
591
			return get_browser();
0 ignored issues
show
Bug Best Practice introduced by
The expression return get_browser() also could return the type object which is incompatible with the documented return type array.
Loading history...
592 1
		} catch (TPhpErrorException $e) {
593
			throw new TConfigurationException('httprequest_browscap_required');
594 1
		}
595
	}
596
597
	/**
598
	 * @return string user agent
599
	 */
600 1
	public function getUserAgent()
601
	{
602 1
		return $_SERVER['HTTP_USER_AGENT'] ?? null;
603
	}
604
605
	/**
606
	 * @return string user IP address
607
	 */
608 1
	public function getUserHostAddress()
609
	{
610 1
		return $_SERVER['REMOTE_ADDR'] ?? null;
611
	}
612
613
	/**
614
	 * @return string user host name, null if cannot be determined
615
	 */
616 1
	public function getUserHost()
617
	{
618
		return $_SERVER['REMOTE_HOST'] ?? null;
619 1
	}
620
621
	/**
622
	 * @return string user browser accept types
623
	 */
624
	public function getAcceptTypes()
625
	{
626
		// TBD: break it into array??
627
		return $_SERVER['HTTP_ACCEPT'] ?? null;
628
	}
629
630
	/**
631
	 * Returns a list of user preferred languages.
632
	 * The languages are returned as an array. Each array element
633
	 * represents a single language preference. The languages are ordered
634
	 * according to user preferences. The first language is the most preferred.
635
	 * @return array list of user preferred languages.
636
	 */
637 2
	public function getUserLanguages()
638
	{
639 2
		return Prado::getUserLanguages();
640
	}
641
642
	/**
643
	 * @return bool whether cookies should be validated. Defaults to false.
644
	 */
645 2
	public function getEnableCookieValidation()
646
	{
647 2
		return $this->_enableCookieValidation;
648 2
	}
649
650
	/**
651
	 * @param bool $value whether cookies should be validated.
652
	 */
653
	public function setEnableCookieValidation($value)
654
	{
655
		$this->_enableCookieValidation = TPropertyValue::ensureBoolean($value);
656
	}
657
658
	/**
659
	 * @return int whether to use ORIG_PATH_INFO and/or ORIG_SCRIPT_NAME. Defaults to 0.
660
	 * @see THttpRequest::CGIFIX__PATH_INFO, THttpRequest::CGIFIX__SCRIPT_NAME
661
	 */
662
	public function getCgiFix()
663
	{
664
		return $this->_cgiFix;
665
	}
666
667
	/**
668
	 * Enable this, if you're using PHP via CGI with php.ini setting "cgi.fix_pathinfo=1"
669
	 * and have trouble with friendly URL feature. Enable this only if you really know what you are doing!
670
	 * @param int $value enable bitwise to use ORIG_PATH_INFO and/or ORIG_SCRIPT_NAME.
671
	 * @see THttpRequest::CGIFIX__PATH_INFO, THttpRequest::CGIFIX__SCRIPT_NAME
672
	 */
673 1
	public function setCgiFix($value)
674
	{
675 1
		$this->_cgiFix = TPropertyValue::ensureInteger($value);
676 1
	}
677 1
678 1
	/**
679 1
	 * @return THttpCookieCollection list of cookies to be sent
680 1
	 */
681 1
	public function getCookies()
682
	{
683
		if ($this->_cookies === null) {
684
			$this->_cookies = new THttpCookieCollection();
685 1
			if ($this->getEnableCookieValidation()) {
686 1
				$sm = $this->getApplication()->getSecurityManager();
687
				foreach ($_COOKIE as $key => $value) {
688
					if (($value = $sm->validateData($value)) !== false) {
689
						$this->_cookies->add(new THttpCookie($key, $value));
690 1
					}
691
				}
692
			} else {
693
				foreach ($_COOKIE as $key => $value) {
694
					$this->_cookies->add(new THttpCookie($key, $value));
695
				}
696 1
			}
697
		}
698 1
		return $this->_cookies;
699
	}
700
701
	/**
702
	 * @return array list of uploaded files.
703
	 */
704 1
	public function getUploadedFiles()
705
	{
706 1
		return $_FILES;
707
	}
708
709
	/**
710
	 * @return array list of server variables.
711
	 */
712 1
	public function getServerVariables()
713
	{
714 1
		return $_SERVER;
715
	}
716
717
	/**
718
	 * @return array list of environment variables.
719
	 */
720
	public function getEnvironmentVariables()
721
	{
722
		return $_ENV;
723
	}
724
725
	/**
726
	 * Constructs a URL that can be recognized by PRADO.
727
	 * The actual construction work is done by the URL manager module.
728
	 * This method may append session information to the generated URL if needed.
729
	 * You may provide your own URL manager module by setting {@link setUrlManager UrlManager}
730
	 * to provide your own URL scheme.
731
	 *
732
	 * Note, the constructed URL does not contain the protocol and hostname part.
733
	 * You may obtain an absolute URL by prepending the constructed URL with {@link getBaseUrl BaseUrl}.
734 1
	 * @param string $serviceID service ID
735
	 * @param string $serviceParam service parameter
736 1
	 * @param null|array $getItems GET parameters, null if not needed
737 1
	 * @param bool $encodeAmpersand whether to encode the ampersand in URL, defaults to true.
738
	 * @param bool $encodeGetItems whether to encode the GET parameters (their names and values), defaults to false.
739 1
	 * @return string URL
740 1
	 * @see TUrlManager::constructUrl
741
	 */
742
	public function constructUrl($serviceID, $serviceParam, $getItems = null, $encodeAmpersand = true, $encodeGetItems = true)
743 1
	{
744
		if ($this->_cookieOnly === null) {
745
			$this->_cookieOnly = (int) ini_get('session.use_cookies') && (int) ini_get('session.use_only_cookies');
746
		}
747
		$url = $this->getUrlManagerModule()->constructUrl($serviceID, $serviceParam, $getItems, $encodeAmpersand, $encodeGetItems);
748
		if (defined('SID') && SID != '' && !$this->_cookieOnly) {
749
			return $url . (strpos($url, '?') === false ? '?' : ($encodeAmpersand ? '&amp;' : '&')) . SID;
750
		} else {
751
			return $url;
752
		}
753 11
	}
754
755 11
	/**
756
	 * Parses the request URL and returns an array of input parameters (excluding GET variables).
757
	 * You may override this method to support customized URL format.
758
	 * @return array list of input parameters, indexed by parameter names
759
	 * @see TUrlManager::parseUrl
760
	 */
761
	protected function parseUrl()
762
	{
763
		return $this->getUrlManagerModule()->parseUrl();
764
	}
765
766
	/**
767
	 * Resolves the requested service.
768 11
	 * This method implements a URL-based service resolution.
769
	 * A URL in the format of /index.php?sp=serviceID.serviceParameter
770 11
	 * will be resolved with the serviceID and the serviceParameter.
771 11
	 * Attach an event handler on onResolveRequest to provide your own service resolution.
772 11
	 * @param array $serviceIDs list of valid service IDs
773 1
	 * @return string the currently requested service ID, null if no service ID is found
774
	 * @see constructUrl
775 11
	 */
776 11
	public function resolveRequest($serviceIDs)
777 11
	{
778 11
		Prado::trace("Resolving request from " . $_SERVER['REMOTE_ADDR'], THttpRequest::class);
779 11
		$urlParams = $this->parseUrl();
780 11
		$serviceID = null;
781 11
782
		// Prepend parameters extracted from the URL so they are first in the parameters array
783
		$this->_items = array_merge($urlParams, $_GET, $_POST);
784
		$urlParams = $this->onResolveRequest($serviceIDs, $original = $urlParams);
785
		if (is_string($urlParams)) {
0 ignored issues
show
introduced by
The condition is_string($urlParams) is always false.
Loading history...
786
			$serviceID = $urlParams;
787
		} elseif ($urlParams !== false) {
0 ignored issues
show
introduced by
The condition $urlParams !== false is always true.
Loading history...
788
			if ($urlParams !== $original) {
789
				$this->_items = array_merge($urlParams, $_GET, $_POST);
790 2
			}
791
			if ($this->_resolveMethod == THttpRequestResolveMethod::ServiceOrder) {
792 2
				foreach ($serviceIDs as $id) {
793
					if ($this->contains($id)) {
794
						$serviceID = $id;
795
						break;
796
					}
797
				}
798 1
			} else {
799
				// THttpRequestResolveMethod::ParameterOrder
800 1
				foreach ($this->_items as $key => $value) {
801
					if (in_array($key, $serviceIDs)) {
802
						$serviceID = $key;
803
						break;
804
					}
805
				}
806
			}
807 12
		}
808
		$this->_requestResolved = true;
809 12
		if ($serviceID !== null) {
810 12
			$this->setServiceID($serviceID);
811
			$this->setServiceParameter($this->itemAt($serviceID));
812
		}
813
		return $serviceID;
814
	}
815 1
816
	/**
817 1
	 * Raised when resolving the request.  This is for application specific request
818
	 * resolution.  Event Handlers can return `false` for no service, a `string` of the
819
	 * service id to use, or an array replacing the $urlParams.  By returning null,
820
	 * the result is skipped and has no effect.  Event Handler results are stacked and
821
	 * results are popped until a valid result is found.  The only invalid result is
822
	 * if a string is provided that is not in the $serviceIDs.
823
	 * The lower priorities of event handlers are more important and so should be last.
824 11
	 * @param array $serviceIDs The Service IDs of the application.
825
	 * @param array $urlParams The decoded url parameters to be updated or replaced
826 11
	 * @return array|false|string
827 11
	 */
828
	public function onResolveRequest(array $serviceIDs, array $urlParams): false|string|array
829
	{
830
		$results = $this->raiseEvent('onResolveRequest', $this, new THttpRequestParameter($serviceIDs, $urlParams), TEventResults::EVENT_REVERSE);
0 ignored issues
show
Bug introduced by
Prado\TEventResults::EVENT_REVERSE of type integer is incompatible with the type Prado\numeric|null expected by parameter $responsetype of Prado\TComponent::raiseEvent(). ( Ignorable by Annotation )

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

830
		$results = $this->raiseEvent('onResolveRequest', $this, new THttpRequestParameter($serviceIDs, $urlParams), /** @scrutinizer ignore-type */ TEventResults::EVENT_REVERSE);
Loading history...
831
		while (count($results)) {
832
			$return = array_pop($results);
833
			if ((is_string($return) && in_array($return, $serviceIDs)) || is_array($return) || $return === false) {
834
				return $return;
835
			}
836 1
		}
837
		return $urlParams;
838 1
	}
839
840
	/**
841
	 * @return bool true if request is already resolved, false otherwise.
842
	 */
843
	public function getRequestResolved()
844 3
	{
845
		return $this->_requestResolved;
846 3
	}
847
848
	/**
849
	 * @return string requested service ID
850
	 */
851
	public function getServiceID()
852
	{
853
		return $this->_serviceID;
854 2
	}
855
856 2
	/**
857
	 * Sets the requested service ID.
858
	 * @param string $value requested service ID
859
	 */
860
	public function setServiceID($value)
861
	{
862 1
		$this->_serviceID = $value;
863
	}
864 1
865
	/**
866
	 * @return string requested service parameter
867
	 */
868
	public function getServiceParameter()
869
	{
870
		return $this->_serviceParam;
871
	}
872
873 13
	/**
874
	 * Sets the requested service parameter.
875 13
	 * @param string $value requested service parameter
876
	 */
877
	public function setServiceParameter($value)
878
	{
879
		$this->_serviceParam = $value;
880
	}
881
882
	//------ The following methods enable THttpRequest to be TMap-like -----
883
884 3
	/**
885
	 * Returns an iterator for traversing the items in the list.
886 3
	 * This method is required by the interface \IteratorAggregate.
887 3
	 * @return \Iterator an iterator for traversing the items in the list.
888
	 */
889
	#[\ReturnTypeWillChange]
890
	public function getIterator()
891
	{
892
		return new \ArrayIterator($this->_items);
893
	}
894
895 3
	/**
896
	 * @return int the number of items in the request
897 3
	 */
898 3
	public function getCount()
899 3
	{
900 3
		return count($this->_items);
901
	}
902 1
903
	/**
904
	 * Returns the number of items in the request.
905
	 * This method is required by \Countable interface.
906
	 * @return int number of items in the request.
907
	 */
908
	public function count(): int
909 1
	{
910
		return $this->getCount();
911 1
	}
912 1
913
	/**
914 1
	 * @return array the key list
915
	 */
916
	public function getKeys()
917
	{
918
		return array_keys($this->_items);
919
	}
920 12
921
	/**
922 12
	 * Returns the item with the specified key.
923
	 * This method is exactly the same as {@link offsetGet}.
924
	 * @param mixed $key the key
925
	 * @return mixed the element at the offset, null if no element is found at the offset
926
	 */
927
	public function itemAt($key)
928 1
	{
929
		return $this->_items[$key] ?? null;
930 1
	}
931
932
	/**
933
	 * Adds an item into the request.
934
	 * Note, if the specified key already exists, the old value will be overwritten.
935
	 * @param mixed $key
936
	 * @param mixed $value
937
	 */
938
	public function add($key, $value)
939 1
	{
940
		$this->_items[$key] = $value;
941 1
	}
942
943
	/**
944
	 * Removes an item from the request by its key.
945
	 * @param mixed $key the key of the item to be removed
946
	 * @throws \Prado\Exceptions\TInvalidOperationException if the item cannot be removed
947
	 * @return mixed the removed value, null if no such key exists.
948
	 */
949
	public function remove($key)
950 2
	{
951
		if (isset($this->_items[$key]) || array_key_exists($key, $this->_items)) {
952 2
			$value = $this->_items[$key];
953
			unset($this->_items[$key]);
954
			return $value;
955
		} else {
956
			return null;
957
		}
958
	}
959
960
	/**
961 2
	 * Removes all items in the request.
962
	 */
963 2
	public function clear()
964 2
	{
965
		foreach (array_keys($this->_items) as $key) {
966
			$this->remove($key);
967
		}
968
	}
969
970
	/**
971 1
	 * @param mixed $key the key
972
	 * @return bool whether the request contains an item with the specified key
973 1
	 */
974 1
	public function contains($key)
975
	{
976
		return isset($this->_items[$key]) || array_key_exists($key, $this->_items);
977
	}
978
979
	/**
980
	 * @return array the list of items in array
981
	 */
982
	public function toArray()
983
	{
984
		return $this->_items;
985
	}
986
987
	/**
988
	 * Returns whether there is an element at the specified offset.
989
	 * This method is required by the interface \ArrayAccess.
990
	 * @param mixed $offset the offset to check on
991
	 * @return bool
992
	 */
993
	public function offsetExists($offset): bool
994
	{
995
		return $this->contains($offset);
996
	}
997
998
	/**
999
	 * Returns the element at the specified offset.
1000
	 * This method is required by the interface \ArrayAccess.
1001
	 * @param int $offset the offset to retrieve element.
1002
	 * @return mixed the element at the offset, null if no element is found at the offset
1003
	 */
1004
	#[\ReturnTypeWillChange]
1005
	public function offsetGet($offset)
1006
	{
1007
		return $this->itemAt($offset);
1008
	}
1009
1010
	/**
1011
	 * Sets the element at the specified offset.
1012
	 * This method is required by the interface \ArrayAccess.
1013
	 * @param int $offset the offset to set element
1014
	 * @param mixed $item the element value
1015
	 */
1016
	public function offsetSet($offset, $item): void
1017
	{
1018
		$this->add($offset, $item);
1019
	}
1020
1021
	/**
1022
	 * Unsets the element at the specified offset.
1023
	 * This method is required by the interface \ArrayAccess.
1024
	 * @param mixed $offset the offset to unset element
1025
	 */
1026
	public function offsetUnset($offset): void
1027
	{
1028
		$this->remove($offset);
1029
	}
1030
}
1031