diff --git a/bin/serve.php b/bin/serve.php index a71056ac..cff6c01b 100755 --- a/bin/serve.php +++ b/bin/serve.php @@ -72,15 +72,21 @@ function handleConnection($conn, string $docroot): void $path = '/example.php'; } - $script = $docroot . $path; + $scriptName = $path; + $pathInfo = ''; + if (preg_match('#^(.+\.php)(/.*)?$#', $path, $pm)) { + $scriptName = $pm[1]; + $pathInfo = $pm[2] ?? ''; + } + + $script = $docroot . $scriptName; if (!is_file($script)) { respond($conn, 404, 'text/plain', "Not Found\n"); return; } - $scriptName = $path; - $requestUri = $path; + $requestUri = $scriptName . $pathInfo; if ('' !== $query) { $requestUri .= '?' . $query; } @@ -90,6 +96,11 @@ function handleConnection($conn, string $docroot): void putenv('REQUEST_BODY=' . $body); putenv('SCRIPT_NAME=' . $scriptName); putenv('REQUEST_URI=' . $requestUri); + if ('' !== $pathInfo) { + putenv('PATH_INFO=' . $pathInfo); + } else { + putenv('PATH_INFO'); + } $code = file_get_contents($script); if (false === $code) { diff --git a/lib/Web/Superglobals.php b/lib/Web/Superglobals.php index 2df75dc0..bdd151c7 100644 --- a/lib/Web/Superglobals.php +++ b/lib/Web/Superglobals.php @@ -98,6 +98,15 @@ private static function populateServer( } } self::setStringEntry($server, 'REQUEST_URI', $requestUri); + + $pathInfo = getenv('PATH_INFO'); + if (false === $pathInfo || '' === $pathInfo) { + $pathInfo = self::derivePathInfo($scriptName, $requestUri); + } + if ('' !== $pathInfo) { + self::setStringEntry($server, 'PATH_INFO', $pathInfo); + } + self::setStringEntry($server, 'GATEWAY_INTERFACE', 'CGI/1.1'); self::setStringEntry($server, 'SERVER_SOFTWARE', 'PHP-Compiler-VM'); @@ -139,6 +148,26 @@ private static function populateFormEncoded(HashTable $ht, string $body): void } } + /** + * Derive PATH_INFO from REQUEST_URI path suffix after SCRIPT_NAME (front-controller pattern). + */ + public static function derivePathInfo(string $scriptName, string $requestUri): string + { + $path = parse_url($requestUri, PHP_URL_PATH); + if (!is_string($path) || '' === $path) { + $path = $requestUri; + $q = strpos($path, '?'); + if (false !== $q) { + $path = substr($path, 0, $q); + } + } + if ($path === $scriptName || !str_starts_with($path, $scriptName)) { + return ''; + } + + return substr($path, strlen($scriptName)); + } + private static function setStringEntry(HashTable $ht, string $key, string $value): void { $v = new Variable(Variable::TYPE_STRING); diff --git a/test/real/cases/web_path_info.phpt b/test/real/cases/web_path_info.phpt new file mode 100644 index 00000000..67f81245 --- /dev/null +++ b/test/real/cases/web_path_info.phpt @@ -0,0 +1,11 @@ +--TEST-- +Web: PATH_INFO from REQUEST_URI (front-controller routing) +--ENV-- +SCRIPT_NAME=/index.php +REQUEST_URI=/index.php/admin/dashboard +REQUEST_METHOD=GET +--FILE-- +