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
![]() |
|||||
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
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.. ![]() |
|||||
233 | 1 | } |
|||
234 | 1 | return $this->_url; |
|||
0 ignored issues
–
show
|
|||||
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
|
|||||
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
|
|||||
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
![]() |
|||||
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
|
|||||
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 ? '&' : '&')) . 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
|
|||||
791 | $serviceID = $urlParams; |
||||
792 | 2 | } elseif ($urlParams !== false) { |
|||
0 ignored issues
–
show
|
|||||
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
![]() |
|||||
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 |