Skip to content

Commit 4771484

Browse files
authored
Merge pull request #2407 from codeigniter4/urlfixes
Fix url helper functions to work when site hosted in subfolders.
2 parents 9966498 + e2641e7 commit 4771484

4 files changed

Lines changed: 87 additions & 11 deletions

File tree

app/Config/App.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class App extends BaseConfig
2121
| environments.
2222
|
2323
*/
24-
public $baseURL = 'http://localhost:8080';
24+
public $baseURL = 'http://localhost:8080/';
2525

2626
/*
2727
|--------------------------------------------------------------------------

system/HTTP/IncomingRequest.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ public function __construct($config, URI $uri = null, $body = 'php://input', Use
174174

175175
$this->populateHeaders();
176176

177+
// Get our current URI.
178+
// NOTE: This WILL NOT match the actual URL in the browser since for
179+
// everything this cares about (and the router, etc) is the portion
180+
// AFTER the script name. So, if hosted in a sub-folder this will
181+
// appear different than actual URL. If you need that, use current_url().
177182
$this->uri = $uri;
178183

179184
$this->detectURI($config->uriProtocol, $config->baseURL);
@@ -604,14 +609,10 @@ protected function detectURI(string $protocol, string $baseURL)
604609
// baseURL, so let's help them out.
605610
$baseURL = ! empty($baseURL) ? rtrim($baseURL, '/ ') . '/' : $baseURL;
606611

607-
// Based on our baseURL provided by the developer (if set)
612+
// Based on our baseURL provided by the developer
608613
// set our current domain name, scheme
609614
if (! empty($baseURL))
610615
{
611-
// We cannot add the path here, otherwise it's possible
612-
// that the routing will not work correctly if we are
613-
// within a sub-folder scheme. So it's modified in
614-
// the
615616
$this->uri->setScheme(parse_url($baseURL, PHP_URL_SCHEME));
616617
$this->uri->setHost(parse_url($baseURL, PHP_URL_HOST));
617618
$this->uri->setPort(parse_url($baseURL, PHP_URL_PORT));
@@ -715,7 +716,8 @@ protected function parseRequestURI(): string
715716
}
716717

717718
// parse_url() returns false if no host is present, but the path or query string
718-
// contains a colon followed by a number
719+
// contains a colon followed by a number. So we attach a dummy host since
720+
// REQUEST_URI does not include the host. This allows us to parse out the query string and path.
719721
$parts = parse_url('http://dummy' . $_SERVER['REQUEST_URI']);
720722
$query = $parts['query'] ?? '';
721723
$uri = $parts['path'] ?? '';

system/Helpers/url_helper.php

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,14 @@ function base_url($uri = '', string $protocol = null): string
111111
// otherwise get rid of the path, because we have
112112
// no way of knowing the intent...
113113
$config = \CodeIgniter\Config\Services::request()->config;
114-
$url = new \CodeIgniter\HTTP\URI($config->baseURL);
114+
115+
// If baseUrl does not have a trailing slash it won't resolve
116+
// correctly for users hosting in a subfolder.
117+
$baseUrl = ! empty($config->baseURL) && $config->baseURL !== '/'
118+
? rtrim($config->baseURL, '/ ') . '/'
119+
: $config->baseURL;
120+
121+
$url = new \CodeIgniter\HTTP\URI($baseUrl);
115122
unset($config);
116123

117124
// Merge in the path set by the user, if any
@@ -132,7 +139,7 @@ function base_url($uri = '', string $protocol = null): string
132139
$url->setScheme($protocol);
133140
}
134141

135-
return (string) $url;
142+
return rtrim((string) $url, '/ ');
136143
}
137144
}
138145

@@ -152,7 +159,25 @@ function base_url($uri = '', string $protocol = null): string
152159
*/
153160
function current_url(bool $returnObject = false)
154161
{
155-
return $returnObject ? \CodeIgniter\Config\Services::request()->uri : (string) \CodeIgniter\Config\Services::request()->uri;
162+
$uri = service('request')->uri;
163+
164+
// If hosted in a sub-folder, we will have additional
165+
// segments that show up prior to the URI path we just
166+
// grabbed from the request, so add it on if necessary.
167+
$baseUri = new \CodeIgniter\HTTP\URI(config('App')->baseURL);
168+
169+
if (! empty($baseUri->getPath()))
170+
{
171+
$path = rtrim($baseUri->getPath(), '/ ') . '/' . $uri->getPath();
172+
173+
$uri->setPath($path);
174+
}
175+
176+
// Since we're basing off of the IncomingRequest URI,
177+
// we are guaranteed to have a host based on our own configs.
178+
return $returnObject
179+
? $uri
180+
: (string)$uri->setQuery('');
156181
}
157182
}
158183

