diff --git a/flight/Engine.php b/flight/Engine.php index c3e769e..5974158 100644 --- a/flight/Engine.php +++ b/flight/Engine.php @@ -1020,8 +1020,10 @@ class Engine public function _lastModified(int $time): void { $this->response()->header('Last-Modified', gmdate('D, d M Y H:i:s \G\M\T', $time)); + $request = $this->request(); + $ifModifiedSince = $request->header('If-Modified-Since'); - $hit = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $time; + $hit = isset($ifModifiedSince) && strtotime($ifModifiedSince) === $time; $this->triggerEvent('flight.cache.checked', 'lastModified', $hit, 0.0); if ($hit === true) { diff --git a/flight/net/Request.php b/flight/net/Request.php index 6ca6b72..2d0c109 100644 --- a/flight/net/Request.php +++ b/flight/net/Request.php @@ -155,27 +155,37 @@ class Request public function __construct(array $config = []) { // Default properties - if (empty($config)) { + if (empty($config) === true) { + $scheme = $this->getScheme(); + $url = $this->getVar('REQUEST_URI', '/'); + if (strpos($url, '@') !== false) { + $url = str_replace('@', '%40', $url); + } + $base = $this->getVar('SCRIPT_NAME', ''); + if (strpos($base, ' ') !== false || strpos($base, '\\') !== false) { + $base = str_replace(['\\', ' '], ['/', '%20'], $base); + } + $base = dirname($base); $config = [ - 'url' => str_replace('@', '%40', self::getVar('REQUEST_URI', '/')), - 'base' => str_replace(['\\', ' '], ['/', '%20'], \dirname(self::getVar('SCRIPT_NAME'))), - 'method' => self::getMethod(), - 'referrer' => self::getVar('HTTP_REFERER'), - 'ip' => self::getVar('REMOTE_ADDR'), - 'ajax' => self::getVar('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest', - 'scheme' => self::getScheme(), - 'user_agent' => self::getVar('HTTP_USER_AGENT'), - 'type' => self::getVar('CONTENT_TYPE'), - 'length' => intval(self::getVar('CONTENT_LENGTH', 0)), + 'url' => $url, + 'base' => $base, + 'method' => $this->getMethod(), + 'referrer' => $this->getVar('HTTP_REFERER'), + 'ip' => $this->getVar('REMOTE_ADDR'), + 'ajax' => $this->getVar('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest', + 'scheme' => $scheme, + 'user_agent' => $this->getVar('HTTP_USER_AGENT'), + 'type' => $this->getVar('CONTENT_TYPE'), + 'length' => intval($this->getVar('CONTENT_LENGTH', 0)), 'query' => new Collection($_GET), 'data' => new Collection($_POST), 'cookies' => new Collection($_COOKIE), 'files' => new Collection($_FILES), - 'secure' => self::getScheme() === 'https', - 'accept' => self::getVar('HTTP_ACCEPT'), - 'proxy_ip' => self::getProxyIpAddress(), - 'host' => self::getVar('HTTP_HOST'), - 'servername' => self::getVar('SERVER_NAME', ''), + 'secure' => $scheme === 'https', + 'accept' => $this->getVar('HTTP_ACCEPT'), + 'proxy_ip' => $this->getProxyIpAddress(), + 'host' => $this->getVar('HTTP_HOST'), + 'servername' => $this->getVar('SERVER_NAME', ''), ]; } @@ -201,7 +211,7 @@ class Request // (such as installing on a subdirectory in a web server) // @see testInitUrlSameAsBaseDirectory if ($this->base !== '/' && $this->base !== '' && strpos($this->url, $this->base) === 0) { - $this->url = substr($this->url, \strlen($this->base)); + $this->url = substr($this->url, strlen($this->base)); } // Default url @@ -249,11 +259,11 @@ class Request return $body; } - $method = $this->method ?? self::getMethod(); + $method = $this->method ?? $this->getMethod(); - if ($method === 'POST' || $method === 'PUT' || $method === 'DELETE' || $method === 'PATCH') { - $body = file_get_contents($this->stream_path); - } + if (in_array($method, ['POST', 'PUT', 'DELETE', 'PATCH'], true) === true) { + $body = file_get_contents($this->stream_path); + } $this->body = $body; @@ -267,8 +277,8 @@ class Request { $method = self::getVar('REQUEST_METHOD', 'GET'); - if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']) === true) { - $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; + if (self::getVar('HTTP_X_HTTP_METHOD_OVERRIDE') !== '') { + $method = self::getVar('HTTP_X_HTTP_METHOD_OVERRIDE'); } elseif (isset($_REQUEST['_method']) === true) { $method = $_REQUEST['_method']; } @@ -295,8 +305,9 @@ class Request $flags = \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE; foreach ($forwarded as $key) { - if (\array_key_exists($key, $_SERVER) === true) { - sscanf($_SERVER[$key], '%[^,]', $ip); + $serverVar = self::getVar($key); + if ($serverVar !== '') { + sscanf($serverVar, '%[^,]', $ip); if (filter_var($ip, \FILTER_VALIDATE_IP, $flags) !== false) { return $ip; } @@ -403,14 +414,16 @@ class Request */ public static function parseQuery(string $url): array { - $params = []; - - $args = parse_url($url); - if (isset($args['query']) === true) { - parse_str($args['query'], $params); - } - - return $params; + $queryPos = strpos($url, '?'); + if ($queryPos === false) { + return []; + } + $query = substr($url, $queryPos + 1); + if ($query === '') { + return []; + } + parse_str($query, $params); + return $params; } /** @@ -421,13 +434,13 @@ class Request public static function getScheme(): string { if ( - (isset($_SERVER['HTTPS']) === true && strtolower($_SERVER['HTTPS']) === 'on') + (strtolower(self::getVar('HTTPS')) === 'on') || - (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) === true && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') + (self::getVar('HTTP_X_FORWARDED_PROTO') === 'https') || - (isset($_SERVER['HTTP_FRONT_END_HTTPS']) === true && $_SERVER['HTTP_FRONT_END_HTTPS'] === 'on') + (self::getVar('HTTP_FRONT_END_HTTPS') === 'on') || - (isset($_SERVER['REQUEST_SCHEME']) === true && $_SERVER['REQUEST_SCHEME'] === 'https') + (self::getVar('REQUEST_SCHEME') === 'https') ) { return 'https'; } diff --git a/flight/net/Response.php b/flight/net/Response.php index f9ee9a2..b364b13 100644 --- a/flight/net/Response.php +++ b/flight/net/Response.php @@ -324,10 +324,11 @@ class Response ); // @codeCoverageIgnoreEnd } else { + $serverProtocol = Request::getVar('SERVER_PROTOCOL') ?: 'HTTP/1.1'; $this->setRealHeader( sprintf( '%s %d %s', - $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.1', + $serverProtocol, $this->status, self::$codes[$this->status] ), diff --git a/tests/RequestTest.php b/tests/RequestTest.php index 1b7b484..a84841b 100644 --- a/tests/RequestTest.php +++ b/tests/RequestTest.php @@ -356,4 +356,25 @@ class RequestTest extends TestCase $this->assertEquals('/tmp/php456', $files[1]->getTempName()); $this->assertEquals(0, $files[1]->getError()); } + + public function testUrlWithAtSymbol(): void + { + $_SERVER['REQUEST_URI'] = '/user@domain'; + $_SERVER['SCRIPT_NAME'] = '/index.php'; + $request = new Request(); + $this->assertEquals('/user%40domain', $request->url); + } + + public function testBaseWithSpaceAndBackslash(): void + { + $_SERVER['SCRIPT_NAME'] = '\\dir name\\base folder\\index.php'; + $request = new Request(); + $this->assertEquals('/dir%20name/base%20folder', $request->base); + } + + public function testParseQueryWithEmptyQueryString(): void + { + $result = Request::parseQuery('/foo?'); + $this->assertEquals([], $result); + } }