Skip to content

Commit d73b455

Browse files
authored
Merge pull request #7282 from kenjis/refactor-URI-creation
rework: URI creation and URL helper
2 parents 3ad9d4a + c6f3d57 commit d73b455

38 files changed

+706
-461
lines changed

system/Config/BaseService.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
use CodeIgniter\HTTP\Request;
3838
use CodeIgniter\HTTP\RequestInterface;
3939
use CodeIgniter\HTTP\ResponseInterface;
40+
use CodeIgniter\HTTP\SiteURIFactory;
4041
use CodeIgniter\HTTP\URI;
4142
use CodeIgniter\Images\Handlers\BaseHandler;
4243
use CodeIgniter\Language\Language;
@@ -47,6 +48,7 @@
4748
use CodeIgniter\Router\Router;
4849
use CodeIgniter\Security\Security;
4950
use CodeIgniter\Session\Session;
51+
use CodeIgniter\Superglobals;
5052
use CodeIgniter\Throttle\Throttler;
5153
use CodeIgniter\Typography\Typography;
5254
use CodeIgniter\Validation\ValidationInterface;
@@ -123,6 +125,8 @@
123125
* @method static RouteCollection routes($getShared = true)
124126
* @method static Security security(App $config = null, $getShared = true)
125127
* @method static Session session(App $config = null, $getShared = true)
128+
* @method static SiteURIFactory siteurifactory(App $config = null, Superglobals $superglobals = null, $getShared = true)
129+
* @method static Superglobals superglobals(array $server = null, array $get = null, bool $getShared = true)
126130
* @method static Throttler throttler($getShared = true)
127131
* @method static Timer timer($getShared = true)
128132
* @method static Toolbar toolbar(ConfigToolbar $config = null, $getShared = true)

system/Config/Services.php

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
use CodeIgniter\HTTP\RequestInterface;
3939
use CodeIgniter\HTTP\Response;
4040
use CodeIgniter\HTTP\ResponseInterface;
41+
use CodeIgniter\HTTP\SiteURIFactory;
4142
use CodeIgniter\HTTP\URI;
4243
use CodeIgniter\HTTP\UserAgent;
4344
use CodeIgniter\Images\Handlers\BaseHandler;
@@ -52,6 +53,7 @@
5253
use CodeIgniter\Session\Handlers\Database\PostgreHandler;
5354
use CodeIgniter\Session\Handlers\DatabaseHandler;
5455
use CodeIgniter\Session\Session;
56+
use CodeIgniter\Superglobals;
5557
use CodeIgniter\Throttle\Throttler;
5658
use CodeIgniter\Typography\Typography;
5759
use CodeIgniter\Validation\Validation;
@@ -693,6 +695,43 @@ public static function session(?SessionConfig $config = null, bool $getShared =
693695
return $session;
694696
}
695697

698+
/**
699+
* The Factory for SiteURI.
700+
*
701+
* @return SiteURIFactory
702+
*/
703+
public static function siteurifactory(
704+
?App $config = null,
705+
?Superglobals $superglobals = null,
706+
bool $getShared = true
707+
) {
708+
if ($getShared) {
709+
return static::getSharedInstance('siteurifactory', $config, $superglobals);
710+
}
711+
712+
$config ??= config('App');
713+
$superglobals ??= AppServices::superglobals();
714+
715+
return new SiteURIFactory($config, $superglobals);
716+
}
717+
718+
/**
719+
* Superglobals.
720+
*
721+
* @return Superglobals
722+
*/
723+
public static function superglobals(
724+
?array $server = null,
725+
?array $get = null,
726+
bool $getShared = true
727+
) {
728+
if ($getShared) {
729+
return static::getSharedInstance('superglobals', $server, $get);
730+
}
731+
732+
return new Superglobals($server, $get);
733+
}
734+
696735
/**
697736
* The Throttler class provides a simple method for implementing
698737
* rate limiting in your applications.
@@ -744,14 +783,21 @@ public static function toolbar(?ToolbarConfig $config = null, bool $getShared =
744783
*
745784
* @param string $uri
746785
*
747-
* @return URI
786+
* @return URI The current URI if $uri is null.
748787
*/
749788
public static function uri(?string $uri = null, bool $getShared = true)
750789
{
751790
if ($getShared) {
752791
return static::getSharedInstance('uri', $uri);
753792
}
754793

794+
if ($uri === null) {
795+
$appConfig = config(App::class);
796+
$factory = AppServices::siteurifactory($appConfig, AppServices::superglobals());
797+
798+
return $factory->createFromGlobals();
799+
}
800+
755801
return new URI($uri);
756802
}
757803

