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

THttpRequest::onResolveRequest()   A

Complexity

Conditions 6
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 6
c 0
b 0
f 0
nc 3
nop 2
dl 0
loc 10
rs 9.2222
ccs 1
cts 1
cp 1
crap 6
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