PluginProbe ʕ •ᴥ•ʔ
FAPI Member / 2.2.31
FAPI Member v2.2.31
2.2.33 2.2.32 trunk 1.9.47 2.1.18 2.2.24 2.2.25 2.2.26 2.2.28 2.2.29 2.2.30 2.2.31
fapi-member / libs / nette / http / src / Http / RequestFactory.php
fapi-member / libs / nette / http / src / Http Last commit date
Context.php 4 weeks ago FileUpload.php 4 weeks ago Helpers.php 4 weeks ago IRequest.php 4 weeks ago IResponse.php 4 weeks ago Request.php 4 weeks ago RequestFactory.php 4 weeks ago Response.php 4 weeks ago Session.php 4 weeks ago SessionSection.php 4 weeks ago Url.php 4 weeks ago UrlImmutable.php 4 weeks ago UrlScript.php 4 weeks ago UserStorage.php 4 weeks ago
RequestFactory.php
272 lines
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace FapiMember\Library\Nette\Http;
9
10 use FapiMember\Library\Nette;
11 use FapiMember\Library\Nette\Utils\Arrays;
12 use FapiMember\Library\Nette\Utils\Strings;
13 /**
14 * HTTP request factory.
15 */
16 class RequestFactory
17 {
18 use Nette\SmartObject;
19 /** @internal */
20 private const ValidChars = '\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}';
21 /** @var array */
22 public $urlFilters = [
23 'path' => ['#//#' => '/'],
24 // '%20' => ''
25 'url' => [],
26 ];
27 /** @var bool */
28 private $binary = \false;
29 /** @var string[] */
30 private $proxies = [];
31 /** @return static */
32 public function setBinary(bool $binary = \true)
33 {
34 $this->binary = $binary;
35 return $this;
36 }
37 /**
38 * @param string|string[] $proxy
39 * @return static
40 */
41 public function setProxy($proxy)
42 {
43 $this->proxies = (array) $proxy;
44 return $this;
45 }
46 /**
47 * Returns new Request instance, using values from superglobals.
48 */
49 public function fromGlobals(): Request
50 {
51 $url = new Url();
52 $this->getServer($url);
53 $this->getPathAndQuery($url);
54 [$post, $cookies] = $this->getGetPostCookie($url);
55 [$remoteAddr, $remoteHost] = $this->getClient($url);
56 return new Request(new UrlScript($url, $this->getScriptPath($url)), $post, $this->getFiles(), $cookies, $this->getHeaders(), $this->getMethod(), $remoteAddr, $remoteHost, function (): string {
57 return file_get_contents('php://input');
58 });
59 }
60 private function getServer(Url $url): void
61 {
62 $url->setScheme((!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off')) ? 'https' : 'http');
63 if ((isset($_SERVER[$tmp = 'HTTP_HOST']) || isset($_SERVER[$tmp = 'SERVER_NAME'])) && $pair = $this->parseHostAndPort($_SERVER[$tmp])) {
64 $url->setHost($pair[0]);
65 if (isset($pair[1])) {
66 $url->setPort($pair[1]);
67 } elseif ($tmp === 'SERVER_NAME' && isset($_SERVER['SERVER_PORT'])) {
68 $url->setPort((int) $_SERVER['SERVER_PORT']);
69 }
70 }
71 }
72 private function getPathAndQuery(Url $url): void
73 {
74 $requestUrl = $_SERVER['REQUEST_URI'] ?? '/';
75 $requestUrl = preg_replace('#^\w++://[^/]++#', '', $requestUrl);
76 $requestUrl = Strings::replace($requestUrl, $this->urlFilters['url']);
77 $tmp = explode('?', $requestUrl, 2);
78 $path = Url::unescape($tmp[0], '%/?#');
79 $path = Strings::fixEncoding(Strings::replace($path, $this->urlFilters['path']));
80 $url->setPath($path);
81 $url->setQuery($tmp[1] ?? '');
82 }
83 private function getScriptPath(Url $url): string
84 {
85 if (\PHP_SAPI === 'cli-server') {
86 return '/';
87 }
88 $path = $url->getPath();
89 $lpath = strtolower($path);
90 $script = strtolower($_SERVER['SCRIPT_NAME'] ?? '');
91 if ($lpath !== $script) {
92 $max = min(strlen($lpath), strlen($script));
93 for ($i = 0; $i < $max && $lpath[$i] === $script[$i]; $i++) {
94 }
95 $path = $i ? substr($path, 0, strrpos($path, '/', $i - strlen($path) - 1) + 1) : '/';
96 }
97 return $path;
98 }
99 private function getGetPostCookie(Url $url): array
100 {
101 $useFilter = !in_array((string) ini_get('filter.default'), ['', 'unsafe_raw'], \true) || ini_get('filter.default_flags');
102 $query = $url->getQueryParameters();
103 $post = $useFilter ? filter_input_array(\INPUT_POST, \FILTER_UNSAFE_RAW) : (empty($_POST) ? [] : $_POST);
104 $cookies = $useFilter ? filter_input_array(\INPUT_COOKIE, \FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? [] : $_COOKIE);
105 // remove invalid characters
106 $reChars = '#^[' . self::ValidChars . ']*+$#Du';
107 if (!$this->binary) {
108 $list = [&$query, &$post, &$cookies];
109 foreach ($list as $key => &$val) {
110 foreach ($val as $k => $v) {
111 if (is_string($k) && (!preg_match($reChars, $k) || preg_last_error())) {
112 unset($list[$key][$k]);
113 } elseif (is_array($v)) {
114 $list[$key][$k] = $v;
115 $list[] =& $list[$key][$k];
116 } elseif (is_string($v)) {
117 $list[$key][$k] = (string) preg_replace('#[^' . self::ValidChars . ']+#u', '', $v);
118 } else {
119 throw new Nette\InvalidStateException(sprintf('Invalid value in $_POST/$_COOKIE in key %s, expected string, %s given.', "'{$k}'", gettype($v)));
120 }
121 }
122 }
123 unset($list, $key, $val, $k, $v);
124 }
125 $url->setQuery($query);
126 return [$post, $cookies];
127 }
128 private function getFiles(): array
129 {
130 $reChars = '#^[' . self::ValidChars . ']*+$#Du';
131 $files = [];
132 $list = [];
133 foreach ($_FILES ?? [] as $k => $v) {
134 if (!is_array($v) || !isset($v['name'], $v['type'], $v['size'], $v['tmp_name'], $v['error']) || !$this->binary && is_string($k) && (!preg_match($reChars, $k) || preg_last_error())) {
135 continue;
136 }
137 $v['@'] =& $files[$k];
138 $list[] = $v;
139 }
140 // create FileUpload objects
141 foreach ($list as &$v) {
142 if (!isset($v['name'])) {
143 continue;
144 } elseif (!is_array($v['name'])) {
145 if (!$this->binary && (!preg_match($reChars, $v['name']) || preg_last_error())) {
146 $v['name'] = '';
147 }
148 if ($v['error'] !== \UPLOAD_ERR_NO_FILE) {
149 $v['@'] = new FileUpload($v);
150 }
151 continue;
152 }
153 foreach ($v['name'] as $k => $foo) {
154 if (!$this->binary && is_string($k) && (!preg_match($reChars, $k) || preg_last_error())) {
155 continue;
156 }
157 $list[] = ['name' => $v['name'][$k], 'type' => $v['type'][$k], 'size' => $v['size'][$k], 'full_path' => $v['full_path'][$k] ?? null, 'tmp_name' => $v['tmp_name'][$k], 'error' => $v['error'][$k], '@' => &$v['@'][$k]];
158 }
159 }
160 return $files;
161 }
162 private function getHeaders(): array
163 {
164 if (function_exists('apache_request_headers')) {
165 $headers = apache_request_headers();
166 } else {
167 $headers = [];
168 foreach ($_SERVER as $k => $v) {
169 if (strncmp($k, 'HTTP_', 5) === 0) {
170 $k = substr($k, 5);
171 } elseif (strncmp($k, 'CONTENT_', 8)) {
172 continue;
173 }
174 $headers[strtr($k, '_', '-')] = $v;
175 }
176 }
177 if (!isset($headers['Authorization'])) {
178 if (isset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])) {
179 $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW']);
180 } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
181 $headers['Authorization'] = 'Digest ' . $_SERVER['PHP_AUTH_DIGEST'];
182 }
183 }
184 return $headers;
185 }
186 private function getMethod(): string
187 {
188 $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
189 if ($method === 'POST' && preg_match('#^[A-Z]+$#D', $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ?? '')) {
190 $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
191 }
192 return $method;
193 }
194 private function getClient(Url $url): array
195 {
196 $remoteAddr = (!empty($_SERVER['REMOTE_ADDR'])) ? trim($_SERVER['REMOTE_ADDR'], '[]') : null;
197 // use real client address and host if trusted proxy is used
198 $usingTrustedProxy = $remoteAddr && Arrays::some($this->proxies, function (string $proxy) use ($remoteAddr): bool {
199 return Helpers::ipMatch($remoteAddr, $proxy);
200 });
201 if ($usingTrustedProxy) {
202 $remoteHost = null;
203 $remoteAddr = empty($_SERVER['HTTP_FORWARDED']) ? $this->useNonstandardProxy($url) : $this->useForwardedProxy($url);
204 } else {
205 $remoteHost = (!empty($_SERVER['REMOTE_HOST'])) ? $_SERVER['REMOTE_HOST'] : null;
206 }
207 return [$remoteAddr, $remoteHost];
208 }
209 private function useForwardedProxy(Url $url): ?string
210 {
211 $forwardParams = preg_split('/[,;]/', $_SERVER['HTTP_FORWARDED']);
212 foreach ($forwardParams as $forwardParam) {
213 [$key, $value] = explode('=', $forwardParam, 2) + [1 => ''];
214 $proxyParams[strtolower(trim($key))][] = trim($value, " \t\"");
215 }
216 if (isset($proxyParams['for'])) {
217 $address = $proxyParams['for'][0];
218 $remoteAddr = (strpos($address, '[') === \false) ? explode(':', $address)[0] : substr($address, 1, strpos($address, ']') - 1);
219 // IPv6
220 }
221 if (isset($proxyParams['proto']) && count($proxyParams['proto']) === 1) {
222 $url->setScheme((strcasecmp($proxyParams['proto'][0], 'https') === 0) ? 'https' : 'http');
223 $url->setPort(($url->getScheme() === 'https') ? 443 : 80);
224 }
225 if (isset($proxyParams['host']) && count($proxyParams['host']) === 1 && $pair = $this->parseHostAndPort($proxyParams['host'][0])) {
226 $url->setHost($pair[0]);
227 if (isset($pair[1])) {
228 $url->setPort($pair[1]);
229 }
230 }
231 return $remoteAddr ?? null;
232 }
233 private function useNonstandardProxy(Url $url): ?string
234 {
235 if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
236 $url->setScheme((strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0) ? 'https' : 'http');
237 $url->setPort(($url->getScheme() === 'https') ? 443 : 80);
238 }
239 if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
240 $url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']);
241 }
242 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
243 $xForwardedForWithoutProxies = array_filter(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']), function (string $ip): bool {
244 return filter_var(trim($ip), \FILTER_VALIDATE_IP) === \false || !Arrays::some($this->proxies, function (string $proxy) use ($ip): bool {
245 return Helpers::ipMatch(trim($ip), $proxy);
246 });
247 });
248 if ($xForwardedForWithoutProxies) {
249 $remoteAddr = trim(end($xForwardedForWithoutProxies));
250 $xForwardedForRealIpKey = key($xForwardedForWithoutProxies);
251 }
252 }
253 if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
254 $xForwardedHost = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
255 if (isset($xForwardedHost[$xForwardedForRealIpKey])) {
256 $url->setHost(trim($xForwardedHost[$xForwardedForRealIpKey]));
257 }
258 }
259 return $remoteAddr ?? null;
260 }
261 /** @return array{string, ?int}|null */
262 private function parseHostAndPort(string $s): ?array
263 {
264 return preg_match('#^([a-z0-9_.-]+|\[[a-f0-9:]+])(:\d+)?$#Di', $s, $matches) ? [rtrim(strtolower($matches[1]), '.'), isset($matches[2]) ? (int) substr($matches[2], 1) : null] : null;
265 }
266 /** @deprecated */
267 public function createHttpRequest(): Request
268 {
269 return $this->fromGlobals();
270 }
271 }
272