Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ac527d
feat: add SiteURI class
kenjis Feb 14, 2023
7eec7bb
chore: add system/HTTP/SiteURI.php
kenjis Feb 14, 2023
5593f8a
docs: add @deprecated
kenjis Feb 14, 2023
8a66de5
test: fix incorrect test
kenjis Feb 14, 2023
406128f
refactor: fix typo in method name
kenjis Feb 14, 2023
7d91c0e
docs: update PHPDocs
kenjis Feb 14, 2023
b728948
docs: add @deprecated
kenjis Feb 15, 2023
9af4994
feat: add validation for baseURL
kenjis Feb 15, 2023
5fa5305
feat: add getBaseURL() implementation
kenjis Feb 15, 2023
c8d5eef
feat: add param $relativePath to constructor
kenjis Feb 16, 2023
b30035b
feat: add param $host to constructor
kenjis Feb 16, 2023
9d82338
feat: add param $scheme to constructor
kenjis Feb 17, 2023
b231d1c
style: break long line
kenjis Feb 17, 2023
4932572
fix: remove unneeded methods
kenjis Feb 17, 2023
c3628d1
fix: change default values to null
kenjis Feb 17, 2023
57afaf9
fix: handling fragment
kenjis Feb 17, 2023
246eea2
fix: host in baseURL may be wrong
kenjis Feb 18, 2023
08bbc18
fix: disable setBaseURL() method
kenjis Feb 18, 2023
ab6da61
test: add assertions
kenjis Feb 20, 2023
82a567d
refactor: SiteURI::__construct()
kenjis Feb 20, 2023
ccfd79d
test: add test cases for path starting with slash
kenjis Feb 20, 2023
e733417
fix: change behavior
kenjis Feb 21, 2023
33fc028
test: use dataProviders
kenjis Feb 21, 2023
ba35e57
docs: update comments
kenjis Feb 22, 2023
fe754f3
fix: change behavior
kenjis Feb 23, 2023
3f4a9fd
fix: remove $allowedHostnames check
kenjis Feb 23, 2023
ecf96eb
test: add test cases
kenjis Feb 23, 2023
5193414
refactor: remove unneeded override
kenjis Feb 24, 2023
b24c01c
docs: fix typo in comments
kenjis Jul 10, 2023
3e349e1
docs: make URI::setSilent() deprecated
kenjis Jul 10, 2023
e529291
feat: add URI::withScheme() and deprecate URI::setScheme()
kenjis Jul 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test-phpcpd.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,4 +55,5 @@ jobs:
--exclude system/Database/OCI8/Builder.php
--exclude system/Database/Postgre/Builder.php
--exclude system/Debug/Exceptions.php
--exclude system/HTTP/SiteURI.php
-- app/ public/ system/
366 changes: 366 additions & 0 deletions system/HTTP/SiteURI.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\HTTP;

use BadMethodCallException;
use CodeIgniter\Exceptions\ConfigException;
use CodeIgniter\HTTP\Exceptions\HTTPException;
use Config\App;

/**
* URI for the application site
*/
class SiteURI extends URI
{
/**
* The current baseURL.
*/
private URI $baseURL;

/**
* The path part of baseURL.
*
* The baseURL "http://example.com/" → '/'
* The baseURL "http://localhost:8888/ci431/public/" → '/ci431/public/'
*/
private string $basePathWithoutIndexPage;

/**
* The Index File.
*/
private string $indexPage;

/**
* List of URI segments in baseURL and indexPage.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $baseSegments = [
* 0 => 'ci431',
* 1 => 'public',
* 2 => 'index.php',
* ];
*/
private array $baseSegments;

/**
* List of URI segments after indexPage.
*
* The word "URI Segments" originally means only the URI path part relative
* to the baseURL.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $segments = [
* 0 => 'test',
* ];
*
* @var array
*
* @deprecated This property will be private.
Comment thread
MGatner marked this conversation as resolved.
*/
protected $segments;

/**
* URI path relative to baseURL.
*
* If the baseURL contains sub folders, this value will be different from
* the current URI path.
*
* This value never starts with '/'.
*/
private string $routePath;

/**
* @param string $relativePath URI path relative to baseURL. May include
* queries or fragments.
* @param string|null $host Optional current hostname.
* @param string|null $scheme Optional scheme. 'http' or 'https'.
* @phpstan-param 'http'|'https'|null $scheme
*/
public function __construct(
App $configApp,
string $relativePath = '',
?string $host = null,
?string $scheme = null
) {
$this->indexPage = $configApp->indexPage;

$this->baseURL = $this->determineBaseURL($configApp, $host, $scheme);

$this->setBasePath();

// Fix routePath, query, fragment
[$routePath, $query, $fragment] = $this->parseRelativePath($relativePath);

// Fix indexPage and routePath
$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

// Fix the current URI
$uri = $this->baseURL . $indexPageRoutePath;

// applyParts
$parts = parse_url($uri);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($uri);
}
$parts['query'] = $query;
$parts['fragment'] = $fragment;
$this->applyParts($parts);

$this->setRoutePath($routePath);
}

private function parseRelativePath(string $relativePath): array
{
$parts = parse_url('http://dummy/' . $relativePath);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($relativePath);
}

$routePath = $relativePath === '/' ? '/' : ltrim($parts['path'], '/');

$query = $parts['query'] ?? '';
$fragment = $parts['fragment'] ?? '';

return [$routePath, $query, $fragment];
}

private function determineBaseURL(
App $configApp,
?string $host,
?string $scheme
): URI {
$baseURL = $this->normalizeBaseURL($configApp);

$uri = new URI($baseURL);

// Update scheme
if ($scheme !== null) {
$uri->setScheme($scheme);
} elseif ($configApp->forceGlobalSecureRequests) {
$uri->setScheme('https');
}

// Update host
if ($host !== null) {
$uri->setHost($host);
}

return $uri;
}

private function getIndexPageRoutePath(string $routePath): string
{
// Remove starting slash unless it is `/`.
if ($routePath !== '' && $routePath[0] === '/' && $routePath !== '/') {
$routePath = ltrim($routePath, '/');
}

// Check for an index page
$indexPage = '';
if ($this->indexPage !== '') {
$indexPage = $this->indexPage;

// Check if we need a separator
if ($routePath !== '' && $routePath[0] !== '/' && $routePath[0] !== '?') {
$indexPage .= '/';
}
}

$indexPageRoutePath = $indexPage . $routePath;

if ($indexPageRoutePath === '/') {
$indexPageRoutePath = '';
}

return $indexPageRoutePath;
}

private function normalizeBaseURL(App $configApp): string
{
// It's possible the user forgot a trailing slash on their
// baseURL, so let's help them out.
$baseURL = rtrim($configApp->baseURL, '/ ') . '/';

// Validate baseURL
if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) {
throw new ConfigException(
'Config\App::$baseURL is invalid.'
);
}

return $baseURL;
}

/**
* Sets basePathWithoutIndexPage and baseSegments.
*/
private function setBasePath(): void
{
$this->basePathWithoutIndexPage = $this->baseURL->getPath();

$this->baseSegments = $this->convertToSegments($this->basePathWithoutIndexPage);

if ($this->indexPage) {
$this->baseSegments[] = $this->indexPage;
}
}

/**
* @deprecated
*/
public function setBaseURL(string $baseURL): void
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* @deprecated
*/
public function setURI(?string $uri = null)
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* Returns the baseURL.
*
* @interal
*/
public function getBaseURL(): string
{
return (string) $this->baseURL;
}

/**
* Returns the URI path relative to baseURL.
*
* @return string The Route path.
*/
public function getRoutePath(): string
{
return $this->routePath;
}

/**
* Formats the URI as a string.
*/
public function __toString(): string
{
return static::createURIString(
$this->getScheme(),
$this->getAuthority(),
$this->getPath(),
$this->getQuery(),
$this->getFragment()
);
}

/**
* Sets the route path (and segments).
*
* @return $this
*/
public function setPath(string $path)
{
$this->setRoutePath($path);

return $this;
}

/**
* Sets the route path (and segments).
*/
private function setRoutePath(string $routePath): void
{
$routePath = $this->filterPath($routePath);

$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

$this->path = $this->basePathWithoutIndexPage . $indexPageRoutePath;

$this->routePath = ltrim($routePath, '/');

$this->segments = $this->convertToSegments($this->routePath);
}

/**
* Converts path to segments
*/
private function convertToSegments(string $path): array
{
$tempPath = trim($path, '/');

return ($tempPath === '') ? [] : explode('/', $tempPath);
}

/**
* Sets the path portion of the URI based on segments.
*
* @return $this
*
* @deprecated This method will be private.
*/
public function refreshPath()
{
$allSegments = array_merge($this->baseSegments, $this->segments);
$this->path = '/' . $this->filterPath(implode('/', $allSegments));

if ($this->routePath === '/' && $this->path !== '/') {
$this->path .= '/';
}

$this->routePath = $this->filterPath(implode('/', $this->segments));

return $this;
}

