Issues (1474)

framework/Web/THttpRequest.php (10 issues)

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

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

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