Elgg  Version master
Request.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Http;
4 
5 use Elgg\Config;
6 use Elgg\Context;
10 use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
11 
17 class Request extends SymfonyRequest {
18 
19  const REWRITE_TEST_TOKEN = '__testing_rewrite';
20  const REWRITE_TEST_OUTPUT = 'success';
21 
26  public bool $_integration_testing = false;
27 
31  protected $context_stack;
32 
36  protected $route;
37 
41  protected $request_overrides;
42 
46  protected $filtered_params;
47 
51  protected $unfiltered_params;
52 
56  public function __construct(
57  array $query = [],
58  array $request = [],
59  array $attributes = [],
60  array $cookies = [],
61  array $files = [],
62  array $server = [],
63  $content = null
64  ) {
65  parent::__construct($query, $request, $attributes, $cookies, $files, $server, $content);
66 
67  $this->initializeContext();
68 
69  $this->request_overrides = [];
70  }
71 
80  $trusted_proxies = $config->http_request_trusted_proxy_ips;
81  if (empty($trusted_proxies)) {
82  return;
83  }
84 
85  $allowed_headers = $config->http_request_trusted_proxy_headers;
86  if (empty($allowed_headers)) {
87  $allowed_headers = self::HEADER_X_FORWARDED_FOR | self::HEADER_X_FORWARDED_HOST | self::HEADER_X_FORWARDED_PORT | self::HEADER_X_FORWARDED_PROTO;
88  }
89 
90  $this->setTrustedProxies($trusted_proxies, $allowed_headers);
91  }
92 
98  public function initializeContext() {
99  $context = new Context($this);
100  $this->context_stack = $context;
101 
102  return $this;
103  }
104 
110  public function getContextStack() {
111  return $this->context_stack;
112  }
113 
121  public function setRoute(Route $route) {
122  $this->route = $route;
123  foreach ($route->getMatchedParameters() as $key => $value) {
124  $this->setParam($key, $value);
125  }
126 
127  return $this;
128  }
129 
135  public function getRoute(): ?Route {
136  return $this->route;
137  }
138 
150  public function setParam(string $key, $value, bool $override_request = false) {
151  if ($override_request) {
152  $this->request_overrides[$key] = $value;
153  } else {
154  $this->request->set($key, $value);
155  }
156 
157  // reset the cached params
158  unset($this->filtered_params);
159  unset($this->unfiltered_params);
160 
161  return $this;
162  }
163 
177  public function getParam(string $key, $default = null, bool $filter_result = true) {
178  $values = $this->getParams($filter_result);
179 
180  return elgg_extract($key, $values, $default);
181  }
182 
190  public function getParams(bool $filter_result = true): array {
191  if (isset($this->filtered_params) && isset($this->unfiltered_params)) {
192  return $filter_result ? $this->filtered_params : $this->unfiltered_params;
193  }
194 
196  $query = $this->query->all();
197  $attributes = $this->attributes->all();
198  $post = $this->request->all();
199 
200  $this->unfiltered_params = array_merge($post, $attributes, $query, $request_overrides);
201 
202  // filter the input params
203  $this->getContextStack()->push('input');
204  $this->filtered_params = elgg_sanitize_input($this->unfiltered_params);
205  $this->getContextStack()->pop();
206 
207  return $filter_result ? $this->filtered_params : $this->unfiltered_params;
208  }
209 
218  public function getCurrentURL(): string {
219  $url = parse_url(elgg_get_site_url());
220 
221  $page = $url['scheme'] . '://' . $url['host'];
222 
223  if (isset($url['port']) && $url['port']) {
224  $page .= ':' . $url['port'];
225  }
226 
227  $page = trim($page, '/');
228 
229  $page .= $this->getRequestUri();
230 
231  return $page;
232  }
233 
241  public function getUrlSegments(bool $raw = false): array {
242  $path = trim($this->getElggPath(), '/');
243  if (!$raw) {
244  $path = htmlspecialchars($path, ENT_QUOTES, 'UTF-8');
245  }
246 
247  if (empty($path)) {
248  return [];
249  }
250 
251  return explode('/', $path);
252  }
253 
261  public function setUrlSegments(array $segments): Request {
262  $base_path = trim($this->getBasePath(), '/');
263  $server = $this->server->all();
264  $server['REQUEST_URI'] = "$base_path/" . implode('/', $segments);
265 
266  return $this->duplicate(null, null, null, null, null, $server);
267  }
268 
276  public function getFirstUrlSegment(): string {
277  $segments = $this->getUrlSegments();
278  if (!empty($segments)) {
279  return array_shift($segments);
280  }
281 
282  return '';
283  }
284 
290  public function getElggPath(): string {
291  if (PHP_SAPI === 'cli-server') {
292  $path = $this->getRequestUri();
293  } else {
294  $path = $this->getPathInfo();
295  }
296 
297  return preg_replace('~(\?.*)$~', '', $path);
298  }
299 
303  public function getClientIp(): ?string {
304  $ip = parent::getClientIp();
305 
306  if ($ip == $this->server->get('REMOTE_ADDR')) {
307  // try one more
308  $ip_addresses = $this->server->get('HTTP_X_REAL_IP');
309  if ($ip_addresses) {
310  $ip_addresses = explode(',', $ip_addresses);
311 
312  return array_pop($ip_addresses);
313  }
314  }
315 
316  return $ip;
317  }
318 
322  public function isXmlHttpRequest(): bool {
323  return (strtolower($this->headers->get('X-Requested-With') ?: '') === 'xmlhttprequest'
324  || $this->query->get('X-Requested-With') === 'XMLHttpRequest'
325  || $this->request->get('X-Requested-With') === 'XMLHttpRequest');
326  }
327 
333  public function sniffElggUrl() {
334  $base_url = $this->getBaseUrl();
335 
336  // baseURL may end with the PHP script
337  if (str_ends_with($base_url, '.php')) {
338  $base_url = dirname($base_url);
339  }
340 
341  $base_url = str_replace('\\', '/', $base_url);
342 
343  return rtrim($this->getSchemeAndHttpHost() . $base_url, '/') . '/';
344  }
345 
351  public function isRewriteCheck(): bool {
352  if ($this->getPathInfo() !== ('/' . self::REWRITE_TEST_TOKEN)) {
353  return false;
354  }
355 
356  if (!$this->get(self::REWRITE_TEST_TOKEN)) {
357  return false;
358  }
359 
360  return true;
361  }
362 
369  public function isAction(): bool {
370  return $this->getFirstUrlSegment() === 'action';
371  }
372 
378  public function isCliServer(): bool {
379  return PHP_SAPI === 'cli-server';
380  }
381 
389  public function isCliServable(string $root): bool {
390  $file = rtrim($root, '\\/') . $this->getElggPath();
391  if (!is_file($file)) {
392  return false;
393  }
394 
395  // http://php.net/manual/en/features.commandline.webserver.php
396  $extensions = '.3gp, .apk, .avi, .bmp, .css, .csv, .doc, .docx, .flac, .gif, .gz, .gzip, .htm, .html, .ics,';
397  $extensions .= ' .jpe, .jpeg, .jpg, .js, .kml, .kmz, .m4a, .mjs, .mov, .mp3, .mp4, .mpeg, .mpg, .odp, .ods, .odt,';
398  $extensions .= ' .oga, .ogg, .ogv, .pdf, .pdf, .png, .pps, .pptx, .qt, .svg, .swf, .tar, .text, .tif, .txt,';
399  $extensions .= ' .wav, .webm, .wmv, .xls, .xlsx, .xml, .xsl, .xsd, and .zip';
400 
401  // The CLI server routes ALL requests here (even existing files), so we have to check for these.
402  $ext = pathinfo($file, PATHINFO_EXTENSION);
403  if (!$ext) {
404  return false;
405  }
406 
407  $ext = preg_quote($ext, '~');
408 
409  return (bool) preg_match("~\\.{$ext}[,$]~", $extensions);
410  }
411 
419  public function getFiles(string $input_name): array {
420  $files = $this->files->get($input_name);
421  if (empty($files)) {
422  return [];
423  }
424 
425  if (!is_array($files)) {
426  $files = [$files];
427  }
428 
429  return $files;
430  }
431 
440  public function getFile(string $input_name, bool $check_for_validity = true): ?UploadedFile {
441  $files = $this->getFiles($input_name);
442  if (empty($files)) {
443  return null;
444  }
445 
446  $file = $files[0];
447  if (empty($file)) {
448  return null;
449  }
450 
451  if ($check_for_validity && !$file->isValid()) {
452  return null;
453  }
454 
455  return $file;
456  }
457 
464  public function validate(): void {
465  $this->validateRequestHostHeader();
467  }
468 
478  protected function validateRequestHostHeader(): void {
479  $config = _elgg_services()->config;
480  if (empty($config->wwwroot)) {
481  return;
482  }
483 
484  $config_host = parse_url($config->wwwroot, PHP_URL_HOST);
485  if ($config_host === $this->getHost()) {
486  return;
487  }
488 
489  throw new BadRequestException(elgg_echo('BadRequestException:invalid_host_header'));
490  }
491 
499  protected function validateRequestBodyTruncated(): void {
500  $reported_bytes = $this->server->get('CONTENT_LENGTH');
501 
502  // Requests with multipart content type
503  $post_data_count = count($this->request->all());
504 
505  // Requests with other content types
506  $content = $this->getContent();
507  $post_body_length = is_string($content) ? elgg_strlen($content) : 0;
508 
509  $file_count = count($this->files->all());
510 
511  $is_valid = function() use ($reported_bytes, $post_data_count, $post_body_length, $file_count) {
512  if (empty($reported_bytes)) {
513  // Content length is set for POST requests only
514  return true;
515  }
516 
517  if (empty($post_data_count) && empty($post_body_length) && empty($file_count)) {
518  // The size of $_POST or uploaded files has exceed the size limit
519  // and the request body/query has been truncated
520  // thus the request reported bytes is set, but no postdata is found
521  return false;
522  }
523 
524  return true;
525  };
526 
527  if ($is_valid()) {
528  return;
529  }
530 
531  throw new BadRequestException(elgg_echo('actiongatekeeper:uploadexceeded'));
532  }
533 
543  public function correctBaseURL(\Elgg\Config $config): void {
544  if (\Elgg\Application::isCli()) {
545  return;
546  }
547 
548  $path = parse_url($config->wwwroot, PHP_URL_PATH);
549 
550  $this->baseUrl = rtrim($path, '/');
551  }
552 }
$default
Definition: checkbox.php:30
validate()
Validate the request.
Definition: Request.php:464
validateRequestHostHeader()
Validate that the request was made on the correct host.
Definition: Request.php:478
getElggPath()
Get the Request URI minus querystring.
Definition: Request.php:290
initializeTrustedProxyConfiguration(Config $config)
Configure trusted proxy servers to allow access to more client information.
Definition: Request.php:79
Elgg HTTP request.
Definition: Request.php:17
$context
Definition: add.php:8
$input_name
Definition: crop.php:24
isCliServer()
Is PHP running the CLI server front controller.
Definition: Request.php:378
getParam(string $key, $default=null, bool $filter_result=true)
Get some input from variables passed submitted through GET or POST.
Definition: Request.php:177
setRoute(Route $route)
Sets the route matched for this request by the router.
Definition: Request.php:121
getFirstUrlSegment()
Get first Elgg URL segment.
Definition: Request.php:276
$request
Definition: livesearch.php:12
initializeContext()
Initialize context stack.
Definition: Request.php:98
c Accompany it with the information you received as to the offer to distribute corresponding source complete source code means all the source code for all modules it plus any associated interface definition plus the scripts used to control compilation and installation of the executable as a special the source code distributed need not include anything that is normally and so on of the operating system on which the executable unless that component itself accompanies the executable If distribution of executable or object code is made by offering access to copy from a designated then offering equivalent access to copy the source code from the same place counts as distribution of the source even though third parties are not compelled to copy the source along with the object code You may not or distribute the Program except as expressly provided under this License Any attempt otherwise to sublicense or distribute the Program is void
Definition: LICENSE.txt:215
elgg_echo(string $message_key, array $args=[], string $language= '')
Elgg language module Functions to manage language and translations.
Definition: languages.php:17
validateRequestBodyTruncated()
Validate that the request body hasn&#39;t been truncated (eg.
Definition: Request.php:499
elgg_sanitize_input($input)
Filter input from a given string based on registered events.
Definition: input.php:77
setUrlSegments(array $segments)
Get a cloned request with new Elgg URL segments.
Definition: Request.php:261
sniffElggUrl()
Sniff the Elgg site URL with trailing slash.
Definition: Request.php:333
$value
Definition: generic.php:51
elgg_strlen()
Wrapper function for mb_strlen().
Definition: mb_wrapper.php:53
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:254
$config
Advanced site settings, debugging section.
Definition: debugging.php:6
$page
Definition: admin.php:23
setParam(string $key, $value, bool $override_request=false)
Sets an input value that may later be retrieved by get_input.
Definition: Request.php:150
if(!$pagination &&$limit!==false &&!empty($items)&&count($items) >=$limit) $base_url
Definition: list.php:114
$path
Definition: details.php:70
isXmlHttpRequest()
{}
Definition: Request.php:322
Route Wrapper.
Definition: Route.php:8
c Accompany it with the information you received as to the offer to distribute corresponding source complete source code means all the source code for all modules it plus any associated interface definition files
Definition: LICENSE.txt:210
getCurrentURL()
Returns current page URL.
Definition: Request.php:218
static isCli()
Is application running in CLI.
getRoute()
Returns the route matched for this request by the router.
Definition: Request.php:135
isAction()
Is the request an action.
Definition: Request.php:369
isCliServable(string $root)
Is the request pointing to a file that the CLI server can handle?
Definition: Request.php:389
getUrlSegments(bool $raw=false)
Get the Elgg URL segments.
Definition: Request.php:241
elgg_get_site_url()
Get the URL for the current (or specified) site, ending with "/".
const REWRITE_TEST_TOKEN
Definition: Request.php:19
$extensions
getFiles(string $input_name)
Returns an array of uploaded file objects regardless of upload status/errors.
Definition: Request.php:419
if($container instanceof ElggGroup &&$container->guid!=elgg_get_page_owner_guid()) $key
Definition: summary.php:44
bool $_integration_testing
Definition: Request.php:26
getContextStack()
Returns context stack.
Definition: Request.php:110
isRewriteCheck()
Is the request for checking URL rewriting?
Definition: Request.php:351
Thrown when request is malformatted.
getParams(bool $filter_result=true)
Returns all values parsed from the request.
Definition: Request.php:190
$query
$content
Set robots.txt action.
Definition: set_robots.php:6
foreach($plugin_guids as $guid) if(empty($deactivated_plugins)) $url
Definition: deactivate.php:39
_elgg_services()
Get the global service provider.
Definition: elgglib.php:351
$segments
Definition: admin.php:13
const REWRITE_TEST_OUTPUT
Definition: Request.php:20
$attributes
Elgg AJAX loader.
Definition: ajax_loader.php:10
getFile(string $input_name, bool $check_for_validity=true)
Returns the first file found based on the input name.
Definition: Request.php:440
getMatchedParameters()
Get matched parameters.
Definition: Route.php:34
Manages a global stack of strings for sharing information about the current execution context...
Definition: Context.php:27
__construct(array $query=[], array $request=[], array $attributes=[], array $cookies=[], array $files=[], array $server=[], $content=null)
{}
Definition: Request.php:56
correctBaseURL(\Elgg\Config $config)
Correct the base URL of the request.
Definition: Request.php:543