Skip to content

Commit f934428

Browse files
committed
Refactor URI detection
1 parent 7546ee0 commit f934428

File tree

5 files changed

+236
-129
lines changed

5 files changed

+236
-129
lines changed

system/HTTP/IncomingRequest.php

Lines changed: 111 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,26 @@ class IncomingRequest extends Request
5656
protected $enableCSRF = false;
5757

5858
/**
59-
* A \CodeIgniter\HTTP\URI instance.
59+
* The URI representing the current URL for this request.
60+
*
61+
* Note: When this is returned as a shared instance via
62+
* Services::request() this property points to the shared
63+
* URI service and represents the primary "current URL"
64+
* for the main app.
65+
*
66+
* @see CodeIgniter\CodeIgniter::getRequestObject()
6067
*
6168
* @var URI
6269
*/
6370
public $uri;
6471

72+
/**
73+
* The detected path (relative to SCRIPT_NAME).
74+
*
75+
* @var string|null
76+
*/
77+
protected $path;
78+
6579
/**
6680
* File collection
6781
*
@@ -142,31 +156,16 @@ public function __construct($config, URI $uri = null, $body = 'php://input', Use
142156
$body = file_get_contents('php://input');
143157
}
144158

145-
$this->body = ! empty($body) ? $body : null;
146-
$this->config = $config;
147-
$this->userAgent = $userAgent;
159+
$this->config = $config;
160+
$this->uri = $uri;
161+
$this->body = ! empty($body) ? $body : null;
162+
$this->userAgent = $userAgent;
163+
$this->validLocales = $config->supportedLocales;
148164

149165
parent::__construct($config);
150166

151167
$this->populateHeaders();
152-
153-
// Determine the current URI
154-
// NOTE: This WILL NOT match the actual URL in the browser since for
155-
// everything this cares about (and the router, etc) is the portion
156-
// AFTER the script name. So, if hosted in a sub-folder this will
157-
// appear different than actual URL. If you need that, use current_url().
158-
$this->uri = $uri;
159-
160-
$this->detectURI($config->uriProtocol, $config->baseURL);
161-
162-
// Check if the baseURL scheme needs to be coerced into its secure version
163-
if ($config->forceGlobalSecureRequests && $this->uri->getScheme() === 'http')
164-
{
165-
$this->uri->setScheme('https');
166-
}
167-
168-
$this->validLocales = $config->supportedLocales;
169-
168+
$this->detectURL();
170169
$this->detectLocale($config);
171170
}
172171

@@ -603,13 +602,67 @@ public function getFile(string $fileID)
603602

604603
//--------------------------------------------------------------------
605604

605+
/**
606+
* Sets the components of the underlying URI instance to
607+
* match the current URL as configured and detected.
608+
*/
609+
protected function detectURL()
610+
{
611+
// Require a baseURL for non-CLI requests
612+
if ($this->config->baseURL === '')
613+
{
614+
if (is_cli())
615+
{
616+
$this->uri->setPath($this->getPath());
617+
return;
618+
}
619+
620+
die('You have an empty or invalid base URL. The baseURL value must be set in Config\App.php, or through the .env file.'); // @codeCoverageIgnore
621+
}
622+
623+
// Build the full URL based on the config and relative path
624+
$path = $this->getPath();
625+
$url = rtrim($this->config->baseURL, '/ ') . '/';
626+
627+
// Check for an index page
628+
if ($this->config->indexPage !== '')
629+
{
630+
$url .= $this->config->indexPage;
631+
632+
// If the path has anything other than ? (for query) we need a separator
633+
if ($path !== '' && strncmp($path, '?', 1) !== 0)
634+
{
635+
$url .= '/';
636+
}
637+
}
638+
639+
$url .= $path;
640+
641+
$this->uri->setURI($url);
642+
643+
// Check if the baseURL scheme needs to be coerced into its secure version
644+
if ($this->config->forceGlobalSecureRequests && $this->uri->getScheme() === 'http')
645+
{
646+
$this->uri->setScheme('https');
647+
}
648+
649+
// Set any remaining query vars
650+
if (isset($_SERVER['QUERY_STRING']))
651+
{
652+
$this->uri->setQuery($_SERVER['QUERY_STRING']);
653+
}
654+
}
655+
606656
/**
607657
* Sets up our URI object based on the information we have. This is
608658
* either provided by the user in the baseURL Config setting, or
609659
* determined from the environment as needed.
610660
*
611661
* @param string $protocol
612662
* @param string $baseURL
663+
*
664+
* @deprecated Use detectCurrentURI() instead
665+
* @codeCoverageIgnore
613666
*/
614667
protected function detectURI(string $protocol, string $baseURL)
615668
{
@@ -632,20 +685,34 @@ protected function detectURI(string $protocol, string $baseURL)
632685
}
633686
else
634687
{
635-
// @codeCoverageIgnoreStart
636688
if (! is_cli())
637689
{
638690
die('You have an empty or invalid base URL. The baseURL value must be set in Config\App.php, or through the .env file.');
639691
}
640-
// @codeCoverageIgnoreEnd
641692
}
642693
}
643694

