diff --git a/system/HTTP/SiteURI.php b/system/HTTP/SiteURI.php index 4c5e66417158..44f819ea8f43 100644 --- a/system/HTTP/SiteURI.php +++ b/system/HTTP/SiteURI.php @@ -70,20 +70,13 @@ class SiteURI extends URI */ private string $routePath; - public function __construct(App $configApp) + /** + * @param string $relativePath URI path relative to baseURL. May include + * queries or fragments. + */ + public function __construct(App $configApp, string $relativePath = '') { - // It's possible the user forgot a trailing slash on their - // baseURL, so let's help them out. - $baseURL = rtrim($configApp->baseURL, '/ ') . '/'; - - // Validate baseURL - if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) { - throw new ConfigException( - 'Config\App::$baseURL is invalid.' - ); - } - - $this->baseURL = $baseURL; + $this->baseURL = $this->normalizeBaseURL($configApp); $this->indexPage = $configApp->indexPage; $this->setBaseSegments(); @@ -91,10 +84,17 @@ public function __construct(App $configApp) // Check for an index page $indexPage = ''; if ($configApp->indexPage !== '') { - $indexPage = $configApp->indexPage . '/'; + $indexPage = $configApp->indexPage; + + // Check if we need a separator + if ($relativePath !== '' && $relativePath[0] !== '/' && $relativePath[0] !== '?') { + $indexPage .= '/'; + } } - $tempUri = $this->baseURL . $indexPage; + $relativePath = URI::removeDotSegments($relativePath); + + $tempUri = $this->baseURL . $indexPage . $relativePath; $uri = new URI($tempUri); if ($configApp->forceGlobalSecureRequests) { @@ -107,7 +107,25 @@ public function __construct(App $configApp) } $this->applyParts($parts); - $this->setPath('/'); + $parts = explode('?', $relativePath); + $routePath = $parts[0]; + $this->setRoutePath($routePath); + } + + private function normalizeBaseURL(App $configApp): string + { + // It's possible the user forgot a trailing slash on their + // baseURL, so let's help them out. + $baseURL = rtrim($configApp->baseURL, '/ ') . '/'; + + // Validate baseURL + if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) { + throw new ConfigException( + 'Config\App::$baseURL is invalid.' + ); + } + + return $baseURL; } /** @@ -247,14 +265,22 @@ public function __toString(): string * @return $this */ public function setPath(string $path) + { + $this->setRoutePath($path); + + return $this; + } + + /** + * Sets the route path (and segments). + */ + private function setRoutePath(string $path): void { $this->routePath = $this->filterPath($path); $this->segments = $this->convertToSegments($this->routePath); $this->refreshPath(); - - return $this; } /** diff --git a/tests/system/HTTP/SiteURITest.php b/tests/system/HTTP/SiteURITest.php index 5d0a3d134024..74e035868196 100644 --- a/tests/system/HTTP/SiteURITest.php +++ b/tests/system/HTTP/SiteURITest.php @@ -37,6 +37,27 @@ public function testConstructor() $this->assertSame('/index.php/', $uri->getPath()); } + public function testConstructorRelativePath() + { + $config = new App(); + + $uri = new SiteURI($config, 'one/two'); + + $this->assertSame('http://example.com/index.php/one/two', (string) $uri); + $this->assertSame('/index.php/one/two', $uri->getPath()); + } + + public function testConstructorRelativePathWithQuery() + { + $config = new App(); + + $uri = new SiteURI($config, 'one/two?foo=1&bar=2'); + + $this->assertSame('http://example.com/index.php/one/two?foo=1&bar=2', (string) $uri); + $this->assertSame('/index.php/one/two', $uri->getPath()); + $this->assertSame('foo=1&bar=2', $uri->getQuery()); + } + public function testConstructorSubfolder() { $config = new App(); @@ -49,6 +70,18 @@ public function testConstructorSubfolder() $this->assertSame('/ci4/index.php/', $uri->getPath()); } + public function testConstructorSubfolderRelativePathWithQuery() + { + $config = new App(); + $config->baseURL = 'http://example.com/ci4/'; + + $uri = new SiteURI($config, 'one/two?foo=1&bar=2'); + + $this->assertSame('http://example.com/ci4/index.php/one/two?foo=1&bar=2', (string) $uri); + $this->assertSame('/ci4/index.php/one/two', $uri->getPath()); + $this->assertSame('foo=1&bar=2', $uri->getQuery()); + } + public function testConstructorForceGlobalSecureRequests() { $config = new App();