system/HTTP/IncomingRequest.php

Lines changed: 12 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,12 @@ public function __construct($config, ?URI $uri = null, $body = 'php://input', ?U
175175

176176
parent::__construct($config);
177177

178-
$this->detectURI($config->uriProtocol, $config->baseURL);
178+
if ($uri instanceof SiteURI) {
179+
$this->setPath($uri->getRoutePath());
180+
} else {
181+
$this->setPath($uri->getPath());
182+
}
183+
179184
$this->detectLocale($config);
180185
}
181186

@@ -227,9 +232,9 @@ public function detectLocale($config)
227232
* either provided by the user in the baseURL Config setting, or
228233
* determined from the environment as needed.
229234
*
230-
* @deprecated $protocol and $baseURL are deprecated. No longer used.
231-
*
232235
* @return void
236+
*
237+
* @deprecated 4.4.0 No longer used.
233238
*/
234239
protected function detectURI(string $protocol, string $baseURL)
235240
{
@@ -447,7 +452,7 @@ public function isSecure(): bool
447452
}
448453

449454
/**
450-
* Sets the relative path and updates the URI object.
455+
* Sets the URI path relative to baseURL.
451456
*
452457
* Note: Since current_url() accesses the shared request
453458
* instance, this can be used to change the "current URL"
@@ -457,88 +462,22 @@ public function isSecure(): bool
457462
* @param App|null $config Optional alternate config to use
458463
*
459464
* @return $this
465+
*
466+
* @deprecated 4.4.0 This method will be private. The parameter $config is deprecated. No longer used.
460467
*/
461468
public function setPath(string $path, ?App $config = null)
462469
{
463470
$this->path = $path;
464471

465-
// @TODO remove this. The path of the URI object should be a full URI path,
466-
// not a URI path relative to baseURL.
467-
$this->uri->setPath($path);
468-
469-
$config ??= $this->config;
470-
471-
// It's possible the user forgot a trailing slash on their
472-
// baseURL, so let's help them out.
473-
$baseURL = ($config->baseURL === '') ? $config->baseURL : rtrim($config->baseURL, '/ ') . '/';
474-
475-
// Based on our baseURL and allowedHostnames provided by the developer
476-
// and HTTP_HOST, set our current domain name, scheme.
477-
if ($baseURL !== '') {
478-
$host = $this->determineHost($config, $baseURL);
479-
480-
// Set URI::$baseURL
481-
$uri = new URI($baseURL);
482-
$currentBaseURL = (string) $uri->setHost($host);
483-
$this->uri->setBaseURL($currentBaseURL);
484-
485-
$this->uri->setScheme(parse_url($baseURL, PHP_URL_SCHEME));
486-
$this->uri->setHost($host);
487-
$this->uri->setPort(parse_url($baseURL, PHP_URL_PORT));
488-
489-
// Ensure we have any query vars
490-
$this->uri->setQuery($_SERVER['QUERY_STRING'] ?? '');
491-
492-
// Check if the scheme needs to be coerced into its secure version
493-
if ($config->forceGlobalSecureRequests && $this->uri->getScheme() === 'http') {
494-
$this->uri->setScheme('https');
495-
}
496-
} elseif (! is_cli()) {
497-
// Do not change exit() to exception; Request is initialized before
498-
// setting the exception handler, so if an exception is raised, an
499-
// error will be displayed even if in the production environment.
500-
// @codeCoverageIgnoreStart
501-
exit('You have an empty or invalid baseURL. The baseURL value must be set in app/Config/App.php, or through the .env file.');
502-
// @codeCoverageIgnoreEnd
503-
}
504-
505472
return $this;
506473
}
507474

508-
/**
509-
* @deprecated 4.4.0 Moved to SiteURIFactory.
510-
*/
511-
private function determineHost(App $config, string $baseURL): string
512-
{
513-
$host = parse_url($baseURL, PHP_URL_HOST);
514-
515-
if (empty($config->allowedHostnames)) {
516-
return $host;
517-
}
518-
519-
// Update host if it is valid.
520-
$httpHostPort = $this->getServer('HTTP_HOST');
521-
if ($httpHostPort !== null) {
522-
[$httpHost] = explode(':', $httpHostPort, 2);
523-
524-
if (in_array($httpHost, $config->allowedHostnames, true)) {
525-
$host = $httpHost;
526-
}
527-
}
528-
529-
return $host;
530-
}
531-
532475
/**
533476
* Returns the URI path relative to baseURL,
534477
* running detection as necessary.
535478
*/
536479
public function getPath(): string
537480
{
538-
if ($this->path === null) {
539-
$this->detectPath($this->config->uriProtocol);
540-
}
541-
542481
return $this->path;
543482
}
544483