/**
* Saves our parts from a parse_url() call.
*/
protected function applyParts(array $parts)
{
if (! empty($parts['host'])) {
$this->host = $parts['host'];
}
if (! empty($parts['user'])) {
$this->user = $parts['user'];
}
if (isset($parts['path']) && $parts['path'] !== '') {
$this->path = $this->filterPath($parts['path']);
}
if (! empty($parts['query'])) {
$this->setQuery($parts['query']);
}
if (! empty($parts['fragment'])) {
$this->fragment = $parts['fragment'];
}

// Scheme
if (isset($parts['scheme'])) {
$this->setScheme(rtrim($parts['scheme'], ':/'));
} else {
$this->setScheme('http');
}

// Port
if (isset($parts['port']) && $parts['port'] !== null) {
// Valid port numbers are enforced by earlier parse_url() or setPort()
$this->port = $parts['port'];
}

if (isset($parts['pass'])) {
$this->password = $parts['pass'];
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ac527d
feat: add SiteURI class
kenjis Feb 14, 2023
7eec7bb
chore: add system/HTTP/SiteURI.php
kenjis Feb 14, 2023
5593f8a
docs: add @deprecated
kenjis Feb 14, 2023
8a66de5
test: fix incorrect test
kenjis Feb 14, 2023
406128f
refactor: fix typo in method name
kenjis Feb 14, 2023
7d91c0e
docs: update PHPDocs
kenjis Feb 14, 2023
b728948
docs: add @deprecated
kenjis Feb 15, 2023
9af4994
feat: add validation for baseURL
kenjis Feb 15, 2023
5fa5305
feat: add getBaseURL() implementation
kenjis Feb 15, 2023
c8d5eef
feat: add param $relativePath to constructor
kenjis Feb 16, 2023
b30035b
feat: add param $host to constructor
kenjis Feb 16, 2023
9d82338
feat: add param $scheme to constructor
kenjis Feb 17, 2023
b231d1c
style: break long line
kenjis Feb 17, 2023
4932572
fix: remove unneeded methods
kenjis Feb 17, 2023
c3628d1
fix: change default values to null
kenjis Feb 17, 2023
57afaf9
fix: handling fragment
kenjis Feb 17, 2023
246eea2
fix: host in baseURL may be wrong
kenjis Feb 18, 2023
08bbc18
fix: disable setBaseURL() method
kenjis Feb 18, 2023
ab6da61
test: add assertions
kenjis Feb 20, 2023
82a567d
refactor: SiteURI::__construct()
kenjis Feb 20, 2023
ccfd79d
test: add test cases for path starting with slash
kenjis Feb 20, 2023
e733417
fix: change behavior
kenjis Feb 21, 2023
33fc028
test: use dataProviders
kenjis Feb 21, 2023
ba35e57
docs: update comments
kenjis Feb 22, 2023
fe754f3
fix: change behavior
kenjis Feb 23, 2023
3f4a9fd
fix: remove $allowedHostnames check
kenjis Feb 23, 2023
ecf96eb
test: add test cases
kenjis Feb 23, 2023
5193414
refactor: remove unneeded override
kenjis Feb 24, 2023
b24c01c
docs: fix typo in comments
kenjis Jul 10, 2023
3e349e1
docs: make URI::setSilent() deprecated
kenjis Jul 10, 2023
e529291
feat: add URI::withScheme() and deprecate URI::setScheme()
kenjis Jul 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test-phpcpd.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,4 +55,5 @@ jobs:
--exclude system/Database/OCI8/Builder.php
--exclude system/Database/Postgre/Builder.php
--exclude system/Debug/Exceptions.php
--exclude system/HTTP/SiteURI.php
-- app/ public/ system/
366 changes: 366 additions & 0 deletions system/HTTP/SiteURI.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\HTTP;

use BadMethodCallException;
use CodeIgniter\Exceptions\ConfigException;
use CodeIgniter\HTTP\Exceptions\HTTPException;
use Config\App;

/**
* URI for the application site
*/
class SiteURI extends URI
{
/**
* The current baseURL.
*/
private URI $baseURL;

/**
* The path part of baseURL.
*
* The baseURL "http://example.com/" → '/'
* The baseURL "http://localhost:8888/ci431/public/" → '/ci431/public/'
*/
private string $basePathWithoutIndexPage;

/**
* The Index File.
*/
private string $indexPage;

/**
* List of URI segments in baseURL and indexPage.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $baseSegments = [
* 0 => 'ci431',
* 1 => 'public',
* 2 => 'index.php',
* ];
*/
private array $baseSegments;

/**
* List of URI segments after indexPage.
*
* The word "URI Segments" originally means only the URI path part relative
* to the baseURL.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $segments = [
* 0 => 'test',
* ];
*
* @var array
*
* @deprecated This property will be private.
Comment thread
MGatner marked this conversation as resolved.
*/
protected $segments;

/**
* URI path relative to baseURL.
*
* If the baseURL contains sub folders, this value will be different from
* the current URI path.
*
* This value never starts with '/'.
*/
private string $routePath;

/**
* @param string $relativePath URI path relative to baseURL. May include
* queries or fragments.
* @param string|null $host Optional current hostname.
* @param string|null $scheme Optional scheme. 'http' or 'https'.
* @phpstan-param 'http'|'https'|null $scheme
*/
public function __construct(
App $configApp,
string $relativePath = '',
?string $host = null,
?string $scheme = null
) {
$this->indexPage = $configApp->indexPage;

$this->baseURL = $this->determineBaseURL($configApp, $host, $scheme);

$this->setBasePath();

// Fix routePath, query, fragment
[$routePath, $query, $fragment] = $this->parseRelativePath($relativePath);

// Fix indexPage and routePath
$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

// Fix the current URI
$uri = $this->baseURL . $indexPageRoutePath;

// applyParts
$parts = parse_url($uri);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($uri);
}
$parts['query'] = $query;
$parts['fragment'] = $fragment;
$this->applyParts($parts);

$this->setRoutePath($routePath);
}

private function parseRelativePath(string $relativePath): array
{
$parts = parse_url('http://dummy/' . $relativePath);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($relativePath);
}

$routePath = $relativePath === '/' ? '/' : ltrim($parts['path'], '/');

$query = $parts['query'] ?? '';
$fragment = $parts['fragment'] ?? '';

return [$routePath, $query, $fragment];
}

private function determineBaseURL(
App $configApp,
?string $host,
?string $scheme
): URI {
$baseURL = $this->normalizeBaseURL($configApp);

$uri = new URI($baseURL);

// Update scheme
if ($scheme !== null) {
$uri->setScheme($scheme);
} elseif ($configApp->forceGlobalSecureRequests) {
$uri->setScheme('https');
}

// Update host
if ($host !== null) {
$uri->setHost($host);
}

return $uri;
}

private function getIndexPageRoutePath(string $routePath): string
{
// Remove starting slash unless it is `/`.
if ($routePath !== '' && $routePath[0] === '/' && $routePath !== '/') {
$routePath = ltrim($routePath, '/');
}

// Check for an index page
$indexPage = '';
if ($this->indexPage !== '') {
$indexPage = $this->indexPage;

// Check if we need a separator
if ($routePath !== '' && $routePath[0] !== '/' && $routePath[0] !== '?') {
$indexPage .= '/';
}
}

$indexPageRoutePath = $indexPage . $routePath;

if ($indexPageRoutePath === '/') {
$indexPageRoutePath = '';
}

return $indexPageRoutePath;
}

private function normalizeBaseURL(App $configApp): string
{
// It's possible the user forgot a trailing slash on their
// baseURL, so let's help them out.
$baseURL = rtrim($configApp->baseURL, '/ ') . '/';

// Validate baseURL
if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) {
throw new ConfigException(
'Config\App::$baseURL is invalid.'
);
}

return $baseURL;
}

/**
* Sets basePathWithoutIndexPage and baseSegments.
*/
private function setBasePath(): void
{
$this->basePathWithoutIndexPage = $this->baseURL->getPath();

$this->baseSegments = $this->convertToSegments($this->basePathWithoutIndexPage);

if ($this->indexPage) {
$this->baseSegments[] = $this->indexPage;
}
}

/**
* @deprecated
*/
public function setBaseURL(string $baseURL): void
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* @deprecated
*/
public function setURI(?string $uri = null)
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* Returns the baseURL.
*
* @interal
*/
public function getBaseURL(): string
{
return (string) $this->baseURL;
}

/**
* Returns the URI path relative to baseURL.
*
* @return string The Route path.
*/
public function getRoutePath(): string
{
return $this->routePath;
}

/**
* Formats the URI as a string.
*/
public function __toString(): string
{
return static::createURIString(
$this->getScheme(),
$this->getAuthority(),
$this->getPath(),
$this->getQuery(),
$this->getFragment()
);
}

/**
* Sets the route path (and segments).
*
* @return $this
*/
public function setPath(string $path)
{
$this->setRoutePath($path);

return $this;
}

/**
* Sets the route path (and segments).
*/
private function setRoutePath(string $routePath): void
{
$routePath = $this->filterPath($routePath);

$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

$this->path = $this->basePathWithoutIndexPage . $indexPageRoutePath;

$this->routePath = ltrim($routePath, '/');

$this->segments = $this->convertToSegments($this->routePath);
}

/**
* Converts path to segments
*/
private function convertToSegments(string $path): array
{
$tempPath = trim($path, '/');

return ($tempPath === '') ? [] : explode('/', $tempPath);
}

/**
* Sets the path portion of the URI based on segments.
*
* @return $this
*
* @deprecated This method will be private.
*/
public function refreshPath()
{
$allSegments = array_merge($this->baseSegments, $this->segments);
$this->path = '/' . $this->filterPath(implode('/', $allSegments));

if ($this->routePath === '/' && $this->path !== '/') {
$this->path .= '/';
}

$this->routePath = $this->filterPath(implode('/', $this->segments));

return $this;
}

