Passed
Push — fix_868 ( ce4a33...40f1e3 )
by Fabio
09:50 queued 05:12
created

THttpRequest::setResolveMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
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
20
/**
21
 * THttpRequest class
22
 *
23
 * THttpRequest provides storage and access scheme for user request sent via HTTP.
24
 * It also encapsulates a uniform way to parse and construct URLs.
25
 *
26
 * User post data can be retrieved from THttpRequest by using it like an associative array.
27
 * For example, to test if a user supplies a variable named 'param1', you can use,
28
 * <code>
29
 *   if(isset($request['param1'])) ...
30
 *   // equivalent to:
31
 *   // if($request->contains('param1')) ...
32
 * </code>
33
 * To get the value of 'param1', use,
34
 * <code>
35
 *   $value=$request['param1'];
36
 *   // equivalent to:
37
 *   //   $value=$request->itemAt('param1');
38
 * </code>
39
 * To traverse the user post data, use
40
 * <code>
41
 *   foreach($request as $name=>$value) ...
42
 * </code>
43
 * Note, POST and GET variables are merged together in THttpRequest.
44
 * If a variable name appears in both POST and GET data, then POST data
45
 * takes precedence.
46
 *
47
 * To construct a URL that can be recognized by Prado, use {@link constructUrl()}.
48
 * The format of the recognizable URLs is determined according to
49
 * {@link setUrlManager UrlManager}. By default, the following two formats
50
 * are recognized:
51
 * <code>
52
 * /index.php?ServiceID=ServiceParameter&Name1=Value1&Name2=Value2
53
 * /index.php/ServiceID,ServiceParameter/Name1,Value1/Name2,Value2
54
 * </code>
55
 * The first format is called 'Get' while the second 'Path', which is specified
56
 * via {@link setUrlFormat UrlFormat}. For advanced users who want to use
57
 * their own URL formats, they can write customized URL management modules
58
 * and install the managers as application modules and set {@link setUrlManager UrlManager}.
59
 *
60
 * The ServiceID in the above URLs is as defined in the application configuration
61
 * (e.g. the default page service's service ID is 'page').
62
 * As a consequence, your GET variable names should not conflict with the service
63
 * IDs that your application supports.
64
 *
65
 * THttpRequest also provides the cookies sent by the user, user information such
66
 * as his browser capabilities, accepted languages, etc.
67
 *
68
 * By default, THttpRequest is registered with {@link TApplication} as the
69
 * request module. It can be accessed via {@link TApplication::getRequest()}.
70
 *
71
 * @author Qiang Xue <[email protected]>
72
 * @since 3.0
73
 */