@@ -972,7 +911,7 @@ public function getFile(string $fileID)
972911
*
973912
* Do some final cleaning of the URI and return it, currently only used in static::_parse_request_uri()
974913
*
975-
* @deprecated Use URI::removeDotSegments() directly
914+
* @deprecated 4.1.2 Use URI::removeDotSegments() directly
976915
*/
977916
protected function removeRelativeDirectory(string $uri): string
978917
{

system/HTTP/SiteURI.php

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ private function determineBaseURL(
145145
$uri = new URI($baseURL);
146146

147147
// Update scheme
148-
if ($scheme !== null) {
148+
if ($scheme !== null && $scheme !== '') {
149149
$uri->setScheme($scheme);
150150
} elseif ($configApp->forceGlobalSecureRequests) {
151151
$uri->setScheme('https');
@@ -363,4 +363,67 @@ protected function applyParts(array $parts): void
363363
$this->password = $parts['pass'];
364364
}
365365
}
366+
367+
/**
368+
* For base_url() helper.
369+
*
370+
* @param array|string $relativePath URI string or array of URI segments
371+
* @param string|null $scheme URI scheme. E.g., http, ftp
372+
*/
373+
public function baseUrl($relativePath = '', ?string $scheme = null): string
374+
{
375+
$relativePath = $this->stringifyRelativePath($relativePath);
376+
377+
$config = clone config(App::class);
378+
$config->indexPage = '';
379+
380+
$host = $this->getHost();
381+
382+
$uri = new self($config, $relativePath, $host, $scheme);
383+
384+
// Support protocol-relative links
385+
if ($scheme === '') {
386+
return substr((string) $uri, strlen($uri->getScheme()) + 1);
387+
}
388+
389+
return (string) $uri;
390+
}
391+
392+
/**
393+
* @param array|string $relativePath URI string or array of URI segments
394+
*/
395+
private function stringifyRelativePath($relativePath): string
396+
{
397+
if (is_array($relativePath)) {
398+
$relativePath = implode('/', $relativePath);
399+
}
400+
401+
return $relativePath;
402+
}
403+
404+
/**
405+
* For site_url() helper.
406+
*
407+
* @param array|string $relativePath URI string or array of URI segments
408+
* @param string|null $scheme URI scheme. E.g., http, ftp
409+
* @param App|null $config Alternate configuration to use
410+
*/
411+
public function siteUrl($relativePath = '', ?string $scheme = null, ?App $config = null): string
412+
{
413+
$relativePath = $this->stringifyRelativePath($relativePath);
414+
415+
// Check current host.
416+
$host = $config === null ? $this->getHost() : null;
417+
418+
$config ??= config(App::class);
419+
420+
$uri = new self($config, $relativePath, $host, $scheme);
421+
422+
// Support protocol-relative links
423+
if ($scheme === '') {
424+
return substr((string) $uri, strlen($uri->getScheme()) + 1);
425+
}
426+
427+
return (string) $uri;
428+
}
366429
}

system/HTTP/SiteURIFactory.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
use CodeIgniter\Superglobals;
1616
use Config\App;
1717

18+
/**
19+
* Creates SiteURI using superglobals.
20+
*
21+
* This class also updates superglobal $_SERVER and $_GET.
22+
*/
1823
final class SiteURIFactory
1924
{
2025
private App $appConfig;
@@ -42,6 +47,7 @@ public function createFromGlobals(): SiteURI
4247
* Create the SiteURI object from URI string.
4348
*
4449
* @internal Used for testing purposes only.
50+
* @testTag
4551
*/
4652
public function createFromString(string $uri): SiteURI
4753
{
@@ -64,7 +70,7 @@ public function createFromString(string $uri): SiteURI
6470
$fragment = '#' . $parts['fragment'];
6571
}
6672

67-
$relativePath = $parts['path'] . $query . $fragment;
73+
$relativePath = ($parts['path'] ?? '') . $query . $fragment;
6874
$host = $this->getValidHost($parts['host']);
6975

7076
return new SiteURI($this->appConfig, $relativePath, $host, $parts['scheme']);
@@ -79,6 +85,7 @@ public function createFromString(string $uri): SiteURI
7985
* @return string The route path
8086
*
8187
* @internal Used for testing purposes only.
88+
* @testTag
8289
*/
8390
public function detectRoutePath(string $protocol = ''): string
8491
{

0 commit comments

Comments
 (0)