tests/system/Helpers/URLHelperTest.php

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,22 @@ public function testSiteURLSegments()
167167
$this->assertEquals('http://example.com/index.php/news/local/123', site_url(['news', 'local', '123'], null, $config));
168168
}
169169

170+
public function testSiteURLInSubfolder()
171+
{
172+
$_SERVER['HTTP_HOST'] = 'example.com';
173+
$_SERVER['REQUEST_URI'] = '/foo/public/bar?baz=quip';
174+
175+
// Since we're on a CLI, we must provide our own URI
176+
$config = new App();
177+
$config->baseURL = 'http://example.com/foo/public';
178+
$request = Services::request($config);
179+
$request->uri = new URI('http://example.com/foo/public/bar');
180+
181+
Services::injectMock('request', $request);
182+
183+
$this->assertEquals('http://example.com/foo/public/bar', current_url());
184+
}
185+
170186
/**
171187
* @see https://github.com/codeigniter4/CodeIgniter4/issues/240
172188
*/
@@ -281,7 +297,7 @@ public function testBaseURLWithSegments()
281297

282298
Services::injectMock('request', $request);
283299

284-
$this->assertEquals('http://example.com/', base_url());
300+
$this->assertEquals('http://example.com', base_url());
285301
}
286302

287303
/**
@@ -316,6 +332,23 @@ public function testBaseURLWithSegmentsAgain()
316332
$this->assertEquals('http://example.com/profile', base_url('profile'));
317333
}
318334

335+
public function testBaseURLHasSubfolder()
336+
{
337+
$_SERVER['HTTP_HOST'] = 'example.com';
338+
$_SERVER['REQUEST_URI'] = '/test';
339+
340+
// Since we're on a CLI, we must provide our own URI
341+
$config = new App();
342+
$config->baseURL = 'http://example.com/subfolder';
343+
$request = Services::request($config, false);
344+
$request->uri = new URI('http://example.com/subfolder/test');
345+
346+
Services::injectMock('request', $request);
347+
348+
$this->assertEquals('http://example.com/subfolder/foo', base_url('foo'));
349+
$this->assertEquals('http://example.com/subfolder', base_url());
350+
}
351+
319352
//--------------------------------------------------------------------
320353
// Test current_url
321354

@@ -370,6 +403,22 @@ public function testCurrentURLEquivalence()
370403
$this->assertEquals(base_url(uri_string()), current_url());
371404
}
372405

406+
public function testCurrentURLInSubfolder()
407+
{
408+
$_SERVER['HTTP_HOST'] = 'example.com';
409+
$_SERVER['REQUEST_URI'] = '/foo/public/bar?baz=quip';
410+
411+
// Since we're on a CLI, we must provide our own URI
412+
$config = new App();
413+
$config->baseURL = 'http://example.com/foo/public';
414+
$request = Services::request($config);
415+
$request->uri = new URI('http://example.com/foo/public/bar');
416+
417+
Services::injectMock('request', $request);
418+
419+
$this->assertEquals('http://example.com/foo/public/bar', current_url());
420+
}
421+
373422
//--------------------------------------------------------------------
374423
// Test previous_url
375424

0 commit comments

Comments
 (0)