74
class THttpRequest extends \Prado\TApplicationComponent implements \IteratorAggregate, \ArrayAccess, \Countable, \Prado\IModule
75
{
76
	public const CGIFIX__PATH_INFO = 1;
77
	public const CGIFIX__SCRIPT_NAME = 2;
78
	/**
79
	 * @var TUrlManager the URL manager module
80
	 */
81
	private $_urlManager;
82
	/**
83
	 * @var string the ID of the URL manager module
84
	 */
85
	private $_urlManagerID = '';
86
	/**
87
	 * @var string Separator used to separate GET variable name and value when URL format is Path.
88
	 */
89
	private $_separator = ',';
90
	/**
91
	 * @var string requested service ID
92
	 */
93
	private $_serviceID;
94
	/**
95
	 * @var string requested service parameter
96
	 */
97
	private $_serviceParam;
98
	/**
99
	 * @var THttpCookieCollection cookies sent from user
100
	 */
101
	private $_cookies;
102
	/**
103
	 * @var string requested URI (URL w/o host info)
104
	 */
105
	private $_requestUri;
106
	/**
107
	 * @var string path info of URL
108
	 */
109
	private $_pathInfo;
110
	/**
111
	 * @var bool whether the session ID should be kept in cookie only
112
	 */
113
	private $_cookieOnly;
114
	private $_urlFormat = THttpRequestUrlFormat::Get;
115
	private $_resolveMethod = THttpRequestResolveMethod::ParameterOrder;
116
	private $_services;
0 ignored issues
show
introduced by
The private property $_services is not used, and could be removed.
Loading history...
117
	private $_requestResolved = false;
118
	private $_enableCookieValidation = false;
119
	private $_cgiFix = 0;
120
	/**
121
	 * @var bool whether to cache the TUrlManager class (useful with a lot of TUrlMappings)
122
	 */
123
	private $_enableCache = false;
124
	/**
125
	 * @var string request URL
126
	 */
127
	private $_url;
128
129
	/**
130
	 * @var string module id
131
	 */
132
	private $_id;
133
134
	/**
135
	 * @var array contains all request variables
136
	 */
137
	private $_items = [];
138
139
	/**
140
	 * @return string id of this module
141
	 */
142
	public function getID()
143
	{
144
		return $this->_id;
145
	}
146
147
	/**
148
	 * @param string $value id of this module
149
	 */
150
	public function setID($value)
151
	{
152
		$this->_id = $value;
153
	}
154
155
	/**
156
	 * Initializes the module.
157
	 * This method is required by IModule and is invoked by application.
158
	 * @param \Prado\Xml\TXmlElement $config module configuration
159
	 */
160
	public function init($config)
161
	{
162
		// Fill in default request info when the script is run in command line
163
		if (php_sapi_name() === 'cli') {
164
			$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
165
			$_SERVER['REQUEST_METHOD'] = 'GET';
166
			$_SERVER['SERVER_NAME'] = 'localhost';
167
			$_SERVER['SERVER_PORT'] = 80;
168
			$_SERVER['HTTP_USER_AGENT'] = '';
169
		}
170
171
		// Info about server variables:
172
		// PHP_SELF contains real URI (w/ path info, w/o query string)
173
		// SCRIPT_NAME is the real URI for the requested script (w/o path info and query string)
174
		// QUERY_STRING is the string following the '?' in the ur (eg the a=x part in http://foo/bar?a=x)
175
		// REQUEST_URI contains the URI part entered in the browser address bar
176
		// SCRIPT_FILENAME is the file path to the executing script
177
		if (isset($_SERVER['REQUEST_URI'])) {
178
			$this->_requestUri = $_SERVER['REQUEST_URI'];
179
		} else {  // TBD: in this case, SCRIPT_NAME need to be escaped
180
			$this->_requestUri = $_SERVER['SCRIPT_NAME'] . (empty($_SERVER['QUERY_STRING']) ? '' : '?' . $_SERVER['QUERY_STRING']);
181
		}
182
183
		if ($this->_cgiFix & self::CGIFIX__PATH_INFO && isset($_SERVER['ORIG_PATH_INFO'])) {
184
			$this->_pathInfo = substr($_SERVER['ORIG_PATH_INFO'], strlen($_SERVER['SCRIPT_NAME']));
185
		} elseif (isset($_SERVER['PATH_INFO'])) {
186
			$this->_pathInfo = $_SERVER['PATH_INFO'];
187
		} elseif (strpos($_SERVER['PHP_SELF'], $_SERVER['SCRIPT_NAME']) === 0 && $_SERVER['PHP_SELF'] !== $_SERVER['SCRIPT_NAME']) {
188
			$this->_pathInfo = substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME']));
189
		} else {
190
			$this->_pathInfo = '';
191
		}
192
193
		$this->getApplication()->setRequest($this);
194
	}
195
196
	/**
197
	 * Strips slashes from input data.
198
	 * This method is applied when magic quotes is enabled.
199
	 * @param mixed $data by reference, input data to be processed
200
	 * @return mixed processed data
201
	 * @deprecated useless since get_magic_quotes_gpc() is unavailable from php 5.4
202
	 */
203
	public function stripSlashes(&$data)
204
	{
205
		return is_array($data) ? array_map([$this, 'stripSlashes'], $data) : stripslashes($data);
206
	}
207
208
	/**
209
	 * @return TUri the request URL
210
	 */
211
	public function getUrl()
212
	{
213
		if ($this->_url === null) {
214
			$secure = $this->getIsSecureConnection();
215
			$url = $secure ? 'https://' : 'http://';
216
			if (empty($_SERVER['HTTP_HOST'])) {
217
				$url .= $_SERVER['SERVER_NAME'];
218
				$port = $_SERVER['SERVER_PORT'];
219
				if (($port != 80 && !$secure) || ($port != 443 && $secure)) {
220
					$url .= ':' . $port;
221
				}
222
			} else {
223
				$url .= $_SERVER['HTTP_HOST'];
224
			}
225
			$url .= $this->getRequestUri();
226
			$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...
227
		}
228
		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...
229
	}
230
231
	/**
232
	 * Set true to cache the UrlManager instance. Consider to enable this cache
233
	 * when the application defines a lot of TUrlMappingPatterns
234
	 * @param bool $value true to cache urlmanager instance.
235
	 */
236
	public function setEnableCache($value)
237
	{
238
		$this->_enableCache = TPropertyValue::ensureBoolean($value);
239
	}
240
241
	/**
242
	 * @return bool true if urlmanager instance should be cached, false otherwise.
243
	 */
244
	public function getEnableCache()
245
	{
246
		return $this->_enableCache;
247
	}
248
249
	protected function getCacheKey()
250
	{
251
		return $this->getID();
252
	}
253
254
	/**
255
	 * Saves the current UrlManager instance to cache.
256
	 * @param mixed $manager
257
	 * @return bool true if UrlManager instance was cached, false otherwise.
258
	 */
259
	protected function cacheUrlManager($manager)
260
	{
261
		if ($this->getEnableCache()) {
262
			$cache = $this->getApplication()->getCache();
263
			if ($cache !== null) {
264
				$dependencies = null;
265
				if ($this->getApplication()->getMode() !== TApplicationMode::Performance) {
266
					if ($manager instanceof TUrlMapping && $fn = $manager->getConfigFile()) {
267
						$fn = Prado::getPathOfNamespace($fn, $this->getApplication()->getConfigurationFileExt());
268
						$dependencies = new TFileCacheDependency($fn);
269
					}
270
				}
271
				return $cache->set($this->getCacheKey(), $manager, 0, $dependencies);
272
			}
273
		}
274
		return false;
275
	}
276
277
	/**
278
	 * Loads UrlManager instance from cache.
279
	 * @return TUrlManager intance if load was successful, null otherwise.
280
	 */
281
	protected function loadCachedUrlManager()
282
	{
283
		if ($this->getEnableCache()) {
284
			$cache = $this->getApplication()->getCache();
285
			if ($cache !== null) {
286
				$manager = $cache->get($this->getCacheKey());
287
				if ($manager instanceof TUrlManager) {
288
					return $manager;
289
				}
290
			}
291
		}
292
		return null;
293
	}
294
295
	/**
296
	 * @return string the ID of the URL manager module
297
	 */
298
	public function getUrlManager()
299
	{
300
		return $this->_urlManagerID;
301
	}
302
303
	/**
304
	 * Sets the URL manager module.
305
	 * By default, {@link TUrlManager} is used for managing URLs.
306
	 * You may specify a different module for URL managing tasks
307
	 * by loading it as an application module and setting this property
308
	 * with the module ID.
309
	 * @param string $value the ID of the URL manager module
310
	 */
311
	public function setUrlManager($value)
312
	{
313
		$this->_urlManagerID = $value;
314
	}
315
316
	/**
317
	 * @return TUrlManager the URL manager module
318
	 */
319
	public function getUrlManagerModule()
320
	{
321
		if ($this->_urlManager === null) {
322
			if (($this->_urlManager = $this->loadCachedUrlManager()) === null) {
323
				if (empty($this->_urlManagerID)) {
324
					$this->_urlManager = new TUrlManager();
325
					$this->_urlManager->init(null);
326
				} else {
327
					$this->_urlManager = $this->getApplication()->getModule($this->_urlManagerID);
328
					if ($this->_urlManager === null) {
329
						throw new TConfigurationException('httprequest_urlmanager_inexist', $this->_urlManagerID);
330
					}
331
					if (!($this->_urlManager instanceof TUrlManager)) {
332
						throw new TConfigurationException('httprequest_urlmanager_invalid', $this->_urlManagerID);
333
					}
334
				}
335
				$this->cacheUrlManager($this->_urlManager);
336
			}
337
		}
338
		return $this->_urlManager;
339
	}
340
341
	/**
342
	 * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get.
343
	 */
344
	public function getUrlFormat()
345
	{
346
		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...
347
	}
348
349
	/**
350
	 * Sets the format of URLs constructed and interpretted by the request module.
351
	 * A Get URL format is like index.php?name1=value1&name2=value2
352
	 * while a Path URL format is like index.php/name1,value1/name2,value.
353
	 * Changing the UrlFormat will affect {@link constructUrl} and how GET variables
354
	 * are parsed.
355
	 * @param THttpRequestUrlFormat $value the format of URLs.
356
	 */
357
	public function setUrlFormat($value)
358
	{
359
		$this->_urlFormat = TPropertyValue::ensureEnum($value, 'Prado\\Web\\THttpRequestUrlFormat');
360
	}
361
362
	/**
363
	 * @return THttpRequestResolveMethod the method used to determine the service used to
364
	 * handle the request. Defaults to THttpRequestResolveMethod::ParameterOrder.
365
	 * @since 4.2.2
366
	 */
367
	public function getResolveMethod()
368
	{
369
		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...
370
	}
371
372
	/**
373
	 * Sets the method used to determine the service used to handle the request.
374
	 * This can be done using the service order defined in application.xml or
375
	 * the order of the parameters received in the request.
376
	 * @param THttpRequestResolveMethod $value the format of URLs.
377
	 * @since 4.2.2
378
	 */
379
	public function setResolveMethod($value)
380
	{
381
		$this->_resolveMethod = TPropertyValue::ensureEnum($value, 'Prado\\Web\\THttpRequestResolveMethod');
382
	}
383
384
	/**
385
	 * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to comma ','.
386
	 */
387
	public function getUrlParamSeparator()
388
	{
389
		return $this->_separator;
390
	}
391
392
	/**
393
	 * @param string $value separator used to separate GET variable name and value when URL format is Path.
394
	 * @throws TInvalidDataValueException if the separator is not a single character
395
	 */
396
	public function setUrlParamSeparator($value)
397
	{
398
		if (strlen($value) === 1) {
399
			$this->_separator = $value;
400
		} else {
401
			throw new TInvalidDataValueException('httprequest_separator_invalid');
402
		}
403
	}
404
405
	/**
406
	 * @return string request type, can be GET, POST, HEAD, or PUT
407
	 */
408
	public function getRequestType()
409
	{
410
		return $_SERVER['REQUEST_METHOD'] ?? null;
411
	}
412
413
	/**
414
	 * @param bool $mimetypeOnly whether to return only the mimetype (default: true)
415
	 * @return string content type (e.g. 'application/json' or 'text/html; encoding=gzip') or null if not specified
416
	 */
417
	public function getContentType($mimetypeOnly = true)
418
	{
419
		if (!isset($_SERVER['CONTENT_TYPE'])) {
420
			return null;
421
		}
422
423
		if ($mimetypeOnly === true && ($_pos = strpos(';', $_SERVER['CONTENT_TYPE'])) !== false) {
424
			return substr($_SERVER['CONTENT_TYPE'], 0, $_pos);
425
		}
426
427
		return $_SERVER['CONTENT_TYPE'];
428
	}
429
430
	/**
431
	 * @return bool if the request is sent via secure channel (https)
432
	 */
433
	public function getIsSecureConnection()
434
	{
435
		return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off');
436
	}
437
438
	/**
439
	 * @return string part of the request URL after script name and before question mark.
440
	 */
441
	public function getPathInfo()
442
	{
443
		return $this->_pathInfo;
444
	}
445
446
	/**
447
	 * @return string part of that request URL after the question mark
448
	 */
449
	public function getQueryString()
450
	{
451
		return $_SERVER['QUERY_STRING'] ?? null;
452
	}
453
454
	/**
455
	 * @return string the requested http procolol. Blank string if not defined.
456
	 */
457
	public function getHttpProtocolVersion()
458
	{
459
		return $_SERVER['SERVER_PROTOCOL'] ?? null;
460
	}
461
462
	/**
463
	 * @param null|int $case Either {@link CASE_UPPER} or {@link CASE_LOWER} or as is null (default)
464
	 * @return array
465
	 */
466
	public function getHeaders($case = null)
467
	{
468
		static $result;
469
470
		if ($result === null && function_exists('apache_request_headers')) {
471
			$result = apache_request_headers();
472
		} elseif ($result === null) {
473
			$result = [];
474
			foreach ($_SERVER as $key => $value) {
475
				if (strncasecmp($key, 'HTTP_', 5) !== 0) {
476
					continue;
477
				}
478
				$key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
479
				$result[$key] = $value;
480
			}
481
		}
482
483
		if ($case !== null) {
484
			return array_change_key_case($result, $case);
485
		}
486
487
		return $result;
488
	}
489
490
	/**
491
	 * @return string part of that request URL after the host info (including pathinfo and query string)
492
	 */
493
	public function getRequestUri()
494
	{
495
		return $this->_requestUri;
496
	}
497
498
	/**
499
	 * @param null|bool $forceSecureConnection whether to use HTTPS instead of HTTP even if the current request
500
	 * is sent via HTTP or vice versa
501
	 * 						null - keep current schema
502
	 * 						true - force https
503
	 * 						false - force http
504
	 * @return string schema and hostname of the requested URL
505
	 */
506
	public function getBaseUrl($forceSecureConnection = null)
507
	{
508
		$url = $this->getUrl();
509
		$scheme = ($forceSecureConnection) ? "https" : (($forceSecureConnection === null) ? $url->getScheme() : 'http');
510
		$host = $url->getHost();
511
		if (($port = $url->getPort())) {
512
			$host .= ':' . $port;
513
		}
514
		return $scheme . '://' . $host;
515
	}
516
517
	/**
518
	 * @return string entry script URL (w/o host part)
519
	 */
520
	public function getApplicationUrl()
521
	{
522
		if ($this->_cgiFix & self::CGIFIX__SCRIPT_NAME && isset($_SERVER['ORIG_SCRIPT_NAME'])) {
523
			return $_SERVER['ORIG_SCRIPT_NAME'];
524
		}
525
526
		return $_SERVER['SCRIPT_NAME'] ?? null;
527
	}
528
529
	/**
530
	 * @param null|bool $forceSecureConnection whether to use HTTPS instead of HTTP even if the current request
531
	 * is sent via HTTP or vice versa
532
	 * 						null - keep current schema
533
	 * 						true - force https
534
	 * 						false - force http
535
	 * @return string entry script URL (w/ host part)
536
	 */
537
	public function getAbsoluteApplicationUrl($forceSecureConnection = null)
538
	{
539
		return $this->getBaseUrl($forceSecureConnection) . $this->getApplicationUrl();
540
	}
541
542
	/**
543
	 * @return string application entry script file path (processed w/ realpath())
544
	 */
545
	public function getApplicationFilePath()
546
	{
547
		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

547
		return realpath(/** @scrutinizer ignore-type */ $_SERVER['SCRIPT_FILENAME'] ?? null);
Loading history...
548
	}
549
550
	/**
551
	 * @return string server name
552
	 */
553
	public function getServerName()
554
	{
555
		return $_SERVER['SERVER_NAME'] ?? null;
556
	}
557
558
	/**
559
	 * @return int server port number
560
	 */
561
	public function getServerPort()
562
	{
563
		return $_SERVER['SERVER_PORT'] ?? null;
564
	}
565
566
	/**
567
	 * @return string URL referrer, null if not present
568
	 */
569
	public function getUrlReferrer()
570
	{
571
		return $_SERVER['HTTP_REFERER'] ?? null;
572
	}
573
574
	/**
575
	 * @return string server software
576
	 * @since 3.3.3
577
	 */
578
	public function getServerSoftware()
579
	{
580
		return $_SERVER['SERVER_SOFTWARE'] ?? null;
581
	}
582
583
	/**
584
	 * @return array user browser capabilities
585
	 * @see get_browser
586
	 */
587
	public function getBrowser()
588
	{
589
		try {
590
			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...
591
		} catch (TPhpErrorException $e) {
592
			throw new TConfigurationException('httprequest_browscap_required');
593
		}
594
	}
595
596
	/**
597
	 * @return string user agent
598
	 */
599
	public function getUserAgent()
600
	{
601
		return $_SERVER['HTTP_USER_AGENT'] ?? null;
602
	}
603
604
	/**
605
	 * @return string user IP address
606
	 */
607
	public function getUserHostAddress()
608
	{
609
		return $_SERVER['REMOTE_ADDR'] ?? null;
610
	}
611
612
	/**
613
	 * @return string user host name, null if cannot be determined
614
	 */
615
	public function getUserHost()
616
	{
617
		return $_SERVER['REMOTE_HOST'] ?? null;
618
	}
619
620
	/**
621
	 * @return string user browser accept types
622
	 */
623
	public function getAcceptTypes()
624
	{
625
		// TBD: break it into array??
626
		return $_SERVER['HTTP_ACCEPT'] ?? null;
627
	}
628
629
	/**
630
	 * Returns a list of user preferred languages.
631
	 * The languages are returned as an array. Each array element
632
	 * represents a single language preference. The languages are ordered
633
	 * according to user preferences. The first language is the most preferred.
634
	 * @return array list of user preferred languages.
635
	 */
636
	public function getUserLanguages()
637
	{
638
		return Prado::getUserLanguages();
639
	}
640
641
	/**
642
	 * @return bool whether cookies should be validated. Defaults to false.
643
	 */
644
	public function getEnableCookieValidation()
645
	{
646
		return $this->_enableCookieValidation;
647
	}
648
649
	/**
650
	 * @param bool $value whether cookies should be validated.
651
	 */
652
	public function setEnableCookieValidation($value)
653
	{
654
		$this->_enableCookieValidation = TPropertyValue::ensureBoolean($value);
655
	}
656
657
	/**
658
	 * @return int whether to use ORIG_PATH_INFO and/or ORIG_SCRIPT_NAME. Defaults to 0.
659
	 * @see THttpRequest::CGIFIX__PATH_INFO, THttpRequest::CGIFIX__SCRIPT_NAME
660
	 */
661
	public function getCgiFix()
662
	{
663
		return $this->_cgiFix;
664
	}
665
666
	/**
667
	 * Enable this, if you're using PHP via CGI with php.ini setting "cgi.fix_pathinfo=1"
668
	 * and have trouble with friendly URL feature. Enable this only if you really know what you are doing!
669
	 * @param int $value enable bitwise to use ORIG_PATH_INFO and/or ORIG_SCRIPT_NAME.
670
	 * @see THttpRequest::CGIFIX__PATH_INFO, THttpRequest::CGIFIX__SCRIPT_NAME
671
	 */
672
	public function setCgiFix($value)
673
	{
674
		$this->_cgiFix = TPropertyValue::ensureInteger($value);
675
	}
676
677
	/**
678
	 * @return THttpCookieCollection list of cookies to be sent
679
	 */
680
	public function getCookies()
681
	{
682
		if ($this->_cookies === null) {
683
			$this->_cookies = new THttpCookieCollection();
684
			if ($this->getEnableCookieValidation()) {
685
				$sm = $this->getApplication()->getSecurityManager();
686
				foreach ($_COOKIE as $key => $value) {
687
					if (($value = $sm->validateData($value)) !== false) {
688
						$this->_cookies->add(new THttpCookie($key, $value));
689
					}
690
				}
691
			} else {
692
				foreach ($_COOKIE as $key => $value) {
693
					$this->_cookies->add(new THttpCookie($key, $value));
694
				}
695
			}
696
		}
697
		return $this->_cookies;
698
	}
699
700
	/**
701
	 * @return array list of uploaded files.
702
	 */
703
	public function getUploadedFiles()
704
	{
705
		return $_FILES;
706
	}
707
708
	/**
709
	 * @return array list of server variables.
710
	 */
711
	public function getServerVariables()
712
	{
713
		return $_SERVER;
714
	}
715
716
	/**
717
	 * @return array list of environment variables.
718
	 */
719
	public function getEnvironmentVariables()
720
	{
721
		return $_ENV;
722
	}
723
724
	/**
725
	 * Constructs a URL that can be recognized by PRADO.
726
	 * The actual construction work is done by the URL manager module.
727
	 * This method may append session information to the generated URL if needed.
728
	 * You may provide your own URL manager module by setting {@link setUrlManager UrlManager}
729
	 * to provide your own URL scheme.
730
	 *
731
	 * Note, the constructed URL does not contain the protocol and hostname part.
732
	 * You may obtain an absolute URL by prepending the constructed URL with {@link getBaseUrl BaseUrl}.
733
	 * @param string $serviceID service ID
734
	 * @param string $serviceParam service parameter
735
	 * @param null|array $getItems GET parameters, null if not needed
736
	 * @param bool $encodeAmpersand whether to encode the ampersand in URL, defaults to true.
737
	 * @param bool $encodeGetItems whether to encode the GET parameters (their names and values), defaults to false.
738
	 * @return string URL
739
	 * @see TUrlManager::constructUrl
740
	 */
741
	public function constructUrl($serviceID, $serviceParam, $getItems = null, $encodeAmpersand = true, $encodeGetItems = true)
742
	{
743
		if ($this->_cookieOnly === null) {
744
			$this->_cookieOnly = (int) ini_get('session.use_cookies') && (int) ini_get('session.use_only_cookies');
745
		}
746
		$url = $this->getUrlManagerModule()->constructUrl($serviceID, $serviceParam, $getItems, $encodeAmpersand, $encodeGetItems);
747
		if (defined('SID') && SID != '' && !$this->_cookieOnly) {
748
			return $url . (strpos($url, '?') === false ? '?' : ($encodeAmpersand ? '&amp;' : '&')) . SID;
749
		} else {
750
			return $url;
751
		}
752
	}
753
754
	/**
755
	 * Parses the request URL and returns an array of input parameters (excluding GET variables).
756
	 * You may override this method to support customized URL format.
757
	 * @return array list of input parameters, indexed by parameter names
758
	 * @see TUrlManager::parseUrl
759
	 */
760
	protected function parseUrl()
761
	{
762
		return $this->getUrlManagerModule()->parseUrl();
763
	}
764
765
	/**
766
	 * Resolves the requested service.
767
	 * This method implements a URL-based service resolution.
768
	 * A URL in the format of /index.php?sp=serviceID.serviceParameter
769
	 * will be resolved with the serviceID and the serviceParameter.
770
	 * You may override this method to provide your own way of service resolution.
771
	 * @param array $serviceIDs list of valid service IDs
772
	 * @return string the currently requested service ID, null if no service ID is found
773
	 * @see constructUrl
774
	 */
775
	public function resolveRequest($serviceIDs)
776
	{
777
		Prado::trace("Resolving request from " . $_SERVER['REMOTE_ADDR'], 'Prado\Web\THttpRequest');
778
		$getParams = $this->parseUrl();
779
780
		if ($this->_resolveMethod == THttpRequestResolveMethod::ServiceOrder) {
781
			foreach ($getParams as $name => $value) {
782
				$_GET[$name] = $value;
783
			}
784
			$this->_items = array_merge($_GET, $_POST);
785
			$this->_requestResolved = true;
786
			foreach ($serviceIDs as $serviceID) {
787
				if ($this->contains($serviceID)) {
788
					$this->setServiceID($serviceID);
789
					$this->setServiceParameter($this->itemAt($serviceID));
790
					return $serviceID;
791
				}
792
			}
793
		} else {
794
			// THttpRequestResolveMethod::ParameterOrder
795
			// Prepend parameters extracted from the URL so they gets first in the parameters array
796
			$this->_items = array_merge($getParams, $_GET, $_POST);
797
			$this->_requestResolved = true;
798
			foreach ($this->_items as $serviceID => $value) {
799
				if (in_array($serviceID, $serviceIDs)) {
800
					$this->setServiceID($serviceID);
801
					$this->setServiceParameter($this->itemAt($serviceID));
802
					return $serviceID;
803
				}
804
			}
805
		}
806
		return null;
807
	}
808
809
	/**
810
	 * @return bool true if request is already resolved, false otherwise.
811
	 */
812
	public function getRequestResolved()
813
	{
814
		return $this->_requestResolved;
815
	}
816
817
	/**
818
	 * @return string requested service ID
819
	 */
820
	public function getServiceID()
821
	{
822
		return $this->_serviceID;
823
	}
824
825
	/**
826
	 * Sets the requested service ID.
827
	 * @param string $value requested service ID
828
	 */
829
	public function setServiceID($value)
830
	{
831
		$this->_serviceID = $value;
832
	}
833
834
	/**
835
	 * @return string requested service parameter
836
	 */
837
	public function getServiceParameter()
838
	{
839
		return $this->_serviceParam;
840
	}
841
842
	/**
843
	 * Sets the requested service parameter.
844
	 * @param string $value requested service parameter
845
	 */
846
	public function setServiceParameter($value)
847
	{
848
		$this->_serviceParam = $value;
849
	}
850
851
	//------ The following methods enable THttpRequest to be TMap-like -----
852
853
	/**
854
	 * Returns an iterator for traversing the items in the list.
855
	 * This method is required by the interface \IteratorAggregate.
856
	 * @return \Iterator an iterator for traversing the items in the list.
857
	 */
858
	#[\ReturnTypeWillChange]
859
	public function getIterator()
860
	{
861
		return new \ArrayIterator($this->_items);
862
	}
863
864
	/**
865
	 * @return int the number of items in the request
866
	 */
867
	public function getCount()
868
	{
869
		return count($this->_items);
870
	}
871
872
	/**
873
	 * Returns the number of items in the request.
874
	 * This method is required by \Countable interface.
875
	 * @return int number of items in the request.
876
	 */
877
	public function count(): int
878
	{
879
		return $this->getCount();
880
	}
881
882
	/**
883
	 * @return array the key list
884
	 */
885
	public function getKeys()
886
	{
887
		return array_keys($this->_items);
888
	}
889
890
	/**
891
	 * Returns the item with the specified key.
892
	 * This method is exactly the same as {@link offsetGet}.
893
	 * @param mixed $key the key
894
	 * @return mixed the element at the offset, null if no element is found at the offset
895
	 */
896
	public function itemAt($key)
897
	{
898
		return $this->_items[$key] ?? null;
899
	}
900
901
	/**
902
	 * Adds an item into the request.
903
	 * Note, if the specified key already exists, the old value will be overwritten.
904
	 * @param mixed $key
905
	 * @param mixed $value
906
	 */
907
	public function add($key, $value)
908
	{
909
		$this->_items[$key] = $value;
910
	}
911
912
	/**
913
	 * Removes an item from the request by its key.
914
	 * @param mixed $key the key of the item to be removed
915
	 * @throws \Prado\Exceptions\TInvalidOperationException if the item cannot be removed
916
	 * @return mixed the removed value, null if no such key exists.
917
	 */
918
	public function remove($key)
919
	{
920
		if (isset($this->_items[$key]) || array_key_exists($key, $this->_items)) {
921
			$value = $this->_items[$key];
922
			unset($this->_items[$key]);
923
			return $value;
924
		} else {
925
			return null;
926
		}
927
	}
928
929
	/**
930
	 * Removes all items in the request.
931
	 */
932
	public function clear()
933
	{
934
		foreach (array_keys($this->_items) as $key) {
935
			$this->remove($key);
936
		}
937
	}
938
939
	/**
940
	 * @param mixed $key the key
941
	 * @return bool whether the request contains an item with the specified key
942
	 */
943
	public function contains($key)
944
	{
945
		return isset($this->_items[$key]) || array_key_exists($key, $this->_items);
946
	}
947
948
	/**
949
	 * @return array the list of items in array
950
	 */
951
	public function toArray()
952
	{
953
		return $this->_items;
954
	}
955
956
	/**
957
	 * Returns whether there is an element at the specified offset.
958
	 * This method is required by the interface \ArrayAccess.
959
	 * @param mixed $offset the offset to check on
960
	 * @return bool
961
	 */
962
	public function offsetExists($offset): bool
963
	{
964
		return $this->contains($offset);
965
	}
966
967
	/**
968
	 * Returns the element at the specified offset.
969
	 * This method is required by the interface \ArrayAccess.
970
	 * @param int $offset the offset to retrieve element.
971
	 * @return mixed the element at the offset, null if no element is found at the offset
972
	 */
973
	#[\ReturnTypeWillChange]
974
	public function offsetGet($offset)
975
	{
976
		return $this->itemAt($offset);
977
	}
978
979
	/**
980
	 * Sets the element at the specified offset.
981
	 * This method is required by the interface \ArrayAccess.
982
	 * @param int $offset the offset to set element
983
	 * @param mixed $item the element value
984
	 */
985
	public function offsetSet($offset, $item): void
986
	{
987
		$this->add($offset, $item);
988
	}
989
990
	/**
991
	 * Unsets the element at the specified offset.
992
	 * This method is required by the interface \ArrayAccess.
993
	 * @param mixed $offset the offset to unset element
994
	 */
995
	public function offsetUnset($offset): void
996
	{
997
		$this->remove($offset);
998
	}
999
}
1000