644695
//--------------------------------------------------------------------
645696

646697
/**
647-
* Based on the URIProtocol Config setting, will attempt to
648-
* detect the path portion of the current URI.
698+
* Returns the path relative to SCRIPT_NAME,
699+
* running detection as necessary.
700+
*
701+
* @return string
702+
*/
703+
public function getPath(): string
704+
{
705+
if (is_null($this->path))
706+
{
707+
$this->detectPath($this->config->uriProtocol);
708+
}
709+
710+
return $this->path;
711+
}
712+
713+
/**
714+
* Detects the relative path based on
715+
* the URIProtocol Config setting.
649716
*
650717
* @param string $protocol
651718
*
@@ -661,18 +728,18 @@ public function detectPath(string $protocol = ''): string
661728
switch ($protocol)
662729
{
663730
case 'REQUEST_URI':
664-
$path = $this->parseRequestURI();
731+
$this->path = $this->parseRequestURI();
665732
break;
666733
case 'QUERY_STRING':
667-
$path = $this->parseQueryString();
734+
$this->path = $this->parseQueryString();
668735
break;
669736
case 'PATH_INFO':
670737
default:
671-
$path = $this->fetchGlobal('server', $protocol) ?? $this->parseRequestURI();
738+
$this->path = $this->fetchGlobal('server', $protocol) ?? $this->parseRequestURI();
672739
break;
673740
}
674741

675-
return $path;
742+
return $this->path;
676743
}
677744

678745
//--------------------------------------------------------------------
@@ -731,23 +798,23 @@ protected function parseRequestURI(): string
731798
$query = $parts['query'] ?? '';
732799
$uri = $parts['path'] ?? '';
733800

734-
if (isset($_SERVER['SCRIPT_NAME'][0]) && pathinfo($_SERVER['SCRIPT_NAME'], PATHINFO_EXTENSION) === 'php')
801+
// Strip the SCRIPT_NAME path from the URI
802+
if ($uri !== '' && isset($_SERVER['SCRIPT_NAME'][0]) && pathinfo($_SERVER['SCRIPT_NAME'], PATHINFO_EXTENSION) === 'php')
735803
{
736-
// strip the script name from the beginning of the URI
737-
if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0 && strpos($uri, '/index.php') === 0)
804+
// Compare each segment, dropping them until there is no match
805+
$segments = $keep = explode('/', $uri);
806+
foreach (explode('/', $_SERVER['SCRIPT_NAME']) as $i => $segment)
738807
{
739-
$uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME']));
740-
}
741-
// if the script is nested, strip the parent folder & script from the URI
742-
elseif (strpos($uri, $_SERVER['SCRIPT_NAME']) > 0)
743-
{
744-
$uri = (string) substr($uri, strpos($uri, $_SERVER['SCRIPT_NAME']) + strlen($_SERVER['SCRIPT_NAME']));
745-
}
746-
// or if index.php is implied
747-
elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
748-
{
749-
$uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
808+
// If these segments are not the same then we're done
809+
if ($segment !== $segments[$i])
810+
{
811+
break;
812+
}
813+
814+
array_shift($keep);
750815
}
816+
817+
$uri = implode('/', $keep);
751818
}
752819

753820
// This section ensures that even on servers that require the URI to contain the query string (Nginx) a correct

0 commit comments

Comments
 (0)