From 28e3a64fb0d29d333cb575d641866a82d5751d54 Mon Sep 17 00:00:00 2001 From: MGatner Date: Tue, 11 May 2021 16:32:34 +0000 Subject: [PATCH 1/4] Refactor URL functions --- system/Helpers/url_helper.php | 164 ++++++----- .../system/Helpers/URLHelper/BaseUrlTest.php | 263 ------------------ .../Helpers/URLHelper/CurrentUrlTest.php | 21 +- .../system/Helpers/URLHelper/SiteUrlTest.php | 159 ++++++----- 4 files changed, 197 insertions(+), 410 deletions(-) delete mode 100644 tests/system/Helpers/URLHelper/BaseUrlTest.php diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index aede159aacef..d854fc9bfa2e 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -9,6 +9,7 @@ * file that was distributed with this source code. */ +use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\URI; use CodeIgniter\Router\Exceptions\RouterException; use Config\App; @@ -18,105 +19,110 @@ * CodeIgniter URL Helpers */ -if (! function_exists('site_url')) +if (! function_exists('_get_uri')) { /** - * Return a site URL to use in views + * Used by the other URL functions to build a + * framework-specific URI based on the App config. * - * @param mixed $uri URI string or array of URI segments - * @param string|null $protocol - * @param App|null $altConfig Alternate configuration to use + * @internal Outside of the framework this should not be used directly. * - * @return string + * @param string $relativePath May include queries or fragments + * @param App|null $config + * + * @return URI + * + * @throws InvalidArgumentException For invalid paths or config */ - function site_url($uri = '', string $protocol = null, App $altConfig = null): string + function _get_uri(string $relativePath = '', App $config = null): URI { - // convert segment array to string - if (is_array($uri)) + $config = $config ?? config('App'); + + if ($config->baseURL === '') { - $uri = implode('/', $uri); + throw new InvalidArgumentException('_get_uri() requires a valid baseURL.'); + } + if (is_int(strpos($relativePath, '://'))) + { + throw new InvalidArgumentException('_get_uri() only accepts relative paths.'); } - // use alternate config if provided, else default one - $config = $altConfig ?? config(App::class); + $relativePath = URI::removeDotSegments($relativePath); - $fullPath = rtrim(base_url(), '/') . '/'; + // Build the full URL based on $config and $relativePath + $url = rtrim($config->baseURL, '/ ') . '/'; - // Add index page, if so configured - if (! empty($config->indexPage)) - { - $fullPath .= rtrim($config->indexPage, '/'); - } - if ($uri !== '') + // Check for an index page + if ($config->indexPage !== '') { - $fullPath .= '/' . $uri; + $url .= $config->indexPage; + + // Check if we need a separator + if ($relativePath !== '' && $relativePath[0] !== '/' && $relativePath[0] !== '?') + { + $url .= '/'; + } } - $url = new URI($fullPath); + $url .= $relativePath; - // allow the scheme to be over-ridden; else, use default - if (! empty($protocol)) + $uri = new URI($url); + + // Check if the baseURL scheme needs to be coerced into its secure version + if ($config->forceGlobalSecureRequests && $uri->getScheme() === 'http') { - $url->setScheme($protocol); + $uri->setScheme('https'); } - return (string) $url; + return $uri; } } //-------------------------------------------------------------------- -if (! function_exists('base_url')) +if (! function_exists('site_url')) { /** - * Return the base URL to use in views + * Returns a site URL as defined by the App config. + * + * @param mixed $relativePath URI string or array of URI segments + * @param string|null $scheme + * @param App|null $config Alternate configuration to use * - * @param mixed $uri URI string or array of URI segments - * @param string $protocol * @return string */ - function base_url($uri = '', string $protocol = null): string + function site_url($relativePath = '', string $scheme = null, App $config = null): string { - // convert segment array to string - if (is_array($uri)) + // Convert array of segments to a string + if (is_array($relativePath)) { - $uri = implode('/', $uri); + $relativePath = implode('/', $relativePath); } - $uri = trim($uri, '/'); - - // We should be using the configured baseURL that the user set; - // otherwise get rid of the path, because we have - // no way of knowing the intent... - $config = Services::request()->config; - - // If baseUrl does not have a trailing slash it won't resolve - // correctly for users hosting in a subfolder. - $baseUrl = ! empty($config->baseURL) && $config->baseURL !== '/' - ? rtrim($config->baseURL, '/ ') . '/' - : $config->baseURL; - $url = new URI($baseUrl); - unset($config); + $uri = _get_uri($relativePath, $config); - // Merge in the path set by the user, if any - if ($uri !== '') - { - $url = $url->resolveRelativeURI($uri); - } + return URI::createURIString($scheme ?? $uri->getScheme(), $uri->getAuthority(), $uri->getPath(), $uri->getQuery(), $uri->getFragment()); + } +} - // If the scheme wasn't provided, check to - // see if it was a secure request - if (empty($protocol) && Services::request()->isSecure()) - { - $protocol = 'https'; - } +//-------------------------------------------------------------------- - if (! empty($protocol)) - { - $url->setScheme($protocol); - } +if (! function_exists('base_url')) +{ + /** + * Returns the base URL as defined by the App config. + * Base URLs are trimmed site URLs without the index page. + * + * @param mixed $relativePath URI string or array of URI segments + * @param string $scheme + * @return string + */ + function base_url($relativePath = '', string $scheme = null): string + { + $config = clone config('App'); + $config->indexPage = ''; - return rtrim((string) $url, '/ '); + return rtrim(site_url($relativePath, $scheme, $config), '/'); } } @@ -125,22 +131,32 @@ function base_url($uri = '', string $protocol = null): string if (! function_exists('current_url')) { /** - * Current URL + * Returns the current full URL based on the IncomingRequest. + * String returns ignore query and fragment parts. * - * Returns the full URL (including segments) of the page where this - * function is placed - * - * @param boolean $returnObject True to return an object instead of a strong + * @param boolean $returnObject True to return an object instead of a string + * @param IncomingRequest|null $request A request to use when retrieving the path * * @return string|URI */ - function current_url(bool $returnObject = false) + function current_url(bool $returnObject = false, IncomingRequest $request = null) { - $uri = clone Services::request()->uri; + $request = $request ?? Services::request(); + $path = $request->getPath(); + + // Append queries and fragments + if ($query = $request->getUri()->getQuery()) + { + $path .= '?' . $query; + } + if ($fragment = $request->getUri()->getFragment()) + { + $path .= '#' . $fragment; + } + + $uri = _get_uri($path); - // Since we're basing off of the IncomingRequest URI, - // we are guaranteed to have a host based on our own configs. - return $returnObject ? $uri : (string) $uri->setQuery(''); + return $returnObject ? $uri : URI::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath()); } } @@ -285,7 +301,7 @@ function anchor_popup($uri = '', string $title = '', $attributes = false, App $a // use alternate config if provided, else default one $config = $altConfig ?? config(App::class); - $siteUrl = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, '', $config); + $siteUrl = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, null, $config); $siteUrl = rtrim($siteUrl, '/'); if ($title === '') diff --git a/tests/system/Helpers/URLHelper/BaseUrlTest.php b/tests/system/Helpers/URLHelper/BaseUrlTest.php deleted file mode 100644 index 679af978a93b..000000000000 --- a/tests/system/Helpers/URLHelper/BaseUrlTest.php +++ /dev/null @@ -1,263 +0,0 @@ -config = new App(); - $this->config->baseURL = 'http://example.com/'; - $this->config->indexPage = 'index.php'; - Factories::injectMock('config', 'App', $this->config); - } - - public function tearDown(): void - { - parent::tearDown(); - - $_SERVER = []; - } - - //-------------------------------------------------------------------- - // Test base_url - - public function testBaseURLBasics() - { - $this->assertEquals('http://example.com', base_url()); - } - - public function testBaseURLAttachesPath() - { - $this->assertEquals('http://example.com/foo', base_url('foo')); - } - - public function testBaseURLAttachesPathArray() - { - $this->assertEquals('http://example.com/foo/bar', base_url(['foo', 'bar'])); - } - - public function testBaseURLAttachesScheme() - { - $this->assertEquals('https://example.com/foo', base_url('foo', 'https')); - } - - public function testBaseURLPathZero() - { - $this->assertEquals('http://example.com/0', base_url('0')); - } - - public function testBaseURLHeedsBaseURL() - { - // Since we're on a CLI, we must provide our own URI - $this->config->baseURL = 'http://example.com/public'; - $request = Services::request($this->config); - $request->uri = new URI('http://example.com/public'); - - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com/public', base_url()); - } - - public function testBaseURLNoTrailingSlash() - { - // Since we're on a CLI, we must provide our own URI - $this->config->baseURL = 'http://example.com'; - $request = Services::request($this->config); - $request->uri = new URI('http://example.com/foobar'); - - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com', base_url()); - } - - public function testBaseURLExample() - { - $this->assertEquals('http://example.com/blog/post/123', base_url('blog/post/123')); - } - - /** - * @see https://github.com/codeigniter4/CodeIgniter4/issues/240 - */ - public function testBaseURLWithSegments() - { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/test'; - - // Since we're on a CLI, we must provide our own URI - $request = Services::request($this->config, false); - $request->uri = new URI('http://example.com/test'); - - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com', base_url()); - } - - /** - * @see https://github.com/codeigniter4/CodeIgniter4/issues/867 - */ - public function testBaseURLHTTPS() - { - $_SERVER['HTTPS'] = 'on'; - - $this->assertEquals('https://example.com/blog/post/123', base_url('blog/post/123')); - } - - /** - * @see https://github.com/codeigniter4/CodeIgniter4/issues/240 - */ - public function testBaseURLWithSegmentsAgain() - { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/test/page'; - - // Since we're on a CLI, we must provide our own URI - $request = Services::request($this->config, false); - $request->uri = new URI('http://example.com/test/page'); - - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com', base_url()); - $this->assertEquals('http://example.com/profile', base_url('profile')); - } - - public function testBaseURLHasSubfolder() - { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/subfolder/test'; - $_SERVER['SCRIPT_NAME'] = '/subfolder/index.php'; - - // Since we're on a CLI, we must provide our own URI - $this->config->baseURL = 'http://example.com/subfolder/'; - Factories::injectMock('config', 'App', $this->config); - - $request = Services::request($this->config, false); - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com/subfolder/foo', base_url('foo')); - $this->assertEquals('http://example.com/subfolder', base_url()); - } - - public function testBaseURLNoTrailingSlashHasSubfolder() - { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/subfolder/test'; - $_SERVER['SCRIPT_NAME'] = '/subfolder/index.php'; - - // Since we're on a CLI, we must provide our own URI - $this->config->baseURL = 'http://example.com/subfolder'; - Factories::injectMock('config', 'App', $this->config); - - $request = Services::request($this->config, false); - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com/subfolder/foo', base_url('foo')); - $this->assertEquals('http://example.com/subfolder', base_url()); - } - - //-------------------------------------------------------------------- - - public function testBasedNoIndex() - { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/ci/v4/x/y'; - - $this->config->baseURL = 'http://example.com/ci/v4/'; - $request = Services::request($this->config); - $request->uri = new URI('http://example.com/ci/v4/x/y'); - - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com/ci/v4/index.php/controller/method', site_url('controller/method', null, $this->config)); - $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); - } - - public function testBasedNoTrailingSlash() - { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/ci/v4/x/y'; - - $this->config->baseURL = 'http://example.com/ci/v4'; - $request = Services::request($this->config); - $request->uri = new URI('http://example.com/ci/v4/x/y'); - - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com/ci/v4/index.php/controller/method', site_url('controller/method', null, $this->config)); - $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); - } - - public function testBasedWithIndex() - { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/ci/v4/index.php/x/y'; - - $this->config->baseURL = 'http://example.com/ci/v4/'; - $request = Services::request($this->config); - $request->uri = new URI('http://example.com/ci/v4/index.php/x/y'); - - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com/ci/v4/index.php/controller/method', site_url('controller/method', null, $this->config)); - $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); - } - - public function testBasedWithoutIndex() - { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/ci/v4/x/y'; - - $this->config->baseURL = 'http://example.com/ci/v4/'; - $this->config->indexPage = ''; - $request = Services::request($this->config); - $request->uri = new URI('http://example.com/ci/v4/x/y'); - - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com/ci/v4/controller/method', site_url('controller/method', null, $this->config)); - $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); - } - - public function testBasedWithOtherIndex() - { - $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/ci/v4/x/y'; - - $this->config->baseURL = 'http://example.com/ci/v4/'; - $this->config->indexPage = 'fc.php'; - $request = Services::request($this->config); - $request->uri = new URI('http://example.com/ci/v4/x/y'); - - Services::injectMock('request', $request); - - $this->assertEquals('http://example.com/ci/v4/fc.php/controller/method', site_url('controller/method', null, $this->config)); - $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); - } -} diff --git a/tests/system/Helpers/URLHelper/CurrentUrlTest.php b/tests/system/Helpers/URLHelper/CurrentUrlTest.php index baba2fc4fd49..895f8318ecbd 100644 --- a/tests/system/Helpers/URLHelper/CurrentUrlTest.php +++ b/tests/system/Helpers/URLHelper/CurrentUrlTest.php @@ -63,7 +63,7 @@ public function testCurrentURLReturnsBasicURL() // Since we're on a CLI, we must provide our own URI $this->config->baseURL = 'http://example.com/public'; - $this->assertEquals('http://example.com/public/', current_url()); + $this->assertEquals('http://example.com/public/index.php/', current_url()); } public function testCurrentURLReturnsObject() @@ -74,7 +74,7 @@ public function testCurrentURLReturnsObject() $url = current_url(true); $this->assertInstanceOf(URI::class, $url); - $this->assertEquals('http://example.com/public/', (string) $url); + $this->assertEquals('http://example.com/public/index.php/', (string) $url); } public function testCurrentURLEquivalence() @@ -89,7 +89,7 @@ public function testCurrentURLEquivalence() $request = Services::request($this->config); Services::injectMock('request', $request); - $this->assertEquals(base_url(uri_string()), current_url()); + $this->assertEquals(site_url(uri_string()), current_url()); } public function testCurrentURLInSubfolder() @@ -105,12 +105,11 @@ public function testCurrentURLInSubfolder() $request = Services::request($this->config); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/foo/public/bar', current_url()); - $this->assertEquals('http://example.com/foo/public/bar?baz=quip', (string) current_url(true)); + $this->assertEquals('http://example.com/foo/public/index.php/bar', current_url()); + $this->assertEquals('http://example.com/foo/public/index.php/bar?baz=quip', (string) current_url(true)); $uri = current_url(true); - $this->assertEquals(['bar'], $uri->getSegments()); - $this->assertEquals('bar', $uri->getSegment(1)); + $this->assertEquals('foo', $uri->getSegment(1)); $this->assertEquals('example.com', $uri->getHost()); $this->assertEquals('http', $uri->getScheme()); } @@ -129,12 +128,12 @@ public function testCurrentURLWithPortInSubfolder() $request = Services::request($this->config); Services::injectMock('request', $request); - $this->assertEquals('http://example.com:8080/foo/public/bar', current_url()); - $this->assertEquals('http://example.com:8080/foo/public/bar?baz=quip', (string) current_url(true)); + $this->assertEquals('http://example.com:8080/foo/public/index.php/bar', current_url()); + $this->assertEquals('http://example.com:8080/foo/public/index.php/bar?baz=quip', (string) current_url(true)); $uri = current_url(true); - $this->assertEquals(['bar'], $uri->getSegments()); - $this->assertEquals('bar', $uri->getSegment(1)); + $this->assertEquals(['foo', 'public', 'index.php', 'bar'], $uri->getSegments()); + $this->assertEquals('foo', $uri->getSegment(1)); $this->assertEquals('example.com', $uri->getHost()); $this->assertEquals('http', $uri->getScheme()); $this->assertEquals('8080', $uri->getPort()); diff --git a/tests/system/Helpers/URLHelper/SiteUrlTest.php b/tests/system/Helpers/URLHelper/SiteUrlTest.php index e6e630c9f734..e98f5b86bd13 100644 --- a/tests/system/Helpers/URLHelper/SiteUrlTest.php +++ b/tests/system/Helpers/URLHelper/SiteUrlTest.php @@ -10,6 +10,10 @@ use Config\App; /** + * Since base_url() only slightly modifies + * site_url() these functions are tested + * simultaneously. + * * @backupGlobals enabled */ final class SiteUrlTest extends CIUnitTestCase @@ -32,10 +36,7 @@ protected function setUp(): void Services::reset(true); - // Set a common base configuration (overriden by individual tests) - $this->config = new App(); - $this->config->baseURL = 'http://example.com/'; - $this->config->indexPage = 'index.php'; + $this->config = new App(); Factories::injectMock('config', 'App', $this->config); } @@ -49,188 +50,222 @@ public function tearDown(): void //-------------------------------------------------------------------- /** - * @dataProvider siteUrlProvider + * Takes a multitude of various config input and verifies + * that base_url() and site_url() return the expected result. + * + * @param string $baseURL + * @param string $indexPage + * @param string|null $scheme + * @param boolean $secure + * @param string $path + * @param string $expectedSiteUrl + * + * @dataProvider configProvider */ - public function testSiteUrl($baseURL, $indexPage, $param, $protocol, $expected) + public function testUrls($baseURL, $indexPage, $scheme, $secure, $path, $expectedSiteUrl) { // Set the config - $this->config->baseURL = $baseURL; - $this->config->indexPage = $indexPage; + $this->config->baseURL = $baseURL; + $this->config->indexPage = $indexPage; + $this->config->forceGlobalSecureRequests = $secure; - // Mock the Request - $request = Services::request($this->config); - $request->uri = new URI('http://example.com/'); - Services::injectMock('request', $request); + $this->assertEquals($expectedSiteUrl, site_url($path, $scheme, $this->config)); - $this->assertEquals($expected, site_url($param, $protocol, $this->config)); + // base_url is always the trimmed site_url without index page + $expectedBaseUrl = $indexPage === '' ? $expectedSiteUrl : str_replace('/' . $indexPage, '', $expectedSiteUrl); + $expectedBaseUrl = rtrim($expectedBaseUrl, '/'); + $this->assertEquals($expectedBaseUrl, base_url($path, $scheme)); } - public function siteUrlProvider() + public function configProvider() { - // baseURL, indexPage, param, protocol, expected + // baseURL, indexPage, scheme, path, expectedSiteUrl return [ [ 'http://example.com/', 'index.php', - '', null, + false, + '', 'http://example.com/index.php', ], [ 'http://example.com', 'index.php', - '', null, + false, + '', 'http://example.com/index.php', ], [ 'http://example.com/', '', - '', null, + false, + '', 'http://example.com/', ], [ 'http://example.com/', 'banana.php', - '', null, + false, + '', 'http://example.com/banana.php', ], [ 'http://example.com/', '', - 'abc', null, + false, + 'abc', 'http://example.com/abc', ], [ 'http://example.com/public/', 'index.php', - '', null, + false, + '', 'http://example.com/public/index.php', ], [ 'http://example.com/public/', '', - '', null, + false, + '', 'http://example.com/public/', ], [ 'http://example.com/public', '', - '', null, + false, + '', 'http://example.com/public/', ], [ 'http://example.com/public', 'index.php', - '/', null, + false, + '/', 'http://example.com/public/index.php/', ], [ 'http://example.com/public/', 'index.php', - '/', null, + false, + '/', 'http://example.com/public/index.php/', ], [ 'http://example.com/', 'index.php', - 'foo', null, + false, + 'foo', 'http://example.com/index.php/foo', ], [ 'http://example.com/', 'index.php', - '0', null, + false, + '0', 'http://example.com/index.php/0', ], [ 'http://example.com/public', 'index.php', - 'foo', null, + false, + 'foo', 'http://example.com/public/index.php/foo', ], [ 'http://example.com/', 'index.php', - 'foo', + null, + false, + 'foo?bar=bam', + 'http://example.com/index.php/foo?bar=bam', + ], + [ + 'http://example.com/', + 'index.php', + null, + false, + 'test#banana', + 'http://example.com/index.php/test#banana', + ], + [ + 'http://example.com/', + 'index.php', 'ftp', + false, + 'foo', 'ftp://example.com/index.php/foo', ], [ 'http://example.com/', 'index.php', - 'news/local/123', null, + false, + 'news/local/123', 'http://example.com/index.php/news/local/123', ], [ 'http://example.com/', 'index.php', - [ - 'news', - 'local', - '123', - ], null, + null, + false, + ['news', 'local', '123'], 'http://example.com/index.php/news/local/123', ], ]; } - public function testSiteURLHTTPS() - { - $_SERVER['HTTPS'] = 'on'; - - $request = Services::request($this->config); - $request->uri = new URI('http://example.com/'); - - Services::injectMock('request', $request); - - $this->assertEquals('https://example.com/index.php', site_url('', null, $this->config)); - } + //-------------------------------------------------------------------- + // base_url + //-------------------------------------------------------------------- /** + * These tests are only really relevant to show that base_url() + * has no interaction with the current request URI. + * * @see https://github.com/codeigniter4/CodeIgniter4/issues/240 */ - public function testSiteURLWithSegments() + public function testBaseURLDiscovery() { + $this->config->baseURL = 'http://example.com/'; + $_SERVER['HTTP_HOST'] = 'example.com'; $_SERVER['REQUEST_URI'] = '/test'; - // Since we're on a CLI, we must provide our own URI - $request = Services::request($this->config, false); - $request->uri = new URI('http://example.com/test'); + $this->assertEquals('http://example.com', base_url()); - Services::injectMock('request', $request); + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/test/page'; - $this->assertEquals('http://example.com/index.php', site_url()); + $this->assertEquals('http://example.com', base_url()); + $this->assertEquals('http://example.com/profile', base_url('profile')); } - /** - * @see https://github.com/codeigniter4/CodeIgniter4/issues/240 - */ - public function testSiteURLWithSegmentsAgain() + public function testBaseURLService() { $_SERVER['HTTP_HOST'] = 'example.com'; - $_SERVER['REQUEST_URI'] = '/test/page'; + $_SERVER['REQUEST_URI'] = '/ci/v4/x/y'; - // Since we're on a CLI, we must provide our own URI - $request = Services::request($this->config, false); - $request->uri = new URI('http://example.com/test/page'); + $this->config->baseURL = 'http://example.com/ci/v4/'; + $request = Services::request($this->config); + $request->uri = new URI('http://example.com/ci/v4/x/y'); Services::injectMock('request', $request); - $this->assertEquals('http://example.com/index.php', site_url()); - $this->assertEquals('http://example.com/index.php/profile', site_url('profile')); + $this->assertEquals('http://example.com/ci/v4/index.php/controller/method', site_url('controller/method', null, $this->config)); + $this->assertEquals('http://example.com/ci/v4/controller/method', base_url('controller/method', null, $this->config)); } } From 1034e69c5eb90b457e70539856a9fa7019dd7fa6 Mon Sep 17 00:00:00 2001 From: MGatner Date: Thu, 13 May 2021 01:43:20 +0000 Subject: [PATCH 2/4] Convert full URIs --- system/Helpers/url_helper.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/system/Helpers/url_helper.php b/system/Helpers/url_helper.php index d854fc9bfa2e..f58045ae73a7 100644 --- a/system/Helpers/url_helper.php +++ b/system/Helpers/url_helper.php @@ -42,9 +42,12 @@ function _get_uri(string $relativePath = '', App $config = null): URI { throw new InvalidArgumentException('_get_uri() requires a valid baseURL.'); } + + // If a full URI was passed then convert it if (is_int(strpos($relativePath, '://'))) { - throw new InvalidArgumentException('_get_uri() only accepts relative paths.'); + $full = new URI($relativePath); + $relativePath = URI::createURIString(null, null, $full->getPath(), $full->getQuery(), $full->getFragment()); } $relativePath = URI::removeDotSegments($relativePath); From 5fd639e31d4b41ef25f87ea55db8cf879c355360 Mon Sep 17 00:00:00 2001 From: MGatner Date: Thu, 13 May 2021 02:01:00 +0000 Subject: [PATCH 3/4] Fix dependent tests --- tests/system/HTTP/ResponseTest.php | 6 +++--- tests/system/HTTP/URITest.php | 3 +++ tests/system/Helpers/FormHelperTest.php | 4 ++-- tests/system/Pager/PagerTest.php | 8 ++++---- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/system/HTTP/ResponseTest.php b/tests/system/HTTP/ResponseTest.php index e72f0d328b0d..3142446d2e9b 100644 --- a/tests/system/HTTP/ResponseTest.php +++ b/tests/system/HTTP/ResponseTest.php @@ -176,7 +176,7 @@ public function testSetLink() $response->setLink($pager); $this->assertEquals( - '; rel="first",; rel="prev",; rel="next",; rel="last"', + '; rel="first",; rel="prev",; rel="next",; rel="last"', $response->header('Link')->getValue() ); @@ -184,7 +184,7 @@ public function testSetLink() $response->setLink($pager); $this->assertEquals( - '; rel="next",; rel="last"', + '; rel="next",; rel="last"', $response->header('Link')->getValue() ); @@ -192,7 +192,7 @@ public function testSetLink() $response->setLink($pager); $this->assertEquals( - '; rel="first",; rel="prev"', + '; rel="first",; rel="prev"', $response->header('Link')->getValue() ); } diff --git a/tests/system/HTTP/URITest.php b/tests/system/HTTP/URITest.php index 84c9e12d59ae..3013acaf4cab 100644 --- a/tests/system/HTTP/URITest.php +++ b/tests/system/HTTP/URITest.php @@ -8,6 +8,9 @@ use CodeIgniter\Test\CIUnitTestCase; use Config\App; +/** + * @backupGlobals enabled + */ class URITest extends CIUnitTestCase { diff --git a/tests/system/Helpers/FormHelperTest.php b/tests/system/Helpers/FormHelperTest.php index 7307f338aca8..513ae5a39ada 100644 --- a/tests/system/Helpers/FormHelperTest.php +++ b/tests/system/Helpers/FormHelperTest.php @@ -96,7 +96,7 @@ public function testFormOpenWithoutAction() $Value = csrf_hash(); $Name = csrf_token(); $expected = << +
EOH; @@ -104,7 +104,7 @@ public function testFormOpenWithoutAction() else { $expected = << + EOH; } diff --git a/tests/system/Pager/PagerTest.php b/tests/system/Pager/PagerTest.php index c43226020812..308be74452d7 100644 --- a/tests/system/Pager/PagerTest.php +++ b/tests/system/Pager/PagerTest.php @@ -133,11 +133,11 @@ public function testStoreWithQueries() $this->pager->store('default', 3, 25, 100); - $this->assertEquals('http://example.com?page=2&foo=bar', $this->pager->getPreviousPageURI()); - $this->assertEquals('http://example.com?page=4&foo=bar', $this->pager->getNextPageURI()); - $this->assertEquals('http://example.com?page=5&foo=bar', $this->pager->getPageURI(5)); + $this->assertEquals('http://example.com/index.php?page=2&foo=bar', $this->pager->getPreviousPageURI()); + $this->assertEquals('http://example.com/index.php?page=4&foo=bar', $this->pager->getNextPageURI()); + $this->assertEquals('http://example.com/index.php?page=5&foo=bar', $this->pager->getPageURI(5)); $this->assertEquals( - 'http://example.com?foo=bar&page=5', + 'http://example.com/index.php?foo=bar&page=5', $this->pager->only(['foo'])->getPageURI(5) ); } From 307cfec32c3733943bfde3e7d5c51e846933684d Mon Sep 17 00:00:00 2001 From: MGatner Date: Thu, 13 May 2021 15:02:32 +0000 Subject: [PATCH 4/4] [ci skip] Update UG for changes --- user_guide_src/source/changelogs/v4.1.2.rst | 7 +++++++ user_guide_src/source/helpers/url_helper.rst | 7 +++++-- user_guide_src/source/libraries/uri.rst | 18 +++++++++--------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.1.2.rst b/user_guide_src/source/changelogs/v4.1.2.rst index 704234850a5a..3063300d0f5e 100644 --- a/user_guide_src/source/changelogs/v4.1.2.rst +++ b/user_guide_src/source/changelogs/v4.1.2.rst @@ -5,6 +5,13 @@ Release Date: Not released **4.1.2 release of CodeIgniter4** +BREAKING: + +Fixed `a bug `_ in ``current_url()`` that prevented +configurations with an ``indexPage`` from including that value in the return value. Any installations +using ``App::$indexPage`` should expect altered values from ``current_url()`` and all its dependencies +(including Response Testing, Pager, Form Helper, Pager, and View Parser). + Enhancements: - New HTTP classes, ``Cookie`` and ``CookieStore``, for abstracting web cookies. diff --git a/user_guide_src/source/helpers/url_helper.rst b/user_guide_src/source/helpers/url_helper.rst index f5996758398d..1c1ca5866ef8 100644 --- a/user_guide_src/source/helpers/url_helper.rst +++ b/user_guide_src/source/helpers/url_helper.rst @@ -81,9 +81,10 @@ The following functions are available: This would give you something like: *http://example.com/images/icons/edit.png* -.. php:function:: current_url([$returnObject = false]) +.. php:function:: current_url([$returnObject = false[, $request = null]]) :param boolean $returnObject: True if you would like a URI instance returned, instead of a string. + :param IncomingRequest|null $request: An alternate request to use for path detection; useful for testing. :returns: The current URL :rtype: string|\\CodeIgniter\\HTTP\\URI @@ -93,7 +94,9 @@ The following functions are available: .. note:: Calling this function is the same as doing this: :: - base_url(uri_string()); + site_url(uri_string()); + +.. important:: Prior to **4.1.2** this function had a bug causing it to ignore the configuration on ``App::$indexPage``. .. php:function:: previous_url([$returnObject = false]) diff --git a/user_guide_src/source/libraries/uri.rst b/user_guide_src/source/libraries/uri.rst index aa3c13ac8cba..11edce898cb7 100644 --- a/user_guide_src/source/libraries/uri.rst +++ b/user_guide_src/source/libraries/uri.rst @@ -31,17 +31,17 @@ into its appropriate sections:: The Current URI --------------- -Many times, all you really want is an object representing the current URL of this request. This can be accessed -in two different ways. The first is to grab it directly from the current request object. Assuming that you're in -a controller that extends ``CodeIgniter\Controller`` you can get it like:: - - $uri = $this->request->uri; - -Second, you can use one of the functions available in the **url_helper**:: +Many times, all you really want is an object representing the current URL of this request. +You can use one of the functions available in the **url_helper**:: $uri = current_url(true); - + You must pass ``true`` as the first parameter, otherwise, it will return the string representation of the current URL. +This URI is based on the path (relative to your ``baseURL``) as determined by the current request object and +your settings in ``Config\App`` (baseURL, indexPage, and forceGlobalSecureRequests). +Assuming that you're in a controller that extends ``CodeIgniter\Controller`` you can get this relative path:: + + $path = $this->request->getPath(); =========== URI Strings @@ -51,7 +51,7 @@ Many times, all you really want is to get a string representation of a URI. This the URI as a string:: $uri = current_url(true); - echo (string)$uri; // http://example.com + echo (string) $uri; // http://example.com/index.php If you know the pieces of the URI and just want to ensure it's all formatted correctly, you can generate a string using the URI class' static ``createURIString()`` method::