THttpRequest::init()   B
last analyzed

Complexity

Conditions 11
Paths 36

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 13.8217

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 11
eloc 23
c 2
b 0
f 0
nc 36
nop 1
dl 0
loc 38
ccs 15
cts 21
cp 0.7143
crap 13.8217
rs 7.3166

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
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
introduced by
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
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

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
introduced by
The condition is_string($urlParams) is always false.
Loading history...
791
			$serviceID = $urlParams;
792 2
		} elseif ($urlParams !== false) {
0 ignored issues
show
introduced by
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
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

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