Skip to content

Commit 2d602ea

Browse files
committed
feat: add SiteURI class
1 parent 40be63e commit 2d602ea

File tree

3 files changed

+538
-0
lines changed

3 files changed

+538
-0
lines changed

system/HTTP/SiteURI.php

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
<?php
2+
3+
/**
4+
* This file is part of CodeIgniter 4 framework.
5+
*
6+
* (c) CodeIgniter Foundation <[email protected]>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
namespace CodeIgniter\HTTP;
13+
14+
use CodeIgniter\HTTP\Exceptions\HTTPException;
15+
use Config\App;
16+
17+
/**
18+
* URI for the application site
19+
*/
20+
class SiteURI extends URI
21+
{
22+
/**
23+
* The baseURL.
24+
*/
25+
private string $baseURL;
26+
27+
/**
28+
* The Index File.
29+
*/
30+
private string $indexPage;
31+
32+
/**
33+
* List of URI segments in baseURL and indexPage.
34+
*
35+
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
36+
* and the baseUR is "http://localhost:8888/ci431/public/", then:
37+
* $baseSegments = [
38+
* 0 => 'ci431',
39+
* 1 => 'public',
40+
* 2 => 'index.php',
41+
* ];
42+
*/
43+
private array $baseSegments;
44+
45+
/**
46+
* List of URI segments after indexPage.
47+
*
48+
* The word "URI Segments" originally means only the URI path part relative
49+
* to the baseURL.
50+
*
51+
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
52+
* and the baseUR is "http://localhost:8888/ci431/public/", then:
53+
* $segments = [
54+
* 0 => 'test',
55+
* ];
56+
*
57+
* @var array
58+
*
59+
* @deprecated This property will be private.
60+
*/
61+
protected $segments;
62+
63+
/**
64+
* URI path relative to baseURL.
65+
*
66+
* If the baseURL contains sub folders, this value will be different from
67+
* the current URI path.
68+
*/
69+
private string $routePath;
70+
71+
public function __construct(App $configApp)
72+
{
73+
// It's possible the user forgot a trailing slash on their
74+
// baseURL, so let's help them out.
75+
$baseURL = rtrim($configApp->baseURL, '/ ') . '/';
76+
77+
$this->baseURL = $baseURL;
78+
$this->indexPage = $configApp->indexPage;
79+
80+
$this->setBaseSeegments();
81+
82+
// Check for an index page
83+
$indexPage = '';
84+
if ($configApp->indexPage !== '') {
85+
$indexPage = $configApp->indexPage . '/';
86+
}
87+
88+
$tempUri = $this->baseURL . $indexPage;
89+
$uri = new URI($tempUri);
90+
91+
if ($configApp->forceGlobalSecureRequests) {
92+
$uri->setScheme('https');
93+
}
94+
95+
parent::__construct((string) $uri);
96+
97+
$this->setPath('/');
98+
}
99+
100+
/**
101+
* Sets baseSegments.
102+
*/
103+
private function setBaseSeegments(): void
104+
{
105+
$basePath = (new URI($this->baseURL))->getPath();
106+
$this->baseSegments = $this->convertToSegments($basePath);
107+
108+
if ($this->indexPage) {
109+
$this->baseSegments[] = $this->indexPage;
110+
}
111+
}
112+
113+
/**
114+
* Returns the URI path relative to baseURL.
115+
*
116+
* @return string The Route path.
117+
*/
118+
public function getRoutePath(): string
119+
{
120+
return $this->routePath;
121+
}
122+
123+
/**
124+
* Returns the URI segments of the path as an array.
125+
*/
126+
public function getSegments(): array
127+
{
128+
return $this->segments;
129+
}
130+
131+
/**
132+
* Returns the value of a specific segment of the URI path relative to baseURL.
133+
*
134+
* @param int $number Segment number
135+
* @param string $default Default value
136+
*
137+
* @return string The value of the segment. If no segment is found,
138+
* throws HTTPException
139+
*/
140+
public function getSegment(int $number, string $default = ''): string
141+
{
142+
if ($number < 1) {
143+
throw HTTPException::forURISegmentOutOfRange($number);
144+
}
145+
146+
if ($number > count($this->segments) && ! $this->silent) {
147+
throw HTTPException::forURISegmentOutOfRange($number);
148+
}
149+
150+
// The segment should treat the array as 1-based for the user
151+
// but we still have to deal with a zero-based array.
152+
$number--;
153+
154+
return $this->segments[$number] ?? $default;
155+
}
156+
157+
/**
158+
* Set the value of a specific segment of the URI path relative to baseURL.
159+
* Allows to set only existing segments or add new one.
160+
*
161+
* @param int $number The segment number. Starting with 1.
162+
* @param string $value The segment value.
163+
*
164+
* @return $this
165+
*/
166+
public function setSegment(int $number, $value)
167+
{
168+
if ($number < 1) {
169+
throw HTTPException::forURISegmentOutOfRange($number);
170+
}
171+
172+
if ($number > count($this->segments) + 1) {
173+
if ($this->silent) {
174+
return $this;
175+
}
176+
177+
throw HTTPException::forURISegmentOutOfRange($number);
178+
}
179+
180+
// The segment should treat the array as 1-based for the user,
181+
// but we still have to deal with a zero-based array.
182+
$number--;
183+
184+
$this->segments[$number] = $value;
185+
186+
$this->refreshPath();
187+
188+
return $this;
189+
}
190+
191+
/**
192+
* Returns the total number of segments.
193+
*/
194+
public function getTotalSegments(): int
195+
{
196+
return count($this->segments);
197+
}
198+
199+
/**
200+
* Formats the URI as a string.
201+
*/
202+
public function __toString(): string
203+
{
204+
return static::createURIString(
205+
$this->getScheme(),
206+
$this->getAuthority(),
207+
$this->getPath(), // Absolute URIs should use a "/" for an empty path
208+
$this->getQuery(),
209+
$this->getFragment()
210+
);
211+
}
212+
213+
/**
214+
* Sets the route path (and segments).
215+
*
216+
* @return $this
217+
*/
218+
public function setPath(string $path)
219+
{
220+
$this->routePath = $this->filterPath($path);
221+
222+
$this->segments = $this->convertToSegments($this->routePath);
223+
224+
$this->refreshPath();
225+
226+
return $this;
227+
}
228+
229+
/**
230+
* Converts path to segments
231+
*/
232+
private function convertToSegments(string $path): array
233+
{
234+
$tempPath = trim($path, '/');
235+
236+
return ($tempPath === '') ? [] : explode('/', $tempPath);
237+
}
238+
239+
/**
240+
* Sets the path portion of the URI based on segments.
241+
*
242+
* @return $this
243+
*
244+
* @deprecated This method will be private.
245+
*/
246+
public function refreshPath()
247+
{
248+
$allSegments = array_merge($this->baseSegments, $this->segments);
249+
$this->path = '/' . $this->filterPath(implode('/', $allSegments));
250+
251+
$this->routePath = $this->filterPath(implode('/', $this->segments));
252+
253+
if ($this->routePath === '') {
254+
$this->routePath = '/';
255+
256+
if ($this->indexPage !== '') {
257+
$this->path .= '/';
258+
}
259+
}
260+
261+
return $this;
262+
}
263+
264+
/**
265+
* Saves our parts from a parse_url() call.
266+
*/
267+
protected function applyParts(array $parts)
268+
{
269+
if (! empty($parts['host'])) {
270+
$this->host = $parts['host'];
271+
}
272+
if (! empty($parts['user'])) {
273+
$this->user = $parts['user'];
274+
}
275+
if (isset($parts['path']) && $parts['path'] !== '') {
276+
$this->path = $this->filterPath($parts['path']);
277+
}
278+
if (! empty($parts['query'])) {
279+
$this->setQuery($parts['query']);
280+
}
281+
if (! empty($parts['fragment'])) {
282+
$this->fragment = $parts['fragment'];
283+
}
284+
285+
// Scheme
286+
if (isset($parts['scheme'])) {
287+
$this->setScheme(rtrim($parts['scheme'], ':/'));
288+
} else {
289+
$this->setScheme('http');
290+
}
291+
292+
// Port
293+
if (isset($parts['port']) && $parts['port'] !== null) {
294+
// Valid port numbers are enforced by earlier parse_url() or setPort()
295+
$this->port = $parts['port'];
296+
}
297+
298+
if (isset($parts['pass'])) {
299+
$this->password = $parts['pass'];
300+
}
301+
}
302+
}

system/HTTP/URI.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,8 @@ public function getBaseURL(): string
785785
* Sets the path portion of the URI based on segments.
786786
*
787787
* @return $this
788+
*
789+
* @deprecated This method will be private.
788790
*/
789791
public function refreshPath()
790792
{

0 commit comments

Comments
 (0)