PluginProbe
Packeta / 1.6.1
Packeta v1.6.1
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / http / src / Http / RequestFactory.php

RequestFactory.php in Packeta 1.6.1, at deps/nette/http/src/Http/RequestFactory.php

281 lines 11.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 Packetery\Nette\Http;
9
10 use Packetery\Nette;
11 use Packetery\Nette\Utils\Arrays;
12 use Packetery\Nette\Utils\Strings;
13 /**
14 * HTTP request factory.
15 */
16 class RequestFactory
17 {
18 use \Packetery\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'])) && \preg_match('#^([a-z0-9_.-]+|\\[[a-f0-9:]+\\])(:\\d+)?$#Di', $_SERVER[$tmp], $pair)) {
64 $url->setHost(\rtrim(\strtolower($pair[1]), '.'));
65 if (isset($pair[2])) {
66 $url->setPort((int) \substr($pair[2], 1));
67 } elseif (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 \Packetery\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) {
226 $host = $proxyParams['host'][0];
227 $startingDelimiterPosition = \strpos($host, '[');
228 if ($startingDelimiterPosition === \false) {
229 //IPv4
230 $pair = \explode(':', $host);
231 $url->setHost($pair[0]);
232 if (isset($pair[1])) {
233 $url->setPort((int) $pair[1]);
234 }
235 } else {
236 //IPv6
237 $endingDelimiterPosition = \strpos($host, ']');
238 $url->setHost(\substr($host, \strpos($host, '[') + 1, $endingDelimiterPosition - 1));
239 $pair = \explode(':', \substr($host, $endingDelimiterPosition));
240 if (isset($pair[1])) {
241 $url->setPort((int) $pair[1]);
242 }
243 }
244 }
245 return $remoteAddr ?? null;
246 }
247 private function useNonstandardProxy(Url $url) : ?string
248 {
249 if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
250 $url->setScheme(\strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http');
251 $url->setPort($url->getScheme() === 'https' ? 443 : 80);
252 }
253 if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
254 $url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']);
255 }
256 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
257 $xForwardedForWithoutProxies = \array_filter(\explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']), function (string $ip) : bool {
258 return \filter_var(\trim($ip), \FILTER_VALIDATE_IP) === \false || !Arrays::some($this->proxies, function (string $proxy) use($ip) : bool {
259 return Helpers::ipMatch(\trim($ip), $proxy);
260 });
261 });
262 if ($xForwardedForWithoutProxies) {
263 $remoteAddr = \trim(\end($xForwardedForWithoutProxies));
264 $xForwardedForRealIpKey = \key($xForwardedForWithoutProxies);
265 }
266 }
267 if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
268 $xForwardedHost = \explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
269 if (isset($xForwardedHost[$xForwardedForRealIpKey])) {
270 $url->setHost(\trim($xForwardedHost[$xForwardedForRealIpKey]));
271 }
272 }
273 return $remoteAddr ?? null;
274 }
275 /** @deprecated */
276 public function createHttpRequest() : Request
277 {
278 return $this->fromGlobals();
279 }
280 }
281