From fbca708fbd75e8ecc6b36b39c1307a67bf250808 Mon Sep 17 00:00:00 2001 From: flashwave Date: Sun, 4 Aug 2024 00:14:17 +0000 Subject: [PATCH] Even stricted PHPStan rules! --- VERSION | 2 +- phpstan.neon | 3 + src/CSRFP.php | 6 +- src/Cache/ArrayCache/ArrayCacheProvider.php | 5 +- src/Cache/CacheTools.php | 11 ++-- src/Cache/Memcached/MemcachedBackend.php | 5 +- src/Cache/Valkey/ValkeyProvider.php | 8 ++- src/Data/DbResultIterator.php | 6 +- src/Data/DbTools.php | 11 ++-- src/Data/MariaDB/MariaDBCharacterSetInfo.php | 23 ++++--- src/Data/MariaDB/MariaDBConnection.php | 7 ++- src/Data/Migration/DbMigrationManager.php | 17 ++++-- src/Data/SQLite/SQLiteBackend.php | 6 +- src/Http/Content/FormContent.php | 10 +++- src/Http/HttpRequest.php | 63 +++++++++++++------- src/Http/HttpResponseBuilder.php | 6 +- src/Http/HttpUploadedFile.php | 26 ++++++-- src/Http/Routing/HttpRouter.php | 30 +++++----- src/IntegerBaseConverter.php | 4 +- src/Performance/PerformanceCounter.php | 4 +- src/Performance/Stopwatch.php | 6 +- src/Performance/TimingPoint.php | 4 +- src/Performance/Timings.php | 4 +- src/XArray.php | 14 ++--- 24 files changed, 178 insertions(+), 103 deletions(-) diff --git a/VERSION b/VERSION index 2f6a83c..e40cb48 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2408.32219 +0.2408.40014 diff --git a/phpstan.neon b/phpstan.neon index ffdbd91..39c5d92 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,4 +1,7 @@ parameters: level: 9 + checkUninitializedProperties: true + checkImplicitMixed: true + checkBenevolentUnionTypes: true paths: - src diff --git a/src/CSRFP.php b/src/CSRFP.php index 37b4cfd..a071677 100644 --- a/src/CSRFP.php +++ b/src/CSRFP.php @@ -1,7 +1,7 @@ 0, ]; + if(is_float($value)) + $value = (int)$value; + return $value; } diff --git a/src/Cache/CacheTools.php b/src/Cache/CacheTools.php index 383a414..95d7615 100644 --- a/src/Cache/CacheTools.php +++ b/src/Cache/CacheTools.php @@ -1,7 +1,7 @@ $uri */ private static function resolveBackend(array $uri): ICacheBackend { - static $backends = []; + static $backends = null; + if(!is_array($backends)) + $backends = []; $scheme = $uri['scheme']; + $backend = $backends[$scheme] ?? null; - if(in_array($scheme, $backends)) - $backend = $backends[$scheme]; - else { + if(!($backend instanceof ICacheBackend)) { $backend = null; if(array_key_exists($scheme, self::CACHE_PROTOS)) diff --git a/src/Cache/Memcached/MemcachedBackend.php b/src/Cache/Memcached/MemcachedBackend.php index 5dde3c2..29d7a15 100644 --- a/src/Cache/Memcached/MemcachedBackend.php +++ b/src/Cache/Memcached/MemcachedBackend.php @@ -1,7 +1,7 @@ 1 ? (int)$parts[1] : 0; $endPoint = EndPoint::parse($parts[0]); diff --git a/src/Cache/Valkey/ValkeyProvider.php b/src/Cache/Valkey/ValkeyProvider.php index 367cb30..6912cfa 100644 --- a/src/Cache/Valkey/ValkeyProvider.php +++ b/src/Cache/Valkey/ValkeyProvider.php @@ -1,7 +1,7 @@ redis->incrBy($key, $amount); + $result = $this->redis->incrBy($key, $amount); + return is_int($result) ? $result : 0; } public function decrement(string $key, int $amount = 1): int { - return $this->redis->decrBy($key, $amount); + $result = $this->redis->decrBy($key, $amount); + return is_int($result) ? $result : 0; } public function close(): void { diff --git a/src/Data/DbResultIterator.php b/src/Data/DbResultIterator.php index eb2bb49..d8bd5b7 100644 --- a/src/Data/DbResultIterator.php +++ b/src/Data/DbResultIterator.php @@ -1,7 +1,7 @@ */ class DbResultIterator implements Iterator { - private bool $wasValid; + private bool $wasValid = false; private object $current; /** @@ -29,6 +29,8 @@ class DbResultIterator implements Iterator { ) { if(!is_callable($construct)) throw new InvalidArgumentException('$construct must be a callable.'); + + $this->current = (object)[]; } public function current(): mixed { diff --git a/src/Data/DbTools.php b/src/Data/DbTools.php index 710196d..1fd66ee 100644 --- a/src/Data/DbTools.php +++ b/src/Data/DbTools.php @@ -1,7 +1,7 @@ $uri */ private static function resolveBackend(array $uri): IDbBackend { - static $backends = []; + static $backends = null; + if(!is_array($backends)) + $backends = []; $scheme = $uri['scheme']; + $backend = $backends[$scheme] ?? null; - if(in_array($scheme, $backends)) - $backend = $backends[$scheme]; - else { + if(!($backend instanceof IDbBackend)) { $backend = null; if(array_key_exists($scheme, self::DB_PROTOS)) diff --git a/src/Data/MariaDB/MariaDBCharacterSetInfo.php b/src/Data/MariaDB/MariaDBCharacterSetInfo.php index 1a24448..153b53e 100644 --- a/src/Data/MariaDB/MariaDBCharacterSetInfo.php +++ b/src/Data/MariaDB/MariaDBCharacterSetInfo.php @@ -1,7 +1,7 @@ charSet->charset ?? ''; + return isset($this->charSet->charset) && is_scalar($this->charSet->charset) + ? (string)$this->charSet->charset : ''; } /** @@ -36,7 +37,8 @@ class MariaDBCharacterSetInfo { * @return string Default collation name. */ public function getDefaultCollation(): string { - return $this->charSet->collation ?? ''; + return isset($this->charSet->collation) && is_scalar($this->charSet->collation) + ? (string)$this->charSet->collation : ''; } /** @@ -46,7 +48,8 @@ class MariaDBCharacterSetInfo { * @return string Source directory. */ public function getDirectory(): string { - return $this->charSet->dir ?? ''; + return isset($this->charSet->dir) && is_scalar($this->charSet->dir) + ? (string)$this->charSet->dir : ''; } /** @@ -55,7 +58,8 @@ class MariaDBCharacterSetInfo { * @return int Minimum character width in bytes. */ public function getMinimumWidth(): int { - return $this->charSet->min_length ?? 0; + return isset($this->charSet->min_length) && is_scalar($this->charSet->min_length) + ? (int)$this->charSet->min_length : 0; } /** @@ -64,7 +68,8 @@ class MariaDBCharacterSetInfo { * @return int Maximum character width in bytes. */ public function getMaximumWidth(): int { - return $this->charSet->max_length ?? 0; + return isset($this->charSet->max_length) && is_scalar($this->charSet->max_length) + ? (int)$this->charSet->max_length : 0; } /** @@ -73,7 +78,8 @@ class MariaDBCharacterSetInfo { * @return int Character set identifier. */ public function getId(): int { - return $this->charSet->number ?? 0; + return isset($this->charSet->number) && is_scalar($this->charSet->number) + ? (int)$this->charSet->number : 0; } /** @@ -84,6 +90,7 @@ class MariaDBCharacterSetInfo { * @return int Character set status. */ public function getState(): int { - return $this->charSet->state ?? 0; + return isset($this->charSet->state) && is_scalar($this->charSet->state) + ? (int)$this->charSet->state : 0; } } diff --git a/src/Data/MariaDB/MariaDBConnection.php b/src/Data/MariaDB/MariaDBConnection.php index 378b841..09ae7af 100644 --- a/src/Data/MariaDB/MariaDBConnection.php +++ b/src/Data/MariaDB/MariaDBConnection.php @@ -1,7 +1,7 @@ connection->error_list); + // imagine if stdlib stuff had type annotations, couldn't be me + /** @var array */ + $errorList = $this->connection->error_list; + return MariaDBWarning::fromLastErrors($errorList); } /** diff --git a/src/Data/Migration/DbMigrationManager.php b/src/Data/Migration/DbMigrationManager.php index d03fcfe..48766aa 100644 --- a/src/Data/Migration/DbMigrationManager.php +++ b/src/Data/Migration/DbMigrationManager.php @@ -1,14 +1,15 @@ checkStmt === null) + throw new RuntimeException('Database migration manager has not been initialised.'); + $this->checkStmt->reset(); $this->checkStmt->addParameter(1, $name, DbType::STRING); $this->checkStmt->execute(); @@ -109,8 +114,12 @@ EOF; * * @param string $name Name of the migration. * @param ?DateTimeInterface $dateTime Timestamp of when the migration was run, null for now. + * @throws RuntimeException If the migration manager has not been initialised. */ public function completeMigration(string $name, ?DateTimeInterface $dateTime = null): void { + if($this->insertStmt === null) + throw new RuntimeException('Database migration manager has not been initialised.'); + $dateTime = XDateTime::toISO8601String($dateTime); $this->insertStmt->reset(); diff --git a/src/Data/SQLite/SQLiteBackend.php b/src/Data/SQLite/SQLiteBackend.php index 2bdd3b4..dad4ecd 100644 --- a/src/Data/SQLite/SQLiteBackend.php +++ b/src/Data/SQLite/SQLiteBackend.php @@ -1,7 +1,7 @@ $postVars */ + $postVars = $_POST; + + /** @var array $filesVars */ + $filesVars = $_FILES; + + return self::fromRaw($postVars, $filesVars); } public function __toString(): string { diff --git a/src/Http/HttpRequest.php b/src/Http/HttpRequest.php index 23474f7..075be29 100644 --- a/src/Http/HttpRequest.php +++ b/src/Http/HttpRequest.php @@ -1,7 +1,7 @@ setHttpVersion($_SERVER['SERVER_PROTOCOL']); - $build->setMethod($_SERVER['REQUEST_METHOD']); + $build->setHttpVersion((string)filter_input(INPUT_SERVER, 'SERVER_PROTOCOL')); + $build->setMethod((string)filter_input(INPUT_SERVER, 'REQUEST_METHOD')); // this currently doesn't "properly" support the scenario where a full url is specified in the http request - $path = $_SERVER['REQUEST_URI']; + $path = (string)filter_input(INPUT_SERVER, 'REQUEST_URI'); $pathQueryOffset = strpos($path, '?'); if($pathQueryOffset !== false) $path = substr($path, 0, $pathQueryOffset); @@ -154,22 +154,27 @@ class HttpRequest extends HttpMessage { $path = '/' . $path; $build->setPath($path); - $build->setParams($_GET); - $build->setCookies($_COOKIE); + + /** @var array $getVars */ + $getVars = $_GET; + $build->setParams($getVars); + + /** @var array $cookieVars */ + $cookieVars = $_COOKIE; + $build->setCookies($cookieVars); $contentType = null; $contentLength = 0; $headers = self::getRawRequestHeaders(); foreach($headers as $name => $value) { - $nameLower = strtolower($name); - if($nameLower === 'content-type') + if($name === 'content-type') try { $contentType = MediaType::parse($value); } catch(InvalidArgumentException $ex) { $contentType = null; } - elseif($nameLower === 'content-length') + elseif($name === 'content-length') $contentLength = (int)$value; $build->setHeader($name, $value); @@ -189,29 +194,43 @@ class HttpRequest extends HttpMessage { /** @return array */ private static function getRawRequestHeaders(): array { - if(function_exists('getallheaders')) - return getallheaders(); + if(function_exists('getallheaders')) { + $raw = getallheaders(); + $headers = []; + foreach($raw as $name => $value) + if(is_string($name) && is_string($value)) + $headers[strtolower($name)] = $value; + + return $headers; + } $headers = []; foreach($_SERVER as $key => $value) { + if(!is_string($key) || !is_scalar($value)) + continue; + if(substr($key, 0, 5) === 'HTTP_') { - $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5))))); - $headers[$key] = $value; + $key = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($key, 5)))); + $headers[$key] = (string)$value; } elseif($key === 'CONTENT_TYPE') { - $headers['Content-Type'] = $value; + $headers['content-type'] = (string)$value; } elseif($key === 'CONTENT_LENGTH') { - $headers['Content-Length'] = $value; + $headers['content-length'] = (string)$value; } } - if(!isset($headers['Authorization'])) { - if(isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { - $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; - } elseif(isset($_SERVER['PHP_AUTH_USER'])) { - $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . ($_SERVER['PHP_AUTH_PW'] ?? '')); - } elseif(isset($_SERVER['PHP_AUTH_DIGEST'])) { - $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST']; + if(!isset($headers['authorization'])) { + if(filter_has_var(INPUT_SERVER, 'REDIRECT_HTTP_AUTHORIZATION')) { + $headers['authorization'] = (string)filter_input(INPUT_SERVER, 'REDIRECT_HTTP_AUTHORIZATION'); + } elseif(filter_has_var(INPUT_SERVER, 'PHP_AUTH_USER')) { + $headers['authorization'] = sprintf('Basic %s', base64_encode(sprintf( + '%s:%s', + (string)filter_input(INPUT_SERVER, 'PHP_AUTH_USER'), + (string)filter_input(INPUT_SERVER, 'PHP_AUTH_PW') + ))); + } elseif(filter_has_var(INPUT_SERVER, 'PHP_AUTH_DIGEST')) { + $headers['authorization'] = (string)filter_input(INPUT_SERVER, 'PHP_AUTH_DIGEST'); } } diff --git a/src/Http/HttpResponseBuilder.php b/src/Http/HttpResponseBuilder.php index cba1f1e..0f5cda0 100644 --- a/src/Http/HttpResponseBuilder.php +++ b/src/Http/HttpResponseBuilder.php @@ -1,7 +1,7 @@ statusText = (string)$statusText; + $this->statusText = $statusText; } /** diff --git a/src/Http/HttpUploadedFile.php b/src/Http/HttpUploadedFile.php index e79251a..8079fe6 100644 --- a/src/Http/HttpUploadedFile.php +++ b/src/Http/HttpUploadedFile.php @@ -1,7 +1,7 @@ $val */ $arr[$key] = self::traverseFILES($val, $keyName); } else { $arr[$key][$keyName] = $val; @@ -230,8 +231,17 @@ class HttpUploadedFile implements ICloseable { if(is_array($arr['error'])) { $keys = array_keys($arr); - foreach($keys as $keyName) - $out[$key] = array_merge_recursive($out[$key] ?? [], self::traverseFILES($arr[$keyName], (string)$keyName)); + foreach($keys as $keyName) { + $source = $arr[$keyName]; + if(!is_array($source)) + continue; + + /** @var array $mergeWith */ + $mergeWith = $out[$key] ?? []; + + /** @var array $source */ + $out[$key] = array_merge_recursive($mergeWith, self::traverseFILES($source, (string)$keyName)); + } continue; } } @@ -251,9 +261,13 @@ class HttpUploadedFile implements ICloseable { continue; $key = substr($key, 1); - $coll[$key] = isset($val['error']) - ? self::createFromFILE($val) - : self::createObjectInstances($val); + + if(isset($val['error'])) + /** @var array $val */ + $coll[$key] = self::createFromFILE($val); + else + /** @var array $val */ + $coll[$key] = self::createObjectInstances($val); } return $coll; diff --git a/src/Http/Routing/HttpRouter.php b/src/Http/Routing/HttpRouter.php index 19f6c98..ff08937 100644 --- a/src/Http/Routing/HttpRouter.php +++ b/src/Http/Routing/HttpRouter.php @@ -1,7 +1,7 @@ > */ @@ -147,16 +147,16 @@ class HttpRouter implements IRouter { * @param callable $handler Middleware handler. */ public function use(string $path, callable $handler): void { - $this->middlewares[] = $mwInfo = new stdClass; - $mwInfo->handler = $handler; + $mwInfo = []; + $mwInfo['handler'] = $handler; $prepared = self::preparePath($path, true); - $mwInfo->dynamic = $prepared !== false; - - if($mwInfo->dynamic) - $mwInfo->match = $prepared; + if($prepared === false) + $mwInfo['prefix'] = $path; else - $mwInfo->prefix = $path; + $mwInfo['match'] = $prepared; + + $this->middlewares[] = $mwInfo; } /** @@ -207,19 +207,19 @@ class HttpRouter implements IRouter { $middlewares = []; foreach($this->middlewares as $mwInfo) { - if($mwInfo->dynamic ?? false) { - if(preg_match($mwInfo->match ?? '', $path, $args) !== 1) + if(array_key_exists('match', $mwInfo)) { + if(preg_match($mwInfo['match'], $path, $args) !== 1) continue; array_shift($args); - } else { - if(!str_starts_with($path, $mwInfo->prefix ?? '')) + } elseif(array_key_exists('prefix', $mwInfo)) { + if(!str_starts_with($path, $mwInfo['prefix'])) continue; $args = []; - } + } else continue; - $middlewares[] = [$mwInfo->handler ?? null, $args]; + $middlewares[] = [$mwInfo['handler'], $args]; } $methods = []; diff --git a/src/IntegerBaseConverter.php b/src/IntegerBaseConverter.php index 783ed0a..a15528c 100644 --- a/src/IntegerBaseConverter.php +++ b/src/IntegerBaseConverter.php @@ -1,7 +1,7 @@ getElapsedTicks() / $this->frequency; } diff --git a/src/Performance/TimingPoint.php b/src/Performance/TimingPoint.php index 8a97854..9c10be7 100644 --- a/src/Performance/TimingPoint.php +++ b/src/Performance/TimingPoint.php @@ -1,7 +1,7 @@ duration / PerformanceCounter::getFrequency(); } diff --git a/src/Performance/Timings.php b/src/Performance/Timings.php index b6b61ed..6548e89 100644 --- a/src/Performance/Timings.php +++ b/src/Performance/Timings.php @@ -1,7 +1,7 @@