/**
* Saves our parts from a parse_url() call.
*/
protected function applyParts(array $parts)
{
if (! empty($parts['host'])) {
$this->host = $parts['host'];
}
if (! empty($parts['user'])) {
$this->user = $parts['user'];
}
if (isset($parts['path']) && $parts['path'] !== '') {
$this->path = $this->filterPath($parts['path']);
}
if (! empty($parts['query'])) {
$this->setQuery($parts['query']);
}
if (! empty($parts['fragment'])) {
$this->fragment = $parts['fragment'];
}

// Scheme
if (isset($parts['scheme'])) {
$this->setScheme(rtrim($parts['scheme'], ':/'));
} else {
$this->setScheme('http');
}

// Port
if (isset($parts['port']) && $parts['port'] !== null) {
// Valid port numbers are enforced by earlier parse_url() or setPort()
$this->port = $parts['port'];
}

if (isset($parts['pass'])) {
$this->password = $parts['pass'];
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ac527d
feat: add SiteURI class
kenjis Feb 14, 2023
7eec7bb
chore: add system/HTTP/SiteURI.php
kenjis Feb 14, 2023
5593f8a
docs: add @deprecated
kenjis Feb 14, 2023
8a66de5
test: fix incorrect test
kenjis Feb 14, 2023
406128f
refactor: fix typo in method name
kenjis Feb 14, 2023
7d91c0e
docs: update PHPDocs
kenjis Feb 14, 2023
b728948
docs: add @deprecated
kenjis Feb 15, 2023
9af4994
feat: add validation for baseURL
kenjis Feb 15, 2023
5fa5305
feat: add getBaseURL() implementation
kenjis Feb 15, 2023
c8d5eef
feat: add param $relativePath to constructor
kenjis Feb 16, 2023
b30035b
feat: add param $host to constructor
kenjis Feb 16, 2023
9d82338
feat: add param $scheme to constructor
kenjis Feb 17, 2023
b231d1c
style: break long line
kenjis Feb 17, 2023
4932572
fix: remove unneeded methods
kenjis Feb 17, 2023
c3628d1
fix: change default values to null
kenjis Feb 17, 2023
57afaf9
fix: handling fragment
kenjis Feb 17, 2023
246eea2
fix: host in baseURL may be wrong
kenjis Feb 18, 2023
08bbc18
fix: disable setBaseURL() method
kenjis Feb 18, 2023
ab6da61
test: add assertions
kenjis Feb 20, 2023
82a567d
refactor: SiteURI::__construct()
kenjis Feb 20, 2023
ccfd79d
test: add test cases for path starting with slash
kenjis Feb 20, 2023
e733417
fix: change behavior
kenjis Feb 21, 2023
33fc028
test: use dataProviders
kenjis Feb 21, 2023
ba35e57
docs: update comments
kenjis Feb 22, 2023
fe754f3
fix: change behavior
kenjis Feb 23, 2023
3f4a9fd
fix: remove $allowedHostnames check
kenjis Feb 23, 2023
ecf96eb
test: add test cases
kenjis Feb 23, 2023
5193414
refactor: remove unneeded override
kenjis Feb 24, 2023
b24c01c
docs: fix typo in comments
kenjis Jul 10, 2023
3e349e1
docs: make URI::setSilent() deprecated
kenjis Jul 10, 2023
e529291
feat: add URI::withScheme() and deprecate URI::setScheme()
kenjis Jul 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test-phpcpd.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,4 +55,5 @@ jobs:
--exclude system/Database/OCI8/Builder.php
--exclude system/Database/Postgre/Builder.php
--exclude system/Debug/Exceptions.php
--exclude system/HTTP/SiteURI.php
-- app/ public/ system/
366 changes: 366 additions & 0 deletions system/HTTP/SiteURI.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\HTTP;

use BadMethodCallException;
use CodeIgniter\Exceptions\ConfigException;
use CodeIgniter\HTTP\Exceptions\HTTPException;
use Config\App;

/**
* URI for the application site
*/
class SiteURI extends URI
{
/**
* The current baseURL.
*/
private URI $baseURL;

/**
* The path part of baseURL.
*
* The baseURL "http://example.com/" → '/'
* The baseURL "http://localhost:8888/ci431/public/" → '/ci431/public/'
*/
private string $basePathWithoutIndexPage;

/**
* The Index File.
*/
private string $indexPage;

/**
* List of URI segments in baseURL and indexPage.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $baseSegments = [
* 0 => 'ci431',
* 1 => 'public',
* 2 => 'index.php',
* ];
*/
private array $baseSegments;

/**
* List of URI segments after indexPage.
*
* The word "URI Segments" originally means only the URI path part relative
* to the baseURL.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $segments = [
* 0 => 'test',
* ];
*
* @var array
*
* @deprecated This property will be private.
Comment thread
MGatner marked this conversation as resolved.
*/
protected $segments;

/**
* URI path relative to baseURL.
*
* If the baseURL contains sub folders, this value will be different from
* the current URI path.
*
* This value never starts with '/'.
*/
private string $routePath;

/**
* @param string $relativePath URI path relative to baseURL. May include
* queries or fragments.
* @param string|null $host Optional current hostname.
* @param string|null $scheme Optional scheme. 'http' or 'https'.
* @phpstan-param 'http'|'https'|null $scheme
*/
public function __construct(
App $configApp,
string $relativePath = '',
?string $host = null,
?string $scheme = null
) {
$this->indexPage = $configApp->indexPage;

$this->baseURL = $this->determineBaseURL($configApp, $host, $scheme);

$this->setBasePath();

// Fix routePath, query, fragment
[$routePath, $query, $fragment] = $this->parseRelativePath($relativePath);

// Fix indexPage and routePath
$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

// Fix the current URI
$uri = $this->baseURL . $indexPageRoutePath;

// applyParts
$parts = parse_url($uri);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($uri);
}
$parts['query'] = $query;
$parts['fragment'] = $fragment;
$this->applyParts($parts);

$this->setRoutePath($routePath);
}

private function parseRelativePath(string $relativePath): array
{
$parts = parse_url('http://dummy/' . $relativePath);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($relativePath);
}

$routePath = $relativePath === '/' ? '/' : ltrim($parts['path'], '/');

$query = $parts['query'] ?? '';
$fragment = $parts['fragment'] ?? '';

return [$routePath, $query, $fragment];
}

private function determineBaseURL(
App $configApp,
?string $host,
?string $scheme
): URI {
$baseURL = $this->normalizeBaseURL($configApp);

$uri = new URI($baseURL);

// Update scheme
if ($scheme !== null) {
$uri->setScheme($scheme);
} elseif ($configApp->forceGlobalSecureRequests) {
$uri->setScheme('https');
}

// Update host
if ($host !== null) {
$uri->setHost($host);
}

return $uri;
}

private function getIndexPageRoutePath(string $routePath): string
{
// Remove starting slash unless it is `/`.
if ($routePath !== '' && $routePath[0] === '/' && $routePath !== '/') {
$routePath = ltrim($routePath, '/');
}

// Check for an index page
$indexPage = '';
if ($this->indexPage !== '') {
$indexPage = $this->indexPage;

// Check if we need a separator
if ($routePath !== '' && $routePath[0] !== '/' && $routePath[0] !== '?') {
$indexPage .= '/';
}
}

$indexPageRoutePath = $indexPage . $routePath;

if ($indexPageRoutePath === '/') {
$indexPageRoutePath = '';
}

return $indexPageRoutePath;
}

private function normalizeBaseURL(App $configApp): string
{
// It's possible the user forgot a trailing slash on their
// baseURL, so let's help them out.
$baseURL = rtrim($configApp->baseURL, '/ ') . '/';

// Validate baseURL
if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) {
throw new ConfigException(
'Config\App::$baseURL is invalid.'
);
}

return $baseURL;
}

/**
* Sets basePathWithoutIndexPage and baseSegments.
*/
private function setBasePath(): void
{
$this->basePathWithoutIndexPage = $this->baseURL->getPath();

$this->baseSegments = $this->convertToSegments($this->basePathWithoutIndexPage);

if ($this->indexPage) {
$this->baseSegments[] = $this->indexPage;
}
}

/**
* @deprecated
*/
public function setBaseURL(string $baseURL): void
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* @deprecated
*/
public function setURI(?string $uri = null)
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* Returns the baseURL.
*
* @interal
*/
public function getBaseURL(): string
{
return (string) $this->baseURL;
}

/**
* Returns the URI path relative to baseURL.
*
* @return string The Route path.
*/
public function getRoutePath(): string
{
return $this->routePath;
}

/**
* Formats the URI as a string.
*/
public function __toString(): string
{
return static::createURIString(
$this->getScheme(),
$this->getAuthority(),
$this->getPath(),
$this->getQuery(),
$this->getFragment()
);
}

/**
* Sets the route path (and segments).
*
* @return $this
*/
public function setPath(string $path)
{
$this->setRoutePath($path);

return $this;
}

/**
* Sets the route path (and segments).
*/
private function setRoutePath(string $routePath): void
{
$routePath = $this->filterPath($routePath);

$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

$this->path = $this->basePathWithoutIndexPage . $indexPageRoutePath;

$this->routePath = ltrim($routePath, '/');

$this->segments = $this->convertToSegments($this->routePath);
}

/**
* Converts path to segments
*/
private function convertToSegments(string $path): array
{
$tempPath = trim($path, '/');

return ($tempPath === '') ? [] : explode('/', $tempPath);
}

/**
* Sets the path portion of the URI based on segments.
*
* @return $this
*
* @deprecated This method will be private.
*/
public function refreshPath()
{
$allSegments = array_merge($this->baseSegments, $this->segments);
$this->path = '/' . $this->filterPath(implode('/', $allSegments));

if ($this->routePath === '/' && $this->path !== '/') {
$this->path .= '/';
}

$this->routePath = $this->filterPath(implode('/', $this->segments));

return $this;
}

/**
* Saves our parts from a parse_url() call.
*/
protected function applyParts(array $parts)
{
if (! empty($parts['host'])) {
$this->host = $parts['host'];
}
if (! empty($parts['user'])) {
$this->user = $parts['user'];
}
if (isset($parts['path']) && $parts['path'] !== '') {
$this->path = $this->filterPath($parts['path']);
}
if (! empty($parts['query'])) {
$this->setQuery($parts['query']);
}
if (! empty($parts['fragment'])) {
$this->fragment = $parts['fragment'];
}

// Scheme
if (isset($parts['scheme'])) {
$this->setScheme(rtrim($parts['scheme'], ':/'));
} else {
$this->setScheme('http');
}

// Port
if (isset($parts['port']) && $parts['port'] !== null) {
// Valid port numbers are enforced by earlier parse_url() or setPort()
$this->port = $parts['port'];
}

if (isset($parts['pass'])) {
$this->password = $parts['pass'];
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ac527d
feat: add SiteURI class
kenjis Feb 14, 2023
7eec7bb
chore: add system/HTTP/SiteURI.php
kenjis Feb 14, 2023
5593f8a
docs: add @deprecated
kenjis Feb 14, 2023
8a66de5
test: fix incorrect test
kenjis Feb 14, 2023
406128f
refactor: fix typo in method name
kenjis Feb 14, 2023
7d91c0e
docs: update PHPDocs
kenjis Feb 14, 2023
b728948
docs: add @deprecated
kenjis Feb 15, 2023
9af4994
feat: add validation for baseURL
kenjis Feb 15, 2023
5fa5305
feat: add getBaseURL() implementation
kenjis Feb 15, 2023
c8d5eef
feat: add param $relativePath to constructor
kenjis Feb 16, 2023
b30035b
feat: add param $host to constructor
kenjis Feb 16, 2023
9d82338
feat: add param $scheme to constructor
kenjis Feb 17, 2023
b231d1c
style: break long line
kenjis Feb 17, 2023
4932572
fix: remove unneeded methods
kenjis Feb 17, 2023
c3628d1
fix: change default values to null
kenjis Feb 17, 2023
57afaf9
fix: handling fragment
kenjis Feb 17, 2023
246eea2
fix: host in baseURL may be wrong
kenjis Feb 18, 2023
08bbc18
fix: disable setBaseURL() method
kenjis Feb 18, 2023
ab6da61
test: add assertions
kenjis Feb 20, 2023
82a567d
refactor: SiteURI::__construct()
kenjis Feb 20, 2023
ccfd79d
test: add test cases for path starting with slash
kenjis Feb 20, 2023
e733417
fix: change behavior
kenjis Feb 21, 2023
33fc028
test: use dataProviders
kenjis Feb 21, 2023
ba35e57
docs: update comments
kenjis Feb 22, 2023
fe754f3
fix: change behavior
kenjis Feb 23, 2023
3f4a9fd
fix: remove $allowedHostnames check
kenjis Feb 23, 2023
ecf96eb
test: add test cases
kenjis Feb 23, 2023
5193414
refactor: remove unneeded override
kenjis Feb 24, 2023
b24c01c
docs: fix typo in comments
kenjis Jul 10, 2023
3e349e1
docs: make URI::setSilent() deprecated
kenjis Jul 10, 2023
e529291
feat: add URI::withScheme() and deprecate URI::setScheme()
kenjis Jul 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test-phpcpd.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,4 +55,5 @@ jobs:
--exclude system/Database/OCI8/Builder.php
--exclude system/Database/Postgre/Builder.php
--exclude system/Debug/Exceptions.php
--exclude system/HTTP/SiteURI.php
-- app/ public/ system/
366 changes: 366 additions & 0 deletions system/HTTP/SiteURI.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\HTTP;

use BadMethodCallException;
use CodeIgniter\Exceptions\ConfigException;
use CodeIgniter\HTTP\Exceptions\HTTPException;
use Config\App;

/**
* URI for the application site
*/
class SiteURI extends URI
{
/**
* The current baseURL.
*/
private URI $baseURL;

/**
* The path part of baseURL.
*
* The baseURL "http://example.com/" → '/'
* The baseURL "http://localhost:8888/ci431/public/" → '/ci431/public/'
*/
private string $basePathWithoutIndexPage;

/**
* The Index File.
*/
private string $indexPage;

/**
* List of URI segments in baseURL and indexPage.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $baseSegments = [
* 0 => 'ci431',
* 1 => 'public',
* 2 => 'index.php',
* ];
*/
private array $baseSegments;

/**
* List of URI segments after indexPage.
*
* The word "URI Segments" originally means only the URI path part relative
* to the baseURL.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $segments = [
* 0 => 'test',
* ];
*
* @var array
*
* @deprecated This property will be private.
Comment thread
MGatner marked this conversation as resolved.
*/
protected $segments;

/**
* URI path relative to baseURL.
*
* If the baseURL contains sub folders, this value will be different from
* the current URI path.
*
* This value never starts with '/'.
*/
private string $routePath;

/**
* @param string $relativePath URI path relative to baseURL. May include
* queries or fragments.
* @param string|null $host Optional current hostname.
* @param string|null $scheme Optional scheme. 'http' or 'https'.
* @phpstan-param 'http'|'https'|null $scheme
*/
public function __construct(
App $configApp,
string $relativePath = '',
?string $host = null,
?string $scheme = null
) {
$this->indexPage = $configApp->indexPage;

$this->baseURL = $this->determineBaseURL($configApp, $host, $scheme);

$this->setBasePath();

// Fix routePath, query, fragment
[$routePath, $query, $fragment] = $this->parseRelativePath($relativePath);

// Fix indexPage and routePath
$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

// Fix the current URI
$uri = $this->baseURL . $indexPageRoutePath;

// applyParts
$parts = parse_url($uri);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($uri);
}
$parts['query'] = $query;
$parts['fragment'] = $fragment;
$this->applyParts($parts);

$this->setRoutePath($routePath);
}

private function parseRelativePath(string $relativePath): array
{
$parts = parse_url('http://dummy/' . $relativePath);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($relativePath);
}

$routePath = $relativePath === '/' ? '/' : ltrim($parts['path'], '/');

$query = $parts['query'] ?? '';
$fragment = $parts['fragment'] ?? '';

return [$routePath, $query, $fragment];
}

private function determineBaseURL(
App $configApp,
?string $host,
?string $scheme
): URI {
$baseURL = $this->normalizeBaseURL($configApp);

$uri = new URI($baseURL);

// Update scheme
if ($scheme !== null) {
$uri->setScheme($scheme);
} elseif ($configApp->forceGlobalSecureRequests) {
$uri->setScheme('https');
}

// Update host
if ($host !== null) {
$uri->setHost($host);
}

return $uri;
}

private function getIndexPageRoutePath(string $routePath): string
{
// Remove starting slash unless it is `/`.
if ($routePath !== '' && $routePath[0] === '/' && $routePath !== '/') {
$routePath = ltrim($routePath, '/');
}

// Check for an index page
$indexPage = '';
if ($this->indexPage !== '') {
$indexPage = $this->indexPage;

// Check if we need a separator
if ($routePath !== '' && $routePath[0] !== '/' && $routePath[0] !== '?') {
$indexPage .= '/';
}
}

$indexPageRoutePath = $indexPage . $routePath;

if ($indexPageRoutePath === '/') {
$indexPageRoutePath = '';
}

return $indexPageRoutePath;
}

private function normalizeBaseURL(App $configApp): string
{
// It's possible the user forgot a trailing slash on their
// baseURL, so let's help them out.
$baseURL = rtrim($configApp->baseURL, '/ ') . '/';

// Validate baseURL
if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) {
throw new ConfigException(
'Config\App::$baseURL is invalid.'
);
}

return $baseURL;
}

/**
* Sets basePathWithoutIndexPage and baseSegments.
*/
private function setBasePath(): void
{
$this->basePathWithoutIndexPage = $this->baseURL->getPath();

$this->baseSegments = $this->convertToSegments($this->basePathWithoutIndexPage);

if ($this->indexPage) {
$this->baseSegments[] = $this->indexPage;
}
}

/**
* @deprecated
*/
public function setBaseURL(string $baseURL): void
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* @deprecated
*/
public function setURI(?string $uri = null)
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* Returns the baseURL.
*
* @interal
*/
public function getBaseURL(): string
{
return (string) $this->baseURL;
}

/**
* Returns the URI path relative to baseURL.
*
* @return string The Route path.
*/
public function getRoutePath(): string
{
return $this->routePath;
}

/**
* Formats the URI as a string.
*/
public function __toString(): string
{
return static::createURIString(
$this->getScheme(),
$this->getAuthority(),
$this->getPath(),
$this->getQuery(),
$this->getFragment()
);
}

/**
* Sets the route path (and segments).
*
* @return $this
*/
public function setPath(string $path)
{
$this->setRoutePath($path);

return $this;
}

/**
* Sets the route path (and segments).
*/
private function setRoutePath(string $routePath): void
{
$routePath = $this->filterPath($routePath);

$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

$this->path = $this->basePathWithoutIndexPage . $indexPageRoutePath;

$this->routePath = ltrim($routePath, '/');

$this->segments = $this->convertToSegments($this->routePath);
}

/**
* Converts path to segments
*/
private function convertToSegments(string $path): array
{
$tempPath = trim($path, '/');

return ($tempPath === '') ? [] : explode('/', $tempPath);
}

/**
* Sets the path portion of the URI based on segments.
*
* @return $this
*
* @deprecated This method will be private.
*/
public function refreshPath()
{
$allSegments = array_merge($this->baseSegments, $this->segments);
$this->path = '/' . $this->filterPath(implode('/', $allSegments));

if ($this->routePath === '/' && $this->path !== '/') {
$this->path .= '/';
}

$this->routePath = $this->filterPath(implode('/', $this->segments));

return $this;
}

/**
* Saves our parts from a parse_url() call.
*/
protected function applyParts(array $parts)
{
if (! empty($parts['host'])) {
$this->host = $parts['host'];
}
if (! empty($parts['user'])) {
$this->user = $parts['user'];
}
if (isset($parts['path']) && $parts['path'] !== '') {
$this->path = $this->filterPath($parts['path']);
}
if (! empty($parts['query'])) {
$this->setQuery($parts['query']);
}
if (! empty($parts['fragment'])) {
$this->fragment = $parts['fragment'];
}

// Scheme
if (isset($parts['scheme'])) {
$this->setScheme(rtrim($parts['scheme'], ':/'));
} else {
$this->setScheme('http');
}

// Port
if (isset($parts['port']) && $parts['port'] !== null) {
// Valid port numbers are enforced by earlier parse_url() or setPort()
$this->port = $parts['port'];
}

if (isset($parts['pass'])) {
$this->password = $parts['pass'];
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ac527d
feat: add SiteURI class
kenjis Feb 14, 2023
7eec7bb
chore: add system/HTTP/SiteURI.php
kenjis Feb 14, 2023
5593f8a
docs: add @deprecated
kenjis Feb 14, 2023
8a66de5
test: fix incorrect test
kenjis Feb 14, 2023
406128f
refactor: fix typo in method name
kenjis Feb 14, 2023
7d91c0e
docs: update PHPDocs
kenjis Feb 14, 2023
b728948
docs: add @deprecated
kenjis Feb 15, 2023
9af4994
feat: add validation for baseURL
kenjis Feb 15, 2023
5fa5305
feat: add getBaseURL() implementation
kenjis Feb 15, 2023
c8d5eef
feat: add param $relativePath to constructor
kenjis Feb 16, 2023
b30035b
feat: add param $host to constructor
kenjis Feb 16, 2023
9d82338
feat: add param $scheme to constructor
kenjis Feb 17, 2023
b231d1c
style: break long line
kenjis Feb 17, 2023
4932572
fix: remove unneeded methods
kenjis Feb 17, 2023
c3628d1
fix: change default values to null
kenjis Feb 17, 2023
57afaf9
fix: handling fragment
kenjis Feb 17, 2023
246eea2
fix: host in baseURL may be wrong
kenjis Feb 18, 2023
08bbc18
fix: disable setBaseURL() method
kenjis Feb 18, 2023
ab6da61
test: add assertions
kenjis Feb 20, 2023
82a567d
refactor: SiteURI::__construct()
kenjis Feb 20, 2023
ccfd79d
test: add test cases for path starting with slash
kenjis Feb 20, 2023
e733417
fix: change behavior
kenjis Feb 21, 2023
33fc028
test: use dataProviders
kenjis Feb 21, 2023
ba35e57
docs: update comments
kenjis Feb 22, 2023
fe754f3
fix: change behavior
kenjis Feb 23, 2023
3f4a9fd
fix: remove $allowedHostnames check
kenjis Feb 23, 2023
ecf96eb
test: add test cases
kenjis Feb 23, 2023
5193414
refactor: remove unneeded override
kenjis Feb 24, 2023
b24c01c
docs: fix typo in comments
kenjis Jul 10, 2023
3e349e1
docs: make URI::setSilent() deprecated
kenjis Jul 10, 2023
e529291
feat: add URI::withScheme() and deprecate URI::setScheme()
kenjis Jul 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test-phpcpd.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,4 +55,5 @@ jobs:
--exclude system/Database/OCI8/Builder.php
--exclude system/Database/Postgre/Builder.php
--exclude system/Debug/Exceptions.php
--exclude system/HTTP/SiteURI.php
-- app/ public/ system/
366 changes: 366 additions & 0 deletions system/HTTP/SiteURI.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\HTTP;

use BadMethodCallException;
use CodeIgniter\Exceptions\ConfigException;
use CodeIgniter\HTTP\Exceptions\HTTPException;
use Config\App;

/**
* URI for the application site
*/
class SiteURI extends URI
{
/**
* The current baseURL.
*/
private URI $baseURL;

/**
* The path part of baseURL.
*
* The baseURL "http://example.com/" → '/'
* The baseURL "http://localhost:8888/ci431/public/" → '/ci431/public/'
*/
private string $basePathWithoutIndexPage;

/**
* The Index File.
*/
private string $indexPage;

/**
* List of URI segments in baseURL and indexPage.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $baseSegments = [
* 0 => 'ci431',
* 1 => 'public',
* 2 => 'index.php',
* ];
*/
private array $baseSegments;

/**
* List of URI segments after indexPage.
*
* The word "URI Segments" originally means only the URI path part relative
* to the baseURL.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $segments = [
* 0 => 'test',
* ];
*
* @var array
*
* @deprecated This property will be private.
Comment thread
MGatner marked this conversation as resolved.
*/
protected $segments;

/**
* URI path relative to baseURL.
*
* If the baseURL contains sub folders, this value will be different from
* the current URI path.
*
* This value never starts with '/'.
*/
private string $routePath;

/**
* @param string $relativePath URI path relative to baseURL. May include
* queries or fragments.
* @param string|null $host Optional current hostname.
* @param string|null $scheme Optional scheme. 'http' or 'https'.
* @phpstan-param 'http'|'https'|null $scheme
*/
public function __construct(
App $configApp,
string $relativePath = '',
?string $host = null,
?string $scheme = null
) {
$this->indexPage = $configApp->indexPage;

$this->baseURL = $this->determineBaseURL($configApp, $host, $scheme);

$this->setBasePath();

// Fix routePath, query, fragment
[$routePath, $query, $fragment] = $this->parseRelativePath($relativePath);

// Fix indexPage and routePath
$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

// Fix the current URI
$uri = $this->baseURL . $indexPageRoutePath;

// applyParts
$parts = parse_url($uri);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($uri);
}
$parts['query'] = $query;
$parts['fragment'] = $fragment;
$this->applyParts($parts);

$this->setRoutePath($routePath);
}

private function parseRelativePath(string $relativePath): array
{
$parts = parse_url('http://dummy/' . $relativePath);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($relativePath);
}

$routePath = $relativePath === '/' ? '/' : ltrim($parts['path'], '/');

$query = $parts['query'] ?? '';
$fragment = $parts['fragment'] ?? '';

return [$routePath, $query, $fragment];
}

private function determineBaseURL(
App $configApp,
?string $host,
?string $scheme
): URI {
$baseURL = $this->normalizeBaseURL($configApp);

$uri = new URI($baseURL);

// Update scheme
if ($scheme !== null) {
$uri->setScheme($scheme);
} elseif ($configApp->forceGlobalSecureRequests) {
$uri->setScheme('https');
}

// Update host
if ($host !== null) {
$uri->setHost($host);
}

return $uri;
}

private function getIndexPageRoutePath(string $routePath): string
{
// Remove starting slash unless it is `/`.
if ($routePath !== '' && $routePath[0] === '/' && $routePath !== '/') {
$routePath = ltrim($routePath, '/');
}

// Check for an index page
$indexPage = '';
if ($this->indexPage !== '') {
$indexPage = $this->indexPage;

// Check if we need a separator
if ($routePath !== '' && $routePath[0] !== '/' && $routePath[0] !== '?') {
$indexPage .= '/';
}
}

$indexPageRoutePath = $indexPage . $routePath;

if ($indexPageRoutePath === '/') {
$indexPageRoutePath = '';
}

return $indexPageRoutePath;
}

private function normalizeBaseURL(App $configApp): string
{
// It's possible the user forgot a trailing slash on their
// baseURL, so let's help them out.
$baseURL = rtrim($configApp->baseURL, '/ ') . '/';

// Validate baseURL
if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) {
throw new ConfigException(
'Config\App::$baseURL is invalid.'
);
}

return $baseURL;
}

/**
* Sets basePathWithoutIndexPage and baseSegments.
*/
private function setBasePath(): void
{
$this->basePathWithoutIndexPage = $this->baseURL->getPath();

$this->baseSegments = $this->convertToSegments($this->basePathWithoutIndexPage);

if ($this->indexPage) {
$this->baseSegments[] = $this->indexPage;
}
}

/**
* @deprecated
*/
public function setBaseURL(string $baseURL): void
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* @deprecated
*/
public function setURI(?string $uri = null)
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* Returns the baseURL.
*
* @interal
*/
public function getBaseURL(): string
{
return (string) $this->baseURL;
}

/**
* Returns the URI path relative to baseURL.
*
* @return string The Route path.
*/
public function getRoutePath(): string
{
return $this->routePath;
}

/**
* Formats the URI as a string.
*/
public function __toString(): string
{
return static::createURIString(
$this->getScheme(),
$this->getAuthority(),
$this->getPath(),
$this->getQuery(),
$this->getFragment()
);
}

/**
* Sets the route path (and segments).
*
* @return $this
*/
public function setPath(string $path)
{
$this->setRoutePath($path);

return $this;
}

/**
* Sets the route path (and segments).
*/
private function setRoutePath(string $routePath): void
{
$routePath = $this->filterPath($routePath);

$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

$this->path = $this->basePathWithoutIndexPage . $indexPageRoutePath;

$this->routePath = ltrim($routePath, '/');

$this->segments = $this->convertToSegments($this->routePath);
}

/**
* Converts path to segments
*/
private function convertToSegments(string $path): array
{
$tempPath = trim($path, '/');

return ($tempPath === '') ? [] : explode('/', $tempPath);
}

/**
* Sets the path portion of the URI based on segments.
*
* @return $this
*
* @deprecated This method will be private.
*/
public function refreshPath()
{
$allSegments = array_merge($this->baseSegments, $this->segments);
$this->path = '/' . $this->filterPath(implode('/', $allSegments));

if ($this->routePath === '/' && $this->path !== '/') {
$this->path .= '/';
}

$this->routePath = $this->filterPath(implode('/', $this->segments));

return $this;
}

/**
* Saves our parts from a parse_url() call.
*/
protected function applyParts(array $parts)
{
if (! empty($parts['host'])) {
$this->host = $parts['host'];
}
if (! empty($parts['user'])) {
$this->user = $parts['user'];
}
if (isset($parts['path']) && $parts['path'] !== '') {
$this->path = $this->filterPath($parts['path']);
}
if (! empty($parts['query'])) {
$this->setQuery($parts['query']);
}
if (! empty($parts['fragment'])) {
$this->fragment = $parts['fragment'];
}

// Scheme
if (isset($parts['scheme'])) {
$this->setScheme(rtrim($parts['scheme'], ':/'));
} else {
$this->setScheme('http');
}

// Port
if (isset($parts['port']) && $parts['port'] !== null) {
// Valid port numbers are enforced by earlier parse_url() or setPort()
$this->port = $parts['port'];
}

if (isset($parts['pass'])) {
$this->password = $parts['pass'];
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ac527d
feat: add SiteURI class
kenjis Feb 14, 2023
7eec7bb
chore: add system/HTTP/SiteURI.php
kenjis Feb 14, 2023
5593f8a
docs: add @deprecated
kenjis Feb 14, 2023
8a66de5
test: fix incorrect test
kenjis Feb 14, 2023
406128f
refactor: fix typo in method name
kenjis Feb 14, 2023
7d91c0e
docs: update PHPDocs
kenjis Feb 14, 2023
b728948
docs: add @deprecated
kenjis Feb 15, 2023
9af4994
feat: add validation for baseURL
kenjis Feb 15, 2023
5fa5305
feat: add getBaseURL() implementation
kenjis Feb 15, 2023
c8d5eef
feat: add param $relativePath to constructor
kenjis Feb 16, 2023
b30035b
feat: add param $host to constructor
kenjis Feb 16, 2023
9d82338
feat: add param $scheme to constructor
kenjis Feb 17, 2023
b231d1c
style: break long line
kenjis Feb 17, 2023
4932572
fix: remove unneeded methods
kenjis Feb 17, 2023
c3628d1
fix: change default values to null
kenjis Feb 17, 2023
57afaf9
fix: handling fragment
kenjis Feb 17, 2023
246eea2
fix: host in baseURL may be wrong
kenjis Feb 18, 2023
08bbc18
fix: disable setBaseURL() method
kenjis Feb 18, 2023
ab6da61
test: add assertions
kenjis Feb 20, 2023
82a567d
refactor: SiteURI::__construct()
kenjis Feb 20, 2023
ccfd79d
test: add test cases for path starting with slash
kenjis Feb 20, 2023
e733417
fix: change behavior
kenjis Feb 21, 2023
33fc028
test: use dataProviders
kenjis Feb 21, 2023
ba35e57
docs: update comments
kenjis Feb 22, 2023
fe754f3
fix: change behavior
kenjis Feb 23, 2023
3f4a9fd
fix: remove $allowedHostnames check
kenjis Feb 23, 2023
ecf96eb
test: add test cases
kenjis Feb 23, 2023
5193414
refactor: remove unneeded override
kenjis Feb 24, 2023
b24c01c
docs: fix typo in comments
kenjis Jul 10, 2023
3e349e1
docs: make URI::setSilent() deprecated
kenjis Jul 10, 2023
e529291
feat: add URI::withScheme() and deprecate URI::setScheme()
kenjis Jul 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test-phpcpd.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,4 +55,5 @@ jobs:
--exclude system/Database/OCI8/Builder.php
--exclude system/Database/Postgre/Builder.php
--exclude system/Debug/Exceptions.php
--exclude system/HTTP/SiteURI.php
-- app/ public/ system/
366 changes: 366 additions & 0 deletions system/HTTP/SiteURI.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\HTTP;

use BadMethodCallException;
use CodeIgniter\Exceptions\ConfigException;
use CodeIgniter\HTTP\Exceptions\HTTPException;
use Config\App;

/**
* URI for the application site
*/
class SiteURI extends URI
{
/**
* The current baseURL.
*/
private URI $baseURL;

/**
* The path part of baseURL.
*
* The baseURL "http://example.com/" → '/'
* The baseURL "http://localhost:8888/ci431/public/" → '/ci431/public/'
*/
private string $basePathWithoutIndexPage;

/**
* The Index File.
*/
private string $indexPage;

/**
* List of URI segments in baseURL and indexPage.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $baseSegments = [
* 0 => 'ci431',
* 1 => 'public',
* 2 => 'index.php',
* ];
*/
private array $baseSegments;

/**
* List of URI segments after indexPage.
*
* The word "URI Segments" originally means only the URI path part relative
* to the baseURL.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $segments = [
* 0 => 'test',
* ];
*
* @var array
*
* @deprecated This property will be private.
Comment thread
MGatner marked this conversation as resolved.
*/
protected $segments;

/**
* URI path relative to baseURL.
*
* If the baseURL contains sub folders, this value will be different from
* the current URI path.
*
* This value never starts with '/'.
*/
private string $routePath;

/**
* @param string $relativePath URI path relative to baseURL. May include
* queries or fragments.
* @param string|null $host Optional current hostname.
* @param string|null $scheme Optional scheme. 'http' or 'https'.
* @phpstan-param 'http'|'https'|null $scheme
*/
public function __construct(
App $configApp,
string $relativePath = '',
?string $host = null,
?string $scheme = null
) {
$this->indexPage = $configApp->indexPage;

$this->baseURL = $this->determineBaseURL($configApp, $host, $scheme);

$this->setBasePath();

// Fix routePath, query, fragment
[$routePath, $query, $fragment] = $this->parseRelativePath($relativePath);

// Fix indexPage and routePath
$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

// Fix the current URI
$uri = $this->baseURL . $indexPageRoutePath;

// applyParts
$parts = parse_url($uri);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($uri);
}
$parts['query'] = $query;
$parts['fragment'] = $fragment;
$this->applyParts($parts);

$this->setRoutePath($routePath);
}

private function parseRelativePath(string $relativePath): array
{
$parts = parse_url('http://dummy/' . $relativePath);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($relativePath);
}

$routePath = $relativePath === '/' ? '/' : ltrim($parts['path'], '/');

$query = $parts['query'] ?? '';
$fragment = $parts['fragment'] ?? '';

return [$routePath, $query, $fragment];
}

private function determineBaseURL(
App $configApp,
?string $host,
?string $scheme
): URI {
$baseURL = $this->normalizeBaseURL($configApp);

$uri = new URI($baseURL);

// Update scheme
if ($scheme !== null) {
$uri->setScheme($scheme);
} elseif ($configApp->forceGlobalSecureRequests) {
$uri->setScheme('https');
}

// Update host
if ($host !== null) {
$uri->setHost($host);
}

return $uri;
}

private function getIndexPageRoutePath(string $routePath): string
{
// Remove starting slash unless it is `/`.
if ($routePath !== '' && $routePath[0] === '/' && $routePath !== '/') {
$routePath = ltrim($routePath, '/');
}

// Check for an index page
$indexPage = '';
if ($this->indexPage !== '') {
$indexPage = $this->indexPage;

// Check if we need a separator
if ($routePath !== '' && $routePath[0] !== '/' && $routePath[0] !== '?') {
$indexPage .= '/';
}
}

$indexPageRoutePath = $indexPage . $routePath;

if ($indexPageRoutePath === '/') {
$indexPageRoutePath = '';
}

return $indexPageRoutePath;
}

private function normalizeBaseURL(App $configApp): string
{
// It's possible the user forgot a trailing slash on their
// baseURL, so let's help them out.
$baseURL = rtrim($configApp->baseURL, '/ ') . '/';

// Validate baseURL
if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) {
throw new ConfigException(
'Config\App::$baseURL is invalid.'
);
}

return $baseURL;
}

/**
* Sets basePathWithoutIndexPage and baseSegments.
*/
private function setBasePath(): void
{
$this->basePathWithoutIndexPage = $this->baseURL->getPath();

$this->baseSegments = $this->convertToSegments($this->basePathWithoutIndexPage);

if ($this->indexPage) {
$this->baseSegments[] = $this->indexPage;
}
}

/**
* @deprecated
*/
public function setBaseURL(string $baseURL): void
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* @deprecated
*/
public function setURI(?string $uri = null)
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* Returns the baseURL.
*
* @interal
*/
public function getBaseURL(): string
{
return (string) $this->baseURL;
}

/**
* Returns the URI path relative to baseURL.
*
* @return string The Route path.
*/
public function getRoutePath(): string
{
return $this->routePath;
}

/**
* Formats the URI as a string.
*/
public function __toString(): string
{
return static::createURIString(
$this->getScheme(),
$this->getAuthority(),
$this->getPath(),
$this->getQuery(),
$this->getFragment()
);
}

/**
* Sets the route path (and segments).
*
* @return $this
*/
public function setPath(string $path)
{
$this->setRoutePath($path);

return $this;
}

/**
* Sets the route path (and segments).
*/
private function setRoutePath(string $routePath): void
{
$routePath = $this->filterPath($routePath);

$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

$this->path = $this->basePathWithoutIndexPage . $indexPageRoutePath;

$this->routePath = ltrim($routePath, '/');

$this->segments = $this->convertToSegments($this->routePath);
}

/**
* Converts path to segments
*/
private function convertToSegments(string $path): array
{
$tempPath = trim($path, '/');

return ($tempPath === '') ? [] : explode('/', $tempPath);
}

/**
* Sets the path portion of the URI based on segments.
*
* @return $this
*
* @deprecated This method will be private.
*/
public function refreshPath()
{
$allSegments = array_merge($this->baseSegments, $this->segments);
$this->path = '/' . $this->filterPath(implode('/', $allSegments));

if ($this->routePath === '/' && $this->path !== '/') {
$this->path .= '/';
}

$this->routePath = $this->filterPath(implode('/', $this->segments));

return $this;
}

/**
* Saves our parts from a parse_url() call.
*/
protected function applyParts(array $parts)
{
if (! empty($parts['host'])) {
$this->host = $parts['host'];
}
if (! empty($parts['user'])) {
$this->user = $parts['user'];
}
if (isset($parts['path']) && $parts['path'] !== '') {
$this->path = $this->filterPath($parts['path']);
}
if (! empty($parts['query'])) {
$this->setQuery($parts['query']);
}
if (! empty($parts['fragment'])) {
$this->fragment = $parts['fragment'];
}

// Scheme
if (isset($parts['scheme'])) {
$this->setScheme(rtrim($parts['scheme'], ':/'));
} else {
$this->setScheme('http');
}

// Port
if (isset($parts['port']) && $parts['port'] !== null) {
// Valid port numbers are enforced by earlier parse_url() or setPort()
$this->port = $parts['port'];
}

if (isset($parts['pass'])) {
$this->password = $parts['pass'];
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ac527d
feat: add SiteURI class
kenjis Feb 14, 2023
7eec7bb
chore: add system/HTTP/SiteURI.php
kenjis Feb 14, 2023
5593f8a
docs: add @deprecated
kenjis Feb 14, 2023
8a66de5
test: fix incorrect test
kenjis Feb 14, 2023
406128f
refactor: fix typo in method name
kenjis Feb 14, 2023
7d91c0e
docs: update PHPDocs
kenjis Feb 14, 2023
b728948
docs: add @deprecated
kenjis Feb 15, 2023
9af4994
feat: add validation for baseURL
kenjis Feb 15, 2023
5fa5305
feat: add getBaseURL() implementation
kenjis Feb 15, 2023
c8d5eef
feat: add param $relativePath to constructor
kenjis Feb 16, 2023
b30035b
feat: add param $host to constructor
kenjis Feb 16, 2023
9d82338
feat: add param $scheme to constructor
kenjis Feb 17, 2023
b231d1c
style: break long line
kenjis Feb 17, 2023
4932572
fix: remove unneeded methods
kenjis Feb 17, 2023
c3628d1
fix: change default values to null
kenjis Feb 17, 2023
57afaf9
fix: handling fragment
kenjis Feb 17, 2023
246eea2
fix: host in baseURL may be wrong
kenjis Feb 18, 2023
08bbc18
fix: disable setBaseURL() method
kenjis Feb 18, 2023
ab6da61
test: add assertions
kenjis Feb 20, 2023
82a567d
refactor: SiteURI::__construct()
kenjis Feb 20, 2023
ccfd79d
test: add test cases for path starting with slash
kenjis Feb 20, 2023
e733417
fix: change behavior
kenjis Feb 21, 2023
33fc028
test: use dataProviders
kenjis Feb 21, 2023
ba35e57
docs: update comments
kenjis Feb 22, 2023
fe754f3
fix: change behavior
kenjis Feb 23, 2023
3f4a9fd
fix: remove $allowedHostnames check
kenjis Feb 23, 2023
ecf96eb
test: add test cases
kenjis Feb 23, 2023
5193414
refactor: remove unneeded override
kenjis Feb 24, 2023
b24c01c
docs: fix typo in comments
kenjis Jul 10, 2023
3e349e1
docs: make URI::setSilent() deprecated
kenjis Jul 10, 2023
e529291
feat: add URI::withScheme() and deprecate URI::setScheme()
kenjis Jul 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test-phpcpd.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,4 +55,5 @@ jobs:
--exclude system/Database/OCI8/Builder.php
--exclude system/Database/Postgre/Builder.php
--exclude system/Debug/Exceptions.php
--exclude system/HTTP/SiteURI.php
-- app/ public/ system/
366 changes: 366 additions & 0 deletions system/HTTP/SiteURI.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\HTTP;

use BadMethodCallException;
use CodeIgniter\Exceptions\ConfigException;
use CodeIgniter\HTTP\Exceptions\HTTPException;
use Config\App;

/**
* URI for the application site
*/
class SiteURI extends URI
{
/**
* The current baseURL.
*/
private URI $baseURL;

/**
* The path part of baseURL.
*
* The baseURL "http://example.com/" → '/'
* The baseURL "http://localhost:8888/ci431/public/" → '/ci431/public/'
*/
private string $basePathWithoutIndexPage;

/**
* The Index File.
*/
private string $indexPage;

/**
* List of URI segments in baseURL and indexPage.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $baseSegments = [
* 0 => 'ci431',
* 1 => 'public',
* 2 => 'index.php',
* ];
*/
private array $baseSegments;

/**
* List of URI segments after indexPage.
*
* The word "URI Segments" originally means only the URI path part relative
* to the baseURL.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $segments = [
* 0 => 'test',
* ];
*
* @var array
*
* @deprecated This property will be private.
Comment thread
MGatner marked this conversation as resolved.
*/
protected $segments;

/**
* URI path relative to baseURL.
*
* If the baseURL contains sub folders, this value will be different from
* the current URI path.
*
* This value never starts with '/'.
*/
private string $routePath;

/**
* @param string $relativePath URI path relative to baseURL. May include
* queries or fragments.
* @param string|null $host Optional current hostname.
* @param string|null $scheme Optional scheme. 'http' or 'https'.
* @phpstan-param 'http'|'https'|null $scheme
*/
public function __construct(
App $configApp,
string $relativePath = '',
?string $host = null,
?string $scheme = null
) {
$this->indexPage = $configApp->indexPage;

$this->baseURL = $this->determineBaseURL($configApp, $host, $scheme);

$this->setBasePath();

// Fix routePath, query, fragment
[$routePath, $query, $fragment] = $this->parseRelativePath($relativePath);

// Fix indexPage and routePath
$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

// Fix the current URI
$uri = $this->baseURL . $indexPageRoutePath;

// applyParts
$parts = parse_url($uri);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($uri);
}
$parts['query'] = $query;
$parts['fragment'] = $fragment;
$this->applyParts($parts);

$this->setRoutePath($routePath);
}

private function parseRelativePath(string $relativePath): array
{
$parts = parse_url('http://dummy/' . $relativePath);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($relativePath);
}

$routePath = $relativePath === '/' ? '/' : ltrim($parts['path'], '/');

$query = $parts['query'] ?? '';
$fragment = $parts['fragment'] ?? '';

return [$routePath, $query, $fragment];
}

private function determineBaseURL(
App $configApp,
?string $host,
?string $scheme
): URI {
$baseURL = $this->normalizeBaseURL($configApp);

$uri = new URI($baseURL);

// Update scheme
if ($scheme !== null) {
$uri->setScheme($scheme);
} elseif ($configApp->forceGlobalSecureRequests) {
$uri->setScheme('https');
}

// Update host
if ($host !== null) {
$uri->setHost($host);
}

return $uri;
}

private function getIndexPageRoutePath(string $routePath): string
{
// Remove starting slash unless it is `/`.
if ($routePath !== '' && $routePath[0] === '/' && $routePath !== '/') {
$routePath = ltrim($routePath, '/');
}

// Check for an index page
$indexPage = '';
if ($this->indexPage !== '') {
$indexPage = $this->indexPage;

// Check if we need a separator
if ($routePath !== '' && $routePath[0] !== '/' && $routePath[0] !== '?') {
$indexPage .= '/';
}
}

$indexPageRoutePath = $indexPage . $routePath;

if ($indexPageRoutePath === '/') {
$indexPageRoutePath = '';
}

return $indexPageRoutePath;
}

private function normalizeBaseURL(App $configApp): string
{
// It's possible the user forgot a trailing slash on their
// baseURL, so let's help them out.
$baseURL = rtrim($configApp->baseURL, '/ ') . '/';

// Validate baseURL
if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) {
throw new ConfigException(
'Config\App::$baseURL is invalid.'
);
}

return $baseURL;
}

/**
* Sets basePathWithoutIndexPage and baseSegments.
*/
private function setBasePath(): void
{
$this->basePathWithoutIndexPage = $this->baseURL->getPath();

$this->baseSegments = $this->convertToSegments($this->basePathWithoutIndexPage);

if ($this->indexPage) {
$this->baseSegments[] = $this->indexPage;
}
}

/**
* @deprecated
*/
public function setBaseURL(string $baseURL): void
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* @deprecated
*/
public function setURI(?string $uri = null)
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* Returns the baseURL.
*
* @interal
*/
public function getBaseURL(): string
{
return (string) $this->baseURL;
}

/**
* Returns the URI path relative to baseURL.
*
* @return string The Route path.
*/
public function getRoutePath(): string
{
return $this->routePath;
}

/**
* Formats the URI as a string.
*/
public function __toString(): string
{
return static::createURIString(
$this->getScheme(),
$this->getAuthority(),
$this->getPath(),
$this->getQuery(),
$this->getFragment()
);
}

/**
* Sets the route path (and segments).
*
* @return $this
*/
public function setPath(string $path)
{
$this->setRoutePath($path);

return $this;
}

/**
* Sets the route path (and segments).
*/
private function setRoutePath(string $routePath): void
{
$routePath = $this->filterPath($routePath);

$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

$this->path = $this->basePathWithoutIndexPage . $indexPageRoutePath;

$this->routePath = ltrim($routePath, '/');

$this->segments = $this->convertToSegments($this->routePath);
}

/**
* Converts path to segments
*/
private function convertToSegments(string $path): array
{
$tempPath = trim($path, '/');

return ($tempPath === '') ? [] : explode('/', $tempPath);
}

/**
* Sets the path portion of the URI based on segments.
*
* @return $this
*
* @deprecated This method will be private.
*/
public function refreshPath()
{
$allSegments = array_merge($this->baseSegments, $this->segments);
$this->path = '/' . $this->filterPath(implode('/', $allSegments));

if ($this->routePath === '/' && $this->path !== '/') {
$this->path .= '/';
}

$this->routePath = $this->filterPath(implode('/', $this->segments));

return $this;
}

/**
* Saves our parts from a parse_url() call.
*/
protected function applyParts(array $parts)
{
if (! empty($parts['host'])) {
$this->host = $parts['host'];
}
if (! empty($parts['user'])) {
$this->user = $parts['user'];
}
if (isset($parts['path']) && $parts['path'] !== '') {
$this->path = $this->filterPath($parts['path']);
}
if (! empty($parts['query'])) {
$this->setQuery($parts['query']);
}
if (! empty($parts['fragment'])) {
$this->fragment = $parts['fragment'];
}

// Scheme
if (isset($parts['scheme'])) {
$this->setScheme(rtrim($parts['scheme'], ':/'));
} else {
$this->setScheme('http');
}

// Port
if (isset($parts['port']) && $parts['port'] !== null) {
// Valid port numbers are enforced by earlier parse_url() or setPort()
$this->port = $parts['port'];
}

if (isset($parts['pass'])) {
$this->password = $parts['pass'];
}
}
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ac527d
feat: add SiteURI class
kenjis Feb 14, 2023
7eec7bb
chore: add system/HTTP/SiteURI.php
kenjis Feb 14, 2023
5593f8a
docs: add @deprecated
kenjis Feb 14, 2023
8a66de5
test: fix incorrect test
kenjis Feb 14, 2023
406128f
refactor: fix typo in method name
kenjis Feb 14, 2023
7d91c0e
docs: update PHPDocs
kenjis Feb 14, 2023
b728948
docs: add @deprecated
kenjis Feb 15, 2023
9af4994
feat: add validation for baseURL
kenjis Feb 15, 2023
5fa5305
feat: add getBaseURL() implementation
kenjis Feb 15, 2023
c8d5eef
feat: add param $relativePath to constructor
kenjis Feb 16, 2023
b30035b
feat: add param $host to constructor
kenjis Feb 16, 2023
9d82338
feat: add param $scheme to constructor
kenjis Feb 17, 2023
b231d1c
style: break long line
kenjis Feb 17, 2023
4932572
fix: remove unneeded methods
kenjis Feb 17, 2023
c3628d1
fix: change default values to null
kenjis Feb 17, 2023
57afaf9
fix: handling fragment
kenjis Feb 17, 2023
246eea2
fix: host in baseURL may be wrong
kenjis Feb 18, 2023
08bbc18
fix: disable setBaseURL() method
kenjis Feb 18, 2023
ab6da61
test: add assertions
kenjis Feb 20, 2023
82a567d
refactor: SiteURI::__construct()
kenjis Feb 20, 2023
ccfd79d
test: add test cases for path starting with slash
kenjis Feb 20, 2023
e733417
fix: change behavior
kenjis Feb 21, 2023
33fc028
test: use dataProviders
kenjis Feb 21, 2023
ba35e57
docs: update comments
kenjis Feb 22, 2023
fe754f3
fix: change behavior
kenjis Feb 23, 2023
3f4a9fd
fix: remove $allowedHostnames check
kenjis Feb 23, 2023
ecf96eb
test: add test cases
kenjis Feb 23, 2023
5193414
refactor: remove unneeded override
kenjis Feb 24, 2023
b24c01c
docs: fix typo in comments
kenjis Jul 10, 2023
3e349e1
docs: make URI::setSilent() deprecated
kenjis Jul 10, 2023
e529291
feat: add URI::withScheme() and deprecate URI::setScheme()
kenjis Jul 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test-phpcpd.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,4 +55,5 @@ jobs:
--exclude system/Database/OCI8/Builder.php
--exclude system/Database/Postgre/Builder.php
--exclude system/Debug/Exceptions.php
--exclude system/HTTP/SiteURI.php
-- app/ public/ system/
366 changes: 366 additions & 0 deletions system/HTTP/SiteURI.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\HTTP;

use BadMethodCallException;
use CodeIgniter\Exceptions\ConfigException;
use CodeIgniter\HTTP\Exceptions\HTTPException;
use Config\App;

/**
* URI for the application site
*/
class SiteURI extends URI
{
/**
* The current baseURL.
*/
private URI $baseURL;

/**
* The path part of baseURL.
*
* The baseURL "http://example.com/" → '/'
* The baseURL "http://localhost:8888/ci431/public/" → '/ci431/public/'
*/
private string $basePathWithoutIndexPage;

/**
* The Index File.
*/
private string $indexPage;

/**
* List of URI segments in baseURL and indexPage.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $baseSegments = [
* 0 => 'ci431',
* 1 => 'public',
* 2 => 'index.php',
* ];
*/
private array $baseSegments;

/**
* List of URI segments after indexPage.
*
* The word "URI Segments" originally means only the URI path part relative
* to the baseURL.
*
* If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
* and the baseURL is "http://localhost:8888/ci431/public/", then:
* $segments = [
* 0 => 'test',
* ];
*
* @var array
*
* @deprecated This property will be private.
Comment thread
MGatner marked this conversation as resolved.
*/
protected $segments;

/**
* URI path relative to baseURL.
*
* If the baseURL contains sub folders, this value will be different from
* the current URI path.
*
* This value never starts with '/'.
*/
private string $routePath;

/**
* @param string $relativePath URI path relative to baseURL. May include
* queries or fragments.
* @param string|null $host Optional current hostname.
* @param string|null $scheme Optional scheme. 'http' or 'https'.
* @phpstan-param 'http'|'https'|null $scheme
*/
public function __construct(
App $configApp,
string $relativePath = '',
?string $host = null,
?string $scheme = null
) {
$this->indexPage = $configApp->indexPage;

$this->baseURL = $this->determineBaseURL($configApp, $host, $scheme);

$this->setBasePath();

// Fix routePath, query, fragment
[$routePath, $query, $fragment] = $this->parseRelativePath($relativePath);

// Fix indexPage and routePath
$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

// Fix the current URI
$uri = $this->baseURL . $indexPageRoutePath;

// applyParts
$parts = parse_url($uri);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($uri);
}
$parts['query'] = $query;
$parts['fragment'] = $fragment;
$this->applyParts($parts);

$this->setRoutePath($routePath);
}

private function parseRelativePath(string $relativePath): array
{
$parts = parse_url('http://dummy/' . $relativePath);
if ($parts === false) {
throw HTTPException::forUnableToParseURI($relativePath);
}

$routePath = $relativePath === '/' ? '/' : ltrim($parts['path'], '/');

$query = $parts['query'] ?? '';
$fragment = $parts['fragment'] ?? '';

return [$routePath, $query, $fragment];
}

private function determineBaseURL(
App $configApp,
?string $host,
?string $scheme
): URI {
$baseURL = $this->normalizeBaseURL($configApp);

$uri = new URI($baseURL);

// Update scheme
if ($scheme !== null) {
$uri->setScheme($scheme);
} elseif ($configApp->forceGlobalSecureRequests) {
$uri->setScheme('https');
}

// Update host
if ($host !== null) {
$uri->setHost($host);
}

return $uri;
}

private function getIndexPageRoutePath(string $routePath): string
{
// Remove starting slash unless it is `/`.
if ($routePath !== '' && $routePath[0] === '/' && $routePath !== '/') {
$routePath = ltrim($routePath, '/');
}

// Check for an index page
$indexPage = '';
if ($this->indexPage !== '') {
$indexPage = $this->indexPage;

// Check if we need a separator
if ($routePath !== '' && $routePath[0] !== '/' && $routePath[0] !== '?') {
$indexPage .= '/';
}
}

$indexPageRoutePath = $indexPage . $routePath;

if ($indexPageRoutePath === '/') {
$indexPageRoutePath = '';
}

return $indexPageRoutePath;
}

private function normalizeBaseURL(App $configApp): string
{
// It's possible the user forgot a trailing slash on their
// baseURL, so let's help them out.
$baseURL = rtrim($configApp->baseURL, '/ ') . '/';

// Validate baseURL
if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) {
throw new ConfigException(
'Config\App::$baseURL is invalid.'
);
}

return $baseURL;
}

/**
* Sets basePathWithoutIndexPage and baseSegments.
*/
private function setBasePath(): void
{
$this->basePathWithoutIndexPage = $this->baseURL->getPath();

$this->baseSegments = $this->convertToSegments($this->basePathWithoutIndexPage);

if ($this->indexPage) {
$this->baseSegments[] = $this->indexPage;
}
}

/**
* @deprecated
*/
public function setBaseURL(string $baseURL): void
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* @deprecated
*/
public function setURI(?string $uri = null)
{
throw new BadMethodCallException('Cannot use this method.');
}

/**
* Returns the baseURL.
*
* @interal
*/
public function getBaseURL(): string
{
return (string) $this->baseURL;
}

/**
* Returns the URI path relative to baseURL.
*
* @return string The Route path.
*/
public function getRoutePath(): string
{
return $this->routePath;
}

/**
* Formats the URI as a string.
*/
public function __toString(): string
{
return static::createURIString(
$this->getScheme(),
$this->getAuthority(),
$this->getPath(),
$this->getQuery(),
$this->getFragment()
);
}

/**
* Sets the route path (and segments).
*
* @return $this
*/
public function setPath(string $path)
{
$this->setRoutePath($path);

return $this;
}

/**
* Sets the route path (and segments).
*/
private function setRoutePath(string $routePath): void
{
$routePath = $this->filterPath($routePath);

$indexPageRoutePath = $this->getIndexPageRoutePath($routePath);

$this->path = $this->basePathWithoutIndexPage . $indexPageRoutePath;

$this->routePath = ltrim($routePath, '/');

$this->segments = $this->convertToSegments($this->routePath);
}

/**
* Converts path to segments
*/
private function convertToSegments(string $path): array
{
$tempPath = trim($path, '/');

return ($tempPath === '') ? [] : explode('/', $tempPath);
}

/**
* Sets the path portion of the URI based on segments.
*
* @return $this
*
* @deprecated This method will be private.
*/
public function refreshPath()
{
$allSegments = array_merge($this->baseSegments, $this->segments);
$this->path = '/' . $this->filterPath(implode('/', $allSegments));

if ($this->routePath === '/' && $this->path !== '/') {
$this->path .= '/';
}

$this->routePath = $this->filterPath(implode('/', $this->segments));

return $this;
}

/**
* Saves our parts from a parse_url() call.
*/
protected function applyParts(array $parts)
{
if (! empty($parts['host'])) {
$this->host = $parts['host'];
}
if (! empty($parts['user'])) {
$this->user = $parts['user'];
}
if (isset($parts['path']) && $parts['path'] !== '') {
$this->path = $this->filterPath($parts['path']);
}
if (! empty($parts['query'])) {
$this->setQuery($parts['query']);
}
if (! empty($parts['fragment'])) {
$this->fragment = $parts['fragment'];
}

// Scheme
if (isset($parts['scheme'])) {
$this->setScheme(rtrim($parts['scheme'], ':/'));
} else {
$this->setScheme('http');
}

// Port
if (isset($parts['port']) && $parts['port'] !== null) {
// Valid port numbers are enforced by earlier parse_url() or setPort()
$this->port = $parts['port'];
}

if (isset($parts['pass'])) {
$this->password = $parts['pass'];
}
}
}
Loading