From 417cbd219800f624b5c8a4f8ec22d5adac56f4b4 Mon Sep 17 00:00:00 2001 From: neznaika0 Date: Sun, 17 Nov 2024 12:49:47 +0300 Subject: [PATCH] refactor: Fix `phpstan` errors related to `Autoloader` (#9249) * refactor: Fix `phpstan` errors related to `Autoloader` * fix: Update @ignore-line * fix: Save `realpath()` as variable * refactor: `phpstan` strict comparison * fix: Restore check helpers --- phpstan-baseline.php | 186 ------------------ system/Autoloader/Autoloader.php | 5 +- system/Autoloader/FileLocator.php | 31 +-- system/Autoloader/FileLocatorCached.php | 5 + system/Autoloader/FileLocatorInterface.php | 2 + system/Common.php | 12 +- system/Config/Factories.php | 6 +- system/Test/ReflectionHelper.php | 7 +- tests/system/Autoloader/AutoloaderTest.php | 12 +- .../Autoloader/FileLocatorCachedTest.php | 1 - tests/system/Autoloader/FileLocatorTest.php | 3 + tests/system/View/DecoratorsTest.php | 4 +- tests/system/View/ParserFilterTest.php | 4 +- tests/system/View/ParserTest.php | 4 +- tests/system/View/ViewTest.php | 4 +- 15 files changed, 64 insertions(+), 222 deletions(-) diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 0edfc14b6f47..a8ac948def52 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -7,54 +7,12 @@ 'count' => 1, 'path' => __DIR__ . '/app/Config/Filters.php', ]; -$ignoreErrors[] = [ - // identifier: function.alreadyNarrowedType - 'message' => '#^Call to function method_exists\\(\\) with \'Composer\\\\\\\\InstalledVersions\' and \'getAllRawData\' will always evaluate to true\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Autoloader/Autoloader.php', -]; -$ignoreErrors[] = [ - // identifier: missingType.iterableValue - 'message' => '#^Method CodeIgniter\\\\Autoloader\\\\Autoloader\\:\\:loadComposerNamespaces\\(\\) has parameter \\$composerPackages with no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Autoloader/Autoloader.php', -]; $ignoreErrors[] = [ // identifier: isset.property 'message' => '#^Property Config\\\\Autoload\\:\\:\\$helpers \\(array\\\\) in isset\\(\\) is not nullable\\.$#', 'count' => 1, 'path' => __DIR__ . '/system/Autoloader/Autoloader.php', ]; -$ignoreErrors[] = [ - // identifier: ternary.condNotBoolean - 'message' => '#^Only booleans are allowed in a ternary operator condition, int\\|string given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Autoloader/FileLocator.php', -]; -$ignoreErrors[] = [ - // identifier: ternary.shortNotAllowed - 'message' => '#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#', - 'count' => 6, - 'path' => __DIR__ . '/system/Autoloader/FileLocator.php', -]; -$ignoreErrors[] = [ - // identifier: missingType.iterableValue - 'message' => '#^Method CodeIgniter\\\\Autoloader\\\\FileLocatorCached\\:\\:search\\(\\) return type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Autoloader/FileLocatorCached.php', -]; -$ignoreErrors[] = [ - // identifier: missingType.iterableValue - 'message' => '#^Property CodeIgniter\\\\Autoloader\\\\FileLocatorCached\\:\\:\\$cache type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Autoloader/FileLocatorCached.php', -]; -$ignoreErrors[] = [ - // identifier: missingType.iterableValue - 'message' => '#^Method CodeIgniter\\\\Autoloader\\\\FileLocatorInterface\\:\\:search\\(\\) return type has no value type specified in iterable type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Autoloader/FileLocatorInterface.php', -]; $ignoreErrors[] = [ // identifier: missingType.iterableValue 'message' => '#^Method CodeIgniter\\\\BaseModel\\:\\:__call\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#', @@ -1021,12 +979,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Common.php', ]; -$ignoreErrors[] = [ - // identifier: empty.notAllowed - 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', - 'count' => 4, - 'path' => __DIR__ . '/system/Common.php', -]; $ignoreErrors[] = [ // identifier: missingType.iterableValue 'message' => '#^Function cache\\(\\) return type has no value type specified in iterable type array\\.$#', @@ -1297,18 +1249,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Config/Factories.php', ]; -$ignoreErrors[] = [ - // identifier: booleanNot.exprNotBoolean - 'message' => '#^Only booleans are allowed in a negated boolean, array given\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Config/Factories.php', -]; -$ignoreErrors[] = [ - // identifier: booleanNot.exprNotBoolean - 'message' => '#^Only booleans are allowed in a negated boolean, string\\|null given\\.$#', - 'count' => 2, - 'path' => __DIR__ . '/system/Config/Factories.php', -]; $ignoreErrors[] = [ // identifier: missingType.iterableValue 'message' => '#^Property CodeIgniter\\\\Config\\\\Factory\\:\\:\\$default type has no value type specified in iterable type array\\.$#', @@ -9241,12 +9181,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Test/CIUnitTestCase.php', ]; -$ignoreErrors[] = [ - // identifier: missingType.callable - 'message' => '#^Method CodeIgniter\\\\Test\\\\CIUnitTestCase\\:\\:getPrivateMethodInvoker\\(\\) return type has no signature specified for Closure\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Test/CIUnitTestCase.php', -]; $ignoreErrors[] = [ // identifier: missingType.return 'message' => '#^Method CodeIgniter\\\\Test\\\\CIUnitTestCase\\:\\:mockCache\\(\\) has no return type specified\\.$#', @@ -9277,12 +9211,6 @@ 'count' => 1, 'path' => __DIR__ . '/system/Test/CIUnitTestCase.php', ]; -$ignoreErrors[] = [ - // identifier: missingType.return - 'message' => '#^Method CodeIgniter\\\\Test\\\\CIUnitTestCase\\:\\:setPrivateProperty\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/system/Test/CIUnitTestCase.php', -]; $ignoreErrors[] = [ // identifier: booleanNot.exprNotBoolean 'message' => '#^Only booleans are allowed in a negated boolean, CodeIgniter\\\\CodeIgniter given\\.$#', @@ -11821,36 +11749,6 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/AutoReview/FrameworkCodeTest.php', ]; -$ignoreErrors[] = [ - // identifier: missingType.callable - 'message' => '#^Method CodeIgniter\\\\Autoloader\\\\AutoloaderTest\\:\\:getPrivateMethodInvoker\\(\\) return type has no signature specified for Closure\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Autoloader/AutoloaderTest.php', -]; -$ignoreErrors[] = [ - // identifier: missingType.return - 'message' => '#^Method CodeIgniter\\\\Autoloader\\\\AutoloaderTest\\:\\:setPrivateProperty\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Autoloader/AutoloaderTest.php', -]; -$ignoreErrors[] = [ - // identifier: ternary.shortNotAllowed - 'message' => '#^Short ternary operator is not allowed\\. Use null coalesce operator if applicable or consider using long ternary\\.$#', - 'count' => 2, - 'path' => __DIR__ . '/tests/system/Autoloader/AutoloaderTest.php', -]; -$ignoreErrors[] = [ - // identifier: method.notFound - 'message' => '#^Call to an undefined method CodeIgniter\\\\Autoloader\\\\FileLocatorInterface\\:\\:__destruct\\(\\)\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Autoloader/FileLocatorCachedTest.php', -]; -$ignoreErrors[] = [ - // identifier: method.notFound - 'message' => '#^Call to an undefined method CodeIgniter\\\\Autoloader\\\\FileLocatorInterface\\:\\:deleteCache\\(\\)\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Autoloader/FileLocatorCachedTest.php', -]; $ignoreErrors[] = [ // identifier: codeigniter.superglobalAccessAssign 'message' => '#^Assigning 3 directly on offset \'argc\' of \\$_SERVER is discouraged\\.$#', @@ -13141,24 +13039,12 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/Database/Builder/WhereTest.php', ]; -$ignoreErrors[] = [ - // identifier: missingType.callable - 'message' => '#^Method CodeIgniter\\\\Database\\\\ConfigTest\\:\\:getPrivateMethodInvoker\\(\\) return type has no signature specified for Closure\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Database/ConfigTest.php', -]; $ignoreErrors[] = [ // identifier: missingType.iterableValue 'message' => '#^Method CodeIgniter\\\\Database\\\\ConfigTest\\:\\:provideConvertDSN\\(\\) return type has no value type specified in iterable type iterable\\.$#', 'count' => 1, 'path' => __DIR__ . '/tests/system/Database/ConfigTest.php', ]; -$ignoreErrors[] = [ - // identifier: missingType.return - 'message' => '#^Method CodeIgniter\\\\Database\\\\ConfigTest\\:\\:setPrivateProperty\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Database/ConfigTest.php', -]; $ignoreErrors[] = [ // identifier: missingType.iterableValue 'message' => '#^Property CodeIgniter\\\\Database\\\\ConfigTest\\:\\:\\$dsnGroup type has no value type specified in iterable type array\\.$#', @@ -13501,18 +13387,6 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/Debug/ExceptionsTest.php', ]; -$ignoreErrors[] = [ - // identifier: missingType.callable - 'message' => '#^Method CodeIgniter\\\\Debug\\\\ExceptionsTest\\:\\:getPrivateMethodInvoker\\(\\) return type has no signature specified for Closure\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Debug/ExceptionsTest.php', -]; -$ignoreErrors[] = [ - // identifier: missingType.return - 'message' => '#^Method CodeIgniter\\\\Debug\\\\ExceptionsTest\\:\\:setPrivateProperty\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Debug/ExceptionsTest.php', -]; $ignoreErrors[] = [ // identifier: argument.type 'message' => '#^Parameter \\#2 \\$callable of method CodeIgniter\\\\Debug\\\\Timer\\:\\:record\\(\\) expects callable\\(\\)\\: mixed, \'strlen\' given\\.$#', @@ -13651,12 +13525,6 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php', ]; -$ignoreErrors[] = [ - // identifier: missingType.callable - 'message' => '#^Method CodeIgniter\\\\Entity\\\\EntityTest\\:\\:getPrivateMethodInvoker\\(\\) return type has no signature specified for Closure\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php', -]; $ignoreErrors[] = [ // identifier: missingType.return 'message' => '#^Method CodeIgniter\\\\Entity\\\\EntityTest\\:\\:getSimpleSwappedEntity\\(\\) has no return type specified\\.$#', @@ -13669,12 +13537,6 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php', ]; -$ignoreErrors[] = [ - // identifier: missingType.return - 'message' => '#^Method CodeIgniter\\\\Entity\\\\EntityTest\\:\\:setPrivateProperty\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Entity/EntityTest.php', -]; $ignoreErrors[] = [ // identifier: codeigniter.configArgumentInstanceof 'message' => '#^Argument \\#1 \\$name \\(\'Modules\'\\) passed to function config does not extend CodeIgniter\\\\\\\\Config\\\\\\\\BaseConfig\\.$#', @@ -15979,18 +15841,6 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/Models/InsertModelTest.php', ]; -$ignoreErrors[] = [ - // identifier: missingType.callable - 'message' => '#^Method CodeIgniter\\\\Models\\\\LiveModelTestCase\\:\\:getPrivateMethodInvoker\\(\\) return type has no signature specified for Closure\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Models/LiveModelTestCase.php', -]; -$ignoreErrors[] = [ - // identifier: missingType.return - 'message' => '#^Method CodeIgniter\\\\Models\\\\LiveModelTestCase\\:\\:setPrivateProperty\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Models/LiveModelTestCase.php', -]; $ignoreErrors[] = [ // identifier: property.nonObject 'message' => '#^Cannot access property \\$created_at on array\\.$#', @@ -17041,18 +16891,6 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/Session/Handlers/Database/AbstractHandlerTestCase.php', ]; -$ignoreErrors[] = [ - // identifier: missingType.callable - 'message' => '#^Method CodeIgniter\\\\Session\\\\Handlers\\\\Database\\\\AbstractHandlerTestCase\\:\\:getPrivateMethodInvoker\\(\\) return type has no signature specified for Closure\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Session/Handlers/Database/AbstractHandlerTestCase.php', -]; -$ignoreErrors[] = [ - // identifier: missingType.return - 'message' => '#^Method CodeIgniter\\\\Session\\\\Handlers\\\\Database\\\\AbstractHandlerTestCase\\:\\:setPrivateProperty\\(\\) has no return type specified\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/Session/Handlers/Database/AbstractHandlerTestCase.php', -]; $ignoreErrors[] = [ // identifier: missingType.return 'message' => '#^Method CodeIgniter\\\\Session\\\\Handlers\\\\Database\\\\MySQLiHandlerTest\\:\\:getInstance\\(\\) has no return type specified\\.$#', @@ -18325,18 +18163,6 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/Validation/ValidationTest.php', ]; -$ignoreErrors[] = [ - // identifier: assign.propertyType - 'message' => '#^Property CodeIgniter\\\\View\\\\DecoratorsTest\\:\\:\\$loader \\(CodeIgniter\\\\Autoloader\\\\FileLocator\\) does not accept CodeIgniter\\\\Autoloader\\\\FileLocatorInterface\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/View/DecoratorsTest.php', -]; -$ignoreErrors[] = [ - // identifier: assign.propertyType - 'message' => '#^Property CodeIgniter\\\\View\\\\ParserFilterTest\\:\\:\\$loader \\(CodeIgniter\\\\Autoloader\\\\FileLocator\\) does not accept CodeIgniter\\\\Autoloader\\\\FileLocatorInterface\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/View/ParserFilterTest.php', -]; $ignoreErrors[] = [ // identifier: missingType.return 'message' => '#^Method CodeIgniter\\\\View\\\\ParserPluginTest\\:\\:setHints\\(\\) has no return type specified\\.$#', @@ -18367,12 +18193,6 @@ 'count' => 3, 'path' => __DIR__ . '/tests/system/View/ParserTest.php', ]; -$ignoreErrors[] = [ - // identifier: assign.propertyType - 'message' => '#^Property CodeIgniter\\\\View\\\\ParserTest\\:\\:\\$loader \\(CodeIgniter\\\\Autoloader\\\\FileLocator\\) does not accept CodeIgniter\\\\Autoloader\\\\FileLocatorInterface\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/View/ParserTest.php', -]; $ignoreErrors[] = [ // identifier: method.notFound 'message' => '#^Call to an undefined method CodeIgniter\\\\View\\\\Table\\:\\:compileTemplate\\(\\)\\.$#', @@ -18463,11 +18283,5 @@ 'count' => 1, 'path' => __DIR__ . '/tests/system/View/TableTest.php', ]; -$ignoreErrors[] = [ - // identifier: assign.propertyType - 'message' => '#^Property CodeIgniter\\\\View\\\\ViewTest\\:\\:\\$loader \\(CodeIgniter\\\\Autoloader\\\\FileLocator\\) does not accept CodeIgniter\\\\Autoloader\\\\FileLocatorInterface\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/tests/system/View/ViewTest.php', -]; return ['parameters' => ['ignoreErrors' => $ignoreErrors]]; diff --git a/system/Autoloader/Autoloader.php b/system/Autoloader/Autoloader.php index bba6d3c5cebd..b338027c9c8a 100644 --- a/system/Autoloader/Autoloader.php +++ b/system/Autoloader/Autoloader.php @@ -366,6 +366,9 @@ public function sanitizeFilename(string $filename): string return $cleanFilename; } + /** + * @param array{only?: list, exclude?: list} $composerPackages + */ private function loadComposerNamespaces(ClassLoader $composer, array $composerPackages): void { $namespacePaths = $composer->getPrefixesPsr4(); @@ -379,7 +382,7 @@ private function loadComposerNamespaces(ClassLoader $composer, array $composerPa } } - if (! method_exists(InstalledVersions::class, 'getAllRawData')) { + if (! method_exists(InstalledVersions::class, 'getAllRawData')) { // @phpstan-ignore function.alreadyNarrowedType throw new RuntimeException( 'Your Composer version is too old.' . ' Please update Composer (run `composer self-update`) to v2.0.14 or later' diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index 14b9a1366ab4..8e2564e00941 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -145,10 +145,11 @@ public function getClassname(string $file): string if ((isset($tokens[$i - 2][1]) && ($tokens[$i - 2][1] === 'phpnamespace' || $tokens[$i - 2][1] === 'namespace')) || ($dlm && $tokens[$i - 1][0] === T_NS_SEPARATOR && $token[0] === T_STRING)) { if (! $dlm) { - $namespace = 0; + $namespace = ''; } + if (isset($token[1])) { - $namespace = $namespace ? $namespace . '\\' . $token[1] : $token[1]; + $namespace = $namespace !== '' ? $namespace . '\\' . $token[1] : $token[1]; $dlm = true; } } elseif ($dlm && ($token[0] !== T_NS_SEPARATOR) && ($token[0] !== T_STRING)) { @@ -194,8 +195,9 @@ public function search(string $path, string $ext = 'php', bool $prioritizeApp = foreach ($this->getNamespaces() as $namespace) { if (isset($namespace['path']) && is_file($namespace['path'] . $path)) { - $fullPath = $namespace['path'] . $path; - $fullPath = realpath($fullPath) ?: $fullPath; + $fullPath = $namespace['path'] . $path; + $resolvedPath = realpath($fullPath); + $fullPath = $resolvedPath !== false ? $resolvedPath : $fullPath; if ($prioritizeApp) { $foundPaths[] = $fullPath; @@ -272,14 +274,16 @@ protected function getNamespaces() */ public function findQualifiedNameFromPath(string $path) { - $path = realpath($path) ?: $path; + $resolvedPath = realpath($path); + $path = $resolvedPath !== false ? $resolvedPath : $path; if (! is_file($path)) { return false; } foreach ($this->getNamespaces() as $namespace) { - $namespace['path'] = realpath($namespace['path']) ?: $namespace['path']; + $resolvedNamespacePath = realpath($namespace['path']); + $namespace['path'] = $resolvedNamespacePath !== false ? $resolvedNamespacePath : $namespace['path']; if ($namespace['path'] === '') { continue; @@ -331,8 +335,9 @@ public function listFiles(string $path): array helper('filesystem'); foreach ($this->getNamespaces() as $namespace) { - $fullPath = $namespace['path'] . $path; - $fullPath = realpath($fullPath) ?: $fullPath; + $fullPath = $namespace['path'] . $path; + $resolvedPath = realpath($fullPath); + $fullPath = $resolvedPath !== false ? $resolvedPath : $fullPath; if (! is_dir($fullPath)) { continue; @@ -365,8 +370,9 @@ public function listNamespaceFiles(string $prefix, string $path): array // autoloader->getNamespace($prefix) returns an array of paths for that namespace foreach ($this->autoloader->getNamespace($prefix) as $namespacePath) { - $fullPath = rtrim($namespacePath, '/') . '/' . $path; - $fullPath = realpath($fullPath) ?: $fullPath; + $fullPath = rtrim($namespacePath, '/') . '/' . $path; + $resolvedPath = realpath($fullPath); + $fullPath = $resolvedPath !== false ? $resolvedPath : $fullPath; if (! is_dir($fullPath)) { continue; @@ -392,8 +398,9 @@ public function listNamespaceFiles(string $prefix, string $path): array */ protected function legacyLocate(string $file, ?string $folder = null) { - $path = APPPATH . ($folder === null ? $file : $folder . '/' . $file); - $path = realpath($path) ?: $path; + $path = APPPATH . ($folder === null ? $file : $folder . '/' . $file); + $resolvedPath = realpath($path); + $path = $resolvedPath !== false ? $resolvedPath : $path; if (is_file($path)) { return $path; diff --git a/system/Autoloader/FileLocatorCached.php b/system/Autoloader/FileLocatorCached.php index adf453308de3..8b17399444c6 100644 --- a/system/Autoloader/FileLocatorCached.php +++ b/system/Autoloader/FileLocatorCached.php @@ -36,6 +36,8 @@ final class FileLocatorCached implements FileLocatorInterface * [ * 'search' => [$path => $foundPaths], * ] + * + * @var array> */ private array $cache = []; @@ -114,6 +116,9 @@ public function getClassname(string $file): string return $classname; } + /** + * @return list + */ public function search(string $path, string $ext = 'php', bool $prioritizeApp = true): array { if (isset($this->cache['search'][$path][$ext][$prioritizeApp])) { diff --git a/system/Autoloader/FileLocatorInterface.php b/system/Autoloader/FileLocatorInterface.php index 3f7355a8f09d..3a1112dd480e 100644 --- a/system/Autoloader/FileLocatorInterface.php +++ b/system/Autoloader/FileLocatorInterface.php @@ -53,6 +53,8 @@ public function getClassname(string $file): string; * 'app/Modules/foo/Config/Routes.php', * 'app/Modules/bar/Config/Routes.php', * ] + * + * @return list */ public function search(string $path, string $ext = 'php', bool $prioritizeApp = true): array; diff --git a/system/Common.php b/system/Common.php index 94ecc75e9841..85db03f2870c 100644 --- a/system/Common.php +++ b/system/Common.php @@ -580,8 +580,8 @@ function helper($filenames): void foreach ($filenames as $filename) { // Store our system and application helper // versions so that we can control the load ordering. - $systemHelper = null; - $appHelper = null; + $systemHelper = ''; + $appHelper = ''; $localIncludes = []; if (! str_contains($filename, '_helper')) { @@ -598,7 +598,7 @@ function helper($filenames): void if (str_contains($filename, '\\')) { $path = $loader->locateFile($filename, 'Helpers'); - if (empty($path)) { + if ($path !== '') { throw FileNotFoundException::forFileNotFound($filename); } @@ -620,7 +620,7 @@ function helper($filenames): void } // App-level helpers should override all others - if (! empty($appHelper)) { + if ($appHelper !== '') { $includes[] = $appHelper; $loaded[] = $filename; } @@ -629,7 +629,7 @@ function helper($filenames): void $includes = [...$includes, ...$localIncludes]; // And the system default one should be added in last. - if (! empty($systemHelper)) { + if ($systemHelper !== '') { $includes[] = $systemHelper; $loaded[] = $filename; } @@ -1092,7 +1092,7 @@ function stringify_attributes($attributes, bool $js = false): string { $atts = ''; - if (empty($attributes)) { + if ($attributes === '' || $attributes === [] || $attributes === null) { return $atts; } diff --git a/system/Config/Factories.php b/system/Config/Factories.php index be66005a8adf..b845d5825f43 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -162,7 +162,7 @@ public static function __callStatic(string $component, array $arguments) } // Try to locate the class - if (! $class = self::locateClass($options, $alias)) { + if (($class = self::locateClass($options, $alias)) === null) { return null; } @@ -213,7 +213,7 @@ private static function getDefinedInstance(array $options, string $alias, array } // Try to locate the class - if (! $class = self::locateClass($options, $alias)) { + if (($class = self::locateClass($options, $alias)) === null) { return null; } @@ -310,7 +310,7 @@ class_exists($alias, false) } // No namespace? Search for it // Check all namespaces, prioritizing App and modules - elseif (! $files = $locator->search($options['path'] . DIRECTORY_SEPARATOR . $alias)) { + elseif (($files = $locator->search($options['path'] . DIRECTORY_SEPARATOR . $alias)) === []) { return null; } diff --git a/system/Test/ReflectionHelper.php b/system/Test/ReflectionHelper.php index c72276d1a01a..adf22ac5a9ac 100644 --- a/system/Test/ReflectionHelper.php +++ b/system/Test/ReflectionHelper.php @@ -31,7 +31,8 @@ trait ReflectionHelper * @param object|string $obj object or class name * @param string $method method name * - * @return Closure + * @return Closure + * @phpstan-return Closure(mixed ...$args): mixed * * @throws ReflectionException */ @@ -73,7 +74,7 @@ private static function getAccessibleRefProperty($obj, $property) * * @throws ReflectionException */ - public static function setPrivateProperty($obj, $property, $value) + public static function setPrivateProperty($obj, $property, $value): void { $refProperty = self::getAccessibleRefProperty($obj, $property); @@ -90,7 +91,7 @@ public static function setPrivateProperty($obj, $property, $value) * @param object|string $obj object or class name * @param string $property property name * - * @return mixed value + * @return mixed * * @throws ReflectionException */ diff --git a/tests/system/Autoloader/AutoloaderTest.php b/tests/system/Autoloader/AutoloaderTest.php index d56afbafaf84..19d482890b75 100644 --- a/tests/system/Autoloader/AutoloaderTest.php +++ b/tests/system/Autoloader/AutoloaderTest.php @@ -116,7 +116,11 @@ public function testServiceAutoLoaderFromShareInstances(): void // look for Home controller, as that should be in base repo $actual = $classLoader(Home::class); $expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php'; - $this->assertSame($expected, realpath($actual) ?: $actual); + + $resolvedPath = realpath($actual); + $actual = $resolvedPath !== false ? $resolvedPath : $actual; + + $this->assertSame($expected, $actual); } public function testServiceAutoLoader(): void @@ -130,7 +134,11 @@ public function testServiceAutoLoader(): void // look for Home controller, as that should be in base repo $actual = $classLoader(Home::class); $expected = APPPATH . 'Controllers' . DIRECTORY_SEPARATOR . 'Home.php'; - $this->assertSame($expected, realpath($actual) ?: $actual); + + $resolvedPath = realpath($actual); + $actual = $resolvedPath !== false ? $resolvedPath : $actual; + + $this->assertSame($expected, $actual); $autoloader->unregister(); } diff --git a/tests/system/Autoloader/FileLocatorCachedTest.php b/tests/system/Autoloader/FileLocatorCachedTest.php index 0aac85a8c59c..f8b5b386818c 100644 --- a/tests/system/Autoloader/FileLocatorCachedTest.php +++ b/tests/system/Autoloader/FileLocatorCachedTest.php @@ -25,7 +25,6 @@ final class FileLocatorCachedTest extends FileLocatorTest { private FileVarExportHandler $handler; - protected FileLocatorInterface $locator; public static function tearDownAfterClass(): void { diff --git a/tests/system/Autoloader/FileLocatorTest.php b/tests/system/Autoloader/FileLocatorTest.php index e3a20286acc6..90580864fc47 100644 --- a/tests/system/Autoloader/FileLocatorTest.php +++ b/tests/system/Autoloader/FileLocatorTest.php @@ -27,6 +27,9 @@ #[Group('Others')] class FileLocatorTest extends CIUnitTestCase { + /** + * @var FileLocator|FileLocatorCached + */ protected FileLocatorInterface $locator; protected function setUp(): void diff --git a/tests/system/View/DecoratorsTest.php b/tests/system/View/DecoratorsTest.php index 45993d2d606f..b9c3f6effdea 100644 --- a/tests/system/View/DecoratorsTest.php +++ b/tests/system/View/DecoratorsTest.php @@ -13,7 +13,7 @@ namespace CodeIgniter\View; -use CodeIgniter\Autoloader\FileLocator; +use CodeIgniter\Autoloader\FileLocatorInterface; use CodeIgniter\Config\Factories; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\View\Exceptions\ViewException; @@ -27,7 +27,7 @@ #[Group('Others')] final class DecoratorsTest extends CIUnitTestCase { - private FileLocator $loader; + private FileLocatorInterface $loader; private string $viewsDir; private ?\Config\View $config = null; diff --git a/tests/system/View/ParserFilterTest.php b/tests/system/View/ParserFilterTest.php index 8b122890401c..5cd7ae8f7042 100644 --- a/tests/system/View/ParserFilterTest.php +++ b/tests/system/View/ParserFilterTest.php @@ -13,7 +13,7 @@ namespace CodeIgniter\View; -use CodeIgniter\Autoloader\FileLocator; +use CodeIgniter\Autoloader\FileLocatorInterface; use CodeIgniter\Test\CIUnitTestCase; use Config\View; use PHPUnit\Framework\Attributes\Group; @@ -24,7 +24,7 @@ #[Group('Others')] final class ParserFilterTest extends CIUnitTestCase { - private FileLocator $loader; + private FileLocatorInterface $loader; private string $viewsDir; private View $config; diff --git a/tests/system/View/ParserTest.php b/tests/system/View/ParserTest.php index 1c000cf40978..33ff7880f893 100644 --- a/tests/system/View/ParserTest.php +++ b/tests/system/View/ParserTest.php @@ -13,7 +13,7 @@ namespace CodeIgniter\View; -use CodeIgniter\Autoloader\FileLocator; +use CodeIgniter\Autoloader\FileLocatorInterface; use CodeIgniter\Entity\Entity; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\View\Exceptions\ViewException; @@ -28,7 +28,7 @@ #[Group('Others')] final class ParserTest extends CIUnitTestCase { - private FileLocator $loader; + private FileLocatorInterface $loader; private string $viewsDir; private ViewConfig $config; private Parser $parser; diff --git a/tests/system/View/ViewTest.php b/tests/system/View/ViewTest.php index ad140f6a96c0..5f8c109b447b 100644 --- a/tests/system/View/ViewTest.php +++ b/tests/system/View/ViewTest.php @@ -13,7 +13,7 @@ namespace CodeIgniter\View; -use CodeIgniter\Autoloader\FileLocator; +use CodeIgniter\Autoloader\FileLocatorInterface; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\View\Exceptions\ViewException; use Config; @@ -26,7 +26,7 @@ #[Group('Others')] final class ViewTest extends CIUnitTestCase { - private FileLocator $loader; + private FileLocatorInterface $loader; private string $viewsDir; private Config\View $config;