Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Context
  • FileUpload
  • Helpers
  • Request
  • RequestFactory
  • Response
  • Session
  • SessionSection
  • Url
  • UrlScript
  • UserStorage

Interfaces

  • IRequest
  • IResponse
  • ISessionStorage
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  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: 
  8: namespace Nette\Http;
  9: 
 10: use Nette;
 11: use Nette\Utils\Strings;
 12: 
 13: 
 14: /**
 15:  * Current HTTP request factory.
 16:  */
 17: class RequestFactory
 18: {
 19:     use Nette\SmartObject;
 20: 
 21:     /** @internal */
 22:     const CHARS = '\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}';
 23: 
 24:     /** @var array */
 25:     public $urlFilters = [
 26:         'path' => ['#/{2,}#' => '/'], // '%20' => ''
 27:         'url' => [], // '#[.,)]\z#' => ''
 28:     ];
 29: 
 30:     /** @var bool */
 31:     private $binary = false;
 32: 
 33:     /** @var array */
 34:     private $proxies = [];
 35: 
 36: 
 37:     /**
 38:      * @param  bool
 39:      * @return static
 40:      */
 41:     public function setBinary($binary = true)
 42:     {
 43:         $this->binary = (bool) $binary;
 44:         return $this;
 45:     }
 46: 
 47: 
 48:     /**
 49:      * @param  array|string
 50:      * @return static
 51:      */
 52:     public function setProxy($proxy)
 53:     {
 54:         $this->proxies = (array) $proxy;
 55:         return $this;
 56:     }
 57: 
 58: 
 59:     /**
 60:      * Creates current HttpRequest object.
 61:      * @return Request
 62:      */
 63:     public function createHttpRequest()
 64:     {
 65:         // DETECTS URI, base path and script path of the request.
 66:         $url = new UrlScript;
 67:         $url->setScheme(!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https' : 'http');
 68:         $url->setUser(isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '');
 69:         $url->setPassword(isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '');
 70: 
 71:         // host & port
 72:         if (
 73:             (isset($_SERVER[$tmp = 'HTTP_HOST']) || isset($_SERVER[$tmp = 'SERVER_NAME']))
 74:             && preg_match('#^([a-z0-9_.-]+|\[[a-f0-9:]+\])(:\d+)?\z#i', $_SERVER[$tmp], $pair)
 75:         ) {
 76:             $url->setHost(strtolower($pair[1]));
 77:             if (isset($pair[2])) {
 78:                 $url->setPort((int) substr($pair[2], 1));
 79:             } elseif (isset($_SERVER['SERVER_PORT'])) {
 80:                 $url->setPort((int) $_SERVER['SERVER_PORT']);
 81:             }
 82:         }
 83: 
 84:         // path & query
 85:         $requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
 86:         $requestUrl = preg_replace('#^\w++://[^/]++#', '', $requestUrl);
 87:         $requestUrl = Strings::replace($requestUrl, $this->urlFilters['url']);
 88:         $tmp = explode('?', $requestUrl, 2);
 89:         $path = Url::unescape($tmp[0], '%/?#');
 90:         $path = Strings::fixEncoding(Strings::replace($path, $this->urlFilters['path']));
 91:         $url->setPath($path);
 92:         $url->setQuery(isset($tmp[1]) ? $tmp[1] : '');
 93: 
 94:         // detect script path
 95:         $lpath = strtolower($path);
 96:         $script = isset($_SERVER['SCRIPT_NAME']) ? strtolower($_SERVER['SCRIPT_NAME']) : '';
 97:         if ($lpath !== $script) {
 98:             $max = min(strlen($lpath), strlen($script));
 99:             for ($i = 0; $i < $max && $lpath[$i] === $script[$i]; $i++);
100:             $path = $i ? substr($path, 0, strrpos($path, '/', $i - strlen($path) - 1) + 1) : '/';
101:         }
102:         $url->setScriptPath($path);
103: 
104:         // GET, POST, COOKIE
105:         $useFilter = (!in_array(ini_get('filter.default'), ['', 'unsafe_raw'], true) || ini_get('filter.default_flags'));
106: 
107:         $query = $url->getQueryParameters();
108:         $post = $useFilter ? filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW) : (empty($_POST) ? [] : $_POST);
109:         $cookies = $useFilter ? filter_input_array(INPUT_COOKIE, FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? [] : $_COOKIE);
110: 
111:         // remove invalid characters
112:         $reChars = '#^[' . self::CHARS . ']*+\z#u';
113:         if (!$this->binary) {
114:             $list = [&$query, &$post, &$cookies];
115:             while (list($key, $val) = @each($list)) { // @ intentionally, deprecated in PHP 7.2
116:                 foreach ($val as $k => $v) {
117:                     if (is_string($k) && (!preg_match($reChars, $k) || preg_last_error())) {
118:                         unset($list[$key][$k]);
119: 
120:                     } elseif (is_array($v)) {
121:                         $list[$key][$k] = $v;
122:                         $list[] = &$list[$key][$k];
123: 
124:                     } else {
125:                         $list[$key][$k] = (string) preg_replace('#[^' . self::CHARS . ']+#u', '', $v);
126:                     }
127:                 }
128:             }
129:             unset($list, $key, $val, $k, $v);
130:         }
131:         $url->setQuery($query);
132: 
133: 
134:         // FILES and create FileUpload objects
135:         $files = [];
136:         $list = [];
137:         if (!empty($_FILES)) {
138:             foreach ($_FILES as $k => $v) {
139:                 if (
140:                     !is_array($v)
141:                     || !isset($v['name'], $v['type'], $v['size'], $v['tmp_name'], $v['error'])
142:                     || (!$this->binary && is_string($k) && (!preg_match($reChars, $k) || preg_last_error()))
143:                 ) {
144:                     continue;
145:                 }
146:                 $v['@'] = &$files[$k];
147:                 $list[] = $v;
148:             }
149:         }
150: 
151:         while (list(, $v) = @each($list)) { // @ intentionally, deprecated in PHP 7.2
152:             if (!isset($v['name'])) {
153:                 continue;
154: 
155:             } elseif (!is_array($v['name'])) {
156:                 if (!$this->binary && (!preg_match($reChars, $v['name']) || preg_last_error())) {
157:                     $v['name'] = '';
158:                 }
159:                 if ($v['error'] !== UPLOAD_ERR_NO_FILE) {
160:                     $v['@'] = new FileUpload($v);
161:                 }
162:                 continue;
163:             }
164: 
165:             foreach ($v['name'] as $k => $foo) {
166:                 if (!$this->binary && is_string($k) && (!preg_match($reChars, $k) || preg_last_error())) {
167:                     continue;
168:                 }
169:                 $list[] = [
170:                     'name' => $v['name'][$k],
171:                     'type' => $v['type'][$k],
172:                     'size' => $v['size'][$k],
173:                     'tmp_name' => $v['tmp_name'][$k],
174:                     'error' => $v['error'][$k],
175:                     '@' => &$v['@'][$k],
176:                 ];
177:             }
178:         }
179: 
180: 
181:         // HEADERS
182:         if (function_exists('apache_request_headers')) {
183:             $headers = apache_request_headers();
184:         } else {
185:             $headers = [];
186:             foreach ($_SERVER as $k => $v) {
187:                 if (strncmp($k, 'HTTP_', 5) == 0) {
188:                     $k = substr($k, 5);
189:                 } elseif (strncmp($k, 'CONTENT_', 8)) {
190:                     continue;
191:                 }
192:                 $headers[strtr($k, '_', '-')] = $v;
193:             }
194:         }
195: 
196:         $remoteAddr = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
197:         $remoteHost = !empty($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
198: 
199:         // use real client address and host if trusted proxy is used
200:         $usingTrustedProxy = $remoteAddr && array_filter($this->proxies, function ($proxy) use ($remoteAddr) {
201:             return Helpers::ipMatch($remoteAddr, $proxy);
202:         });
203:         if ($usingTrustedProxy) {
204:             if (!empty($_SERVER['HTTP_FORWARDED'])) {
205:                 $forwardParams = preg_split('/[,;]/', $_SERVER['HTTP_FORWARDED']);
206:                 foreach ($forwardParams as $forwardParam) {
207:                     list($key, $value) = explode('=', $forwardParam, 2) + [1 => null];
208:                     $proxyParams[strtolower(trim($key))][] = trim($value, " \t\"");
209:                 }
210: 
211:                 if (isset($proxyParams['for'])) {
212:                     $address = $proxyParams['for'][0];
213:                     if (strpos($address, '[') === false) { //IPv4
214:                         $remoteAddr = explode(':', $address)[0];
215:                     } else { //IPv6
216:                         $remoteAddr = substr($address, 1, strpos($address, ']') - 1);
217:                     }
218:                 }
219: 
220:                 if (isset($proxyParams['host']) && count($proxyParams['host']) === 1) {
221:                     $host = $proxyParams['host'][0];
222:                     $startingDelimiterPosition = strpos($host, '[');
223:                     if ($startingDelimiterPosition === false) { //IPv4
224:                         $remoteHostArr = explode(':', $host);
225:                         $remoteHost = $remoteHostArr[0];
226:                         if (isset($remoteHostArr[1])) {
227:                             $url->setPort((int) $remoteHostArr[1]);
228:                         }
229:                     } else { //IPv6
230:                         $endingDelimiterPosition = strpos($host, ']');
231:                         $remoteHost = substr($host, strpos($host, '[') + 1, $endingDelimiterPosition - 1);
232:                         $remoteHostArr = explode(':', substr($host, $endingDelimiterPosition));
233:                         if (isset($remoteHostArr[1])) {
234:                             $url->setPort((int) $remoteHostArr[1]);
235:                         }
236:                     }
237:                 }
238: 
239:                 $scheme = (isset($proxyParams['proto']) && count($proxyParams['proto']) === 1) ? $proxyParams['proto'][0] : 'http';
240:                 $url->setScheme(strcasecmp($scheme, 'https') === 0 ? 'https' : 'http');
241:             } else {
242:                 if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
243:                     $url->setScheme(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http');
244:                     $url->setPort($url->getScheme() === 'https' ? 443 : 80);
245:                 }
246: 
247:                 if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
248:                     $url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']);
249:                 }
250: 
251:                 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
252:                     $xForwardedForWithoutProxies = array_filter(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']), function ($ip) {
253:                         return !array_filter($this->proxies, function ($proxy) use ($ip) {
254:                             return filter_var(trim($ip), FILTER_VALIDATE_IP) !== false && Helpers::ipMatch(trim($ip), $proxy);
255:                         });
256:                     });
257:                     $remoteAddr = trim(end($xForwardedForWithoutProxies));
258:                     $xForwardedForRealIpKey = key($xForwardedForWithoutProxies);
259:                 }
260: 
261:                 if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
262:                     $xForwardedHost = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
263:                     if (isset($xForwardedHost[$xForwardedForRealIpKey])) {
264:                         $remoteHost = trim($xForwardedHost[$xForwardedForRealIpKey]);
265:                     }
266:                 }
267:             }
268:         }
269: 
270:         // method, eg. GET, PUT, ...
271:         $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : null;
272:         if (
273:             $method === 'POST'
274:             && isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])
275:             && preg_match('#^[A-Z]+\z#', $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])
276:         ) {
277:             $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
278:         }
279: 
280:         // raw body
281:         $rawBodyCallback = function () {
282:             return file_get_contents('php://input');
283:         };
284: 
285:         return new Request($url, null, $post, $files, $cookies, $headers, $method, $remoteAddr, $remoteHost, $rawBodyCallback);
286:     }
287: }
288: 
Nette 2.4-20180918 API API documentation generated by ApiGen 2.8.0