From 162501b0f817b26794739ad59f979b6cdf9a5099 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Thu, 17 Apr 2025 22:58:52 +0800 Subject: [PATCH] refactor: fix phpstan errors in `URI` and `SiteURI` --- system/HTTP/SiteURI.php | 31 +++-- system/HTTP/URI.php | 130 +++++++++++------- utils/phpstan-baseline/empty.notAllowed.neon | 12 +- utils/phpstan-baseline/loader.neon | 2 +- .../missingType.iterableValue.neon | 52 +------ .../notIdentical.alwaysTrue.neon | 12 +- .../nullCoalesce.property.neon | 12 +- 7 files changed, 104 insertions(+), 147 deletions(-) diff --git a/system/HTTP/SiteURI.php b/system/HTTP/SiteURI.php index 25b5db560d4b..6e5206a0d493 100644 --- a/system/HTTP/SiteURI.php +++ b/system/HTTP/SiteURI.php @@ -68,7 +68,7 @@ class SiteURI extends URI * 0 => 'test', * ]; * - * @var array + * @var array * * @deprecated This property will be private. */ @@ -331,35 +331,48 @@ public function refreshPath() /** * Saves our parts from a parse_url() call. + * + * @param array{ + * host?: string, + * user?: string, + * path?: string, + * query?: string, + * fragment?: string, + * scheme?: string, + * port?: int, + * pass?: string, + * } $parts */ protected function applyParts(array $parts): void { - if (! empty($parts['host'])) { + if (isset($parts['host']) && $parts['host'] !== '') { $this->host = $parts['host']; } - if (! empty($parts['user'])) { + + if (isset($parts['user']) && $parts['user'] !== '') { $this->user = $parts['user']; } + if (isset($parts['path']) && $parts['path'] !== '') { $this->path = $this->filterPath($parts['path']); } - if (! empty($parts['query'])) { + + if (isset($parts['query']) && $parts['query'] !== '') { $this->setQuery($parts['query']); } - if (! empty($parts['fragment'])) { + + if (isset($parts['fragment']) && $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() + if (isset($parts['port'])) { + // Valid port numbers are enforced by earlier parse_url or setPort() $this->port = $parts['port']; } diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index dc0fffbd79ea..b714995ce143 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -57,7 +57,7 @@ class URI implements Stringable * * Starts at 1 instead of 0 * - * @var array + * @var array */ protected $segments = []; @@ -71,35 +71,35 @@ class URI implements Stringable /** * URI User Info * - * @var string + * @var string|null */ protected $user; /** * URI User Password * - * @var string + * @var string|null */ protected $password; /** * URI Host * - * @var string + * @var string|null */ protected $host; /** * URI Port * - * @var int + * @var int|null */ protected $port; /** * URI path. * - * @var string + * @var string|null */ protected $path; @@ -113,14 +113,19 @@ class URI implements Stringable /** * The query string. * - * @var array + * @var array */ protected $query = []; /** * Default schemes/ports. * - * @var array + * @var array{ + * http: int, + * https: int, + * ftp: int, + * sftp: int, + * } */ protected $defaultPorts = [ 'http' => 80, @@ -166,25 +171,26 @@ public static function createURIString( ?string $fragment = null, ): string { $uri = ''; - if ($scheme !== null && $scheme !== '') { + + if ((string) $scheme !== '') { $uri .= $scheme . '://'; } - if ($authority !== null && $authority !== '') { + if ((string) $authority !== '') { $uri .= $authority; } - if (isset($path) && $path !== '') { + if ((string) $path !== '') { $uri .= ! str_ends_with($uri, '/') ? '/' . ltrim($path, '/') : ltrim($path, '/'); } - if ($query !== '' && $query !== null) { + if ((string) $query !== '') { $uri .= '?' . $query; } - if ($fragment !== '' && $fragment !== null) { + if ((string) $fragment !== '') { $uri .= '#' . $fragment; } @@ -255,9 +261,7 @@ public static function removeDotSegments(string $path): string */ public function __construct(?string $uri = null) { - if ($uri !== null) { - $this->setURI($uri); - } + $this->setURI($uri); } /** @@ -301,21 +305,23 @@ public function useRawQueryString(bool $raw = true) */ public function setURI(?string $uri = null) { - if ($uri !== null) { - $parts = parse_url($uri); - - if ($parts === false) { - if ($this->silent) { - return $this; - } + if ($uri === null) { + return $this; + } - throw HTTPException::forUnableToParseURI($uri); - } + $parts = parse_url($uri); + if (is_array($parts)) { $this->applyParts($parts); + + return $this; } - return $this; + if ($this->silent) { + return $this; + } + + throw HTTPException::forUnableToParseURI($uri); } /** @@ -359,19 +365,18 @@ public function getScheme(): string */ public function getAuthority(bool $ignorePort = false): string { - if (empty($this->host)) { + if ((string) $this->host === '') { return ''; } $authority = $this->host; - if (! empty($this->getUserInfo())) { + if ((string) $this->getUserInfo() !== '') { $authority = $this->getUserInfo() . '@' . $authority; } - // Don't add port if it's a standard port for - // this scheme - if (! empty($this->port) && ! $ignorePort && $this->port !== $this->defaultPorts[$this->scheme]) { + // Don't add port if it's a standard port for this scheme + if ((int) $this->port !== 0 && ! $ignorePort && $this->port !== $this->defaultPorts[$this->scheme]) { $authority .= ':' . $this->port; } @@ -404,7 +409,7 @@ public function getUserInfo() { $userInfo = $this->user; - if ($this->showPassword === true && ! empty($this->password)) { + if ($this->showPassword === true && (string) $this->password !== '') { $userInfo .= ':' . $this->password; } @@ -496,6 +501,8 @@ public function getPath(): string /** * Retrieve the query string + * + * @param array{except?: list|string, only?: list|string} $options */ public function getQuery(array $options = []): string { @@ -525,7 +532,7 @@ public function getQuery(array $options = []): string $vars = $temp; } - return empty($vars) ? '' : http_build_query($vars); + return $vars === [] ? '' : http_build_query($vars); } /** @@ -538,6 +545,8 @@ public function getFragment(): string /** * Returns the segments of the path as an array. + * + * @return array */ public function getSegments(): array { @@ -602,9 +611,8 @@ public function setSegment(int $number, $value) $number--; $this->segments[$number] = $value; - $this->refreshPath(); - return $this; + return $this->refreshPath(); } /** @@ -646,6 +654,8 @@ public function __toString(): string * Change the path (and scheme) assuming URIs with the same host as baseURL * should be relative to the project's configuration. * + * @return array{string, string} + * * @deprecated This method will be deleted. */ private function changeSchemeAndPath(string $scheme, string $path): array @@ -690,7 +700,7 @@ public function setAuthority(string $str) $parts['path'] = $this->getPath(); } - if (empty($parts['host']) && $parts['path'] !== '') { + if (! isset($parts['host']) && $parts['path'] !== '') { $parts['host'] = $parts['path']; unset($parts['path']); } @@ -793,17 +803,17 @@ public function setPort(?int $port = null) return $this; } - if ($port <= 0 || $port > 65535) { - if ($this->silent) { - return $this; - } + if ($port > 0 && $port <= 65535) { + $this->port = $port; - throw HTTPException::forInvalidPort($port); + return $this; } - $this->port = $port; + if ($this->silent) { + return $this; + } - return $this; + throw HTTPException::forInvalidPort($port); } /** @@ -865,7 +875,7 @@ public function refreshPath() $tempPath = trim($this->path, '/'); - $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath); + $this->segments = $tempPath === '' ? [] : explode('/', $tempPath); return $this; } @@ -1031,35 +1041,48 @@ protected function filterPath(?string $path = null): string /** * Saves our parts from a parse_url call. * + * @param array{ + * host?: string, + * user?: string, + * path?: string, + * query?: string, + * fragment?: string, + * scheme?: string, + * port?: int, + * pass?: string, + * } $parts + * * @return void */ protected function applyParts(array $parts) { - if (! empty($parts['host'])) { + if (isset($parts['host']) && $parts['host'] !== '') { $this->host = $parts['host']; } - if (! empty($parts['user'])) { + + if (isset($parts['user']) && $parts['user'] !== '') { $this->user = $parts['user']; } + if (isset($parts['path']) && $parts['path'] !== '') { $this->path = $this->filterPath($parts['path']); } - if (! empty($parts['query'])) { + + if (isset($parts['query']) && $parts['query'] !== '') { $this->setQuery($parts['query']); } - if (! empty($parts['fragment'])) { + + if (isset($parts['fragment']) && $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) { + if (isset($parts['port'])) { // Valid port numbers are enforced by earlier parse_url or setPort() $this->port = $parts['port']; } @@ -1068,11 +1091,10 @@ protected function applyParts(array $parts) $this->password = $parts['pass']; } - // Populate our segments array if (isset($parts['path']) && $parts['path'] !== '') { $tempPath = trim($parts['path'], '/'); - $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath); + $this->segments = $tempPath === '' ? [] : explode('/', $tempPath); } } @@ -1161,6 +1183,8 @@ protected function mergePaths(self $base, self $reference): string /** * This is equivalent to the native PHP parse_str() function. * This version allows the dot to be used as a key of the query string. + * + * @return array */ protected function parseStr(string $query): array { diff --git a/utils/phpstan-baseline/empty.notAllowed.neon b/utils/phpstan-baseline/empty.notAllowed.neon index c0e03d572785..ea06afa8b720 100644 --- a/utils/phpstan-baseline/empty.notAllowed.neon +++ b/utils/phpstan-baseline/empty.notAllowed.neon @@ -1,4 +1,4 @@ -# total 265 errors +# total 251 errors parameters: ignoreErrors: @@ -252,16 +252,6 @@ parameters: count: 2 path: ../../system/HTTP/Response.php - - - message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#' - count: 4 - path: ../../system/HTTP/SiteURI.php - - - - message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#' - count: 10 - path: ../../system/HTTP/URI.php - - message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#' count: 2 diff --git a/utils/phpstan-baseline/loader.neon b/utils/phpstan-baseline/loader.neon index 7ffb4839cc44..adbe8eaf1322 100644 --- a/utils/phpstan-baseline/loader.neon +++ b/utils/phpstan-baseline/loader.neon @@ -1,4 +1,4 @@ -# total 3539 errors +# total 3511 errors includes: - argument.type.neon - assign.propertyType.neon diff --git a/utils/phpstan-baseline/missingType.iterableValue.neon b/utils/phpstan-baseline/missingType.iterableValue.neon index 773002730217..838268a14cc5 100644 --- a/utils/phpstan-baseline/missingType.iterableValue.neon +++ b/utils/phpstan-baseline/missingType.iterableValue.neon @@ -1,4 +1,4 @@ -# total 1628 errors +# total 1618 errors parameters: ignoreErrors: @@ -3932,11 +3932,6 @@ parameters: count: 1 path: ../../system/HTTP/ResponseInterface.php - - - message: '#^Method CodeIgniter\\HTTP\\SiteURI\:\:applyParts\(\) has parameter \$parts with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/HTTP/SiteURI.php - - message: '#^Method CodeIgniter\\HTTP\\SiteURI\:\:baseUrl\(\) has parameter \$relativePath with no value type specified in iterable type array\.$#' count: 1 @@ -3967,56 +3962,11 @@ parameters: count: 1 path: ../../system/HTTP/SiteURI.php - - - message: '#^Property CodeIgniter\\HTTP\\SiteURI\:\:\$segments type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/HTTP/SiteURI.php - - - - message: '#^Method CodeIgniter\\HTTP\\URI\:\:applyParts\(\) has parameter \$parts with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/HTTP/URI.php - - - - message: '#^Method CodeIgniter\\HTTP\\URI\:\:changeSchemeAndPath\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/HTTP/URI.php - - - - message: '#^Method CodeIgniter\\HTTP\\URI\:\:getQuery\(\) has parameter \$options with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/HTTP/URI.php - - - - message: '#^Method CodeIgniter\\HTTP\\URI\:\:getSegments\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/HTTP/URI.php - - - - message: '#^Method CodeIgniter\\HTTP\\URI\:\:parseStr\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/HTTP/URI.php - - message: '#^Method CodeIgniter\\HTTP\\URI\:\:setQueryArray\(\) has parameter \$query with no value type specified in iterable type array\.$#' count: 1 path: ../../system/HTTP/URI.php - - - message: '#^Property CodeIgniter\\HTTP\\URI\:\:\$defaultPorts type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/HTTP/URI.php - - - - message: '#^Property CodeIgniter\\HTTP\\URI\:\:\$query type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/HTTP/URI.php - - - - message: '#^Property CodeIgniter\\HTTP\\URI\:\:\$segments type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/HTTP/URI.php - - message: '#^Method CodeIgniter\\Helpers\\Array\\ArrayHelper\:\:arrayAttachIndexedValue\(\) has parameter \$indexes with no value type specified in iterable type array\.$#' count: 1 diff --git a/utils/phpstan-baseline/notIdentical.alwaysTrue.neon b/utils/phpstan-baseline/notIdentical.alwaysTrue.neon index bb1db9cde6db..48feec028165 100644 --- a/utils/phpstan-baseline/notIdentical.alwaysTrue.neon +++ b/utils/phpstan-baseline/notIdentical.alwaysTrue.neon @@ -1,4 +1,4 @@ -# total 3 errors +# total 1 error parameters: ignoreErrors: @@ -6,13 +6,3 @@ parameters: message: '#^Strict comparison using \!\=\= between mixed and null will always evaluate to true\.$#' count: 1 path: ../../system/BaseModel.php - - - - message: '#^Strict comparison using \!\=\= between mixed and null will always evaluate to true\.$#' - count: 1 - path: ../../system/HTTP/SiteURI.php - - - - message: '#^Strict comparison using \!\=\= between mixed and null will always evaluate to true\.$#' - count: 1 - path: ../../system/HTTP/URI.php diff --git a/utils/phpstan-baseline/nullCoalesce.property.neon b/utils/phpstan-baseline/nullCoalesce.property.neon index fb447e22e1c2..6caf5f23ef1d 100644 --- a/utils/phpstan-baseline/nullCoalesce.property.neon +++ b/utils/phpstan-baseline/nullCoalesce.property.neon @@ -1,4 +1,4 @@ -# total 22 errors +# total 20 errors parameters: ignoreErrors: @@ -27,16 +27,6 @@ parameters: count: 1 path: ../../system/HTTP/URI.php - - - message: '#^Property CodeIgniter\\HTTP\\URI\:\:\$host \(string\) on left side of \?\? is not nullable\.$#' - count: 1 - path: ../../system/HTTP/URI.php - - - - message: '#^Property CodeIgniter\\HTTP\\URI\:\:\$path \(string\) on left side of \?\? is not nullable\.$#' - count: 1 - path: ../../system/HTTP/URI.php - - message: '#^Property CodeIgniter\\Images\\Handlers\\BaseHandler\:\:\$height \(int\) on left side of \?\? is not nullable\.$#' count: 4