diff --git a/system/Autoloader/FileLocator.php b/system/Autoloader/FileLocator.php index 844ac7fc4cc9..13ecb817a56c 100644 --- a/system/Autoloader/FileLocator.php +++ b/system/Autoloader/FileLocator.php @@ -266,12 +266,6 @@ protected function getNamespaces() return array_merge($namespaces, $system); } - /** - * Find the qualified name of a file according to - * the namespace of the first matched namespace path. - * - * @return false|string The qualified name or false if the path is not found - */ public function findQualifiedNameFromPath(string $path) { $resolvedPath = realpath($path); @@ -299,7 +293,9 @@ public function findQualifiedNameFromPath(string $path) ), '\\', ); + // Remove the file extension (.php) + /** @var class-string */ $className = mb_substr($className, 0, -4); if (in_array($className, $this->invalidClassnames, true)) { diff --git a/system/Autoloader/FileLocatorInterface.php b/system/Autoloader/FileLocatorInterface.php index 3a1112dd480e..8f6129abee17 100644 --- a/system/Autoloader/FileLocatorInterface.php +++ b/system/Autoloader/FileLocatorInterface.php @@ -62,7 +62,7 @@ public function search(string $path, string $ext = 'php', bool $prioritizeApp = * Find the qualified name of a file according to * the namespace of the first matched namespace path. * - * @return false|string The qualified name or false if the path is not found + * @return class-string|false The qualified name or false if the path is not found */ public function findQualifiedNameFromPath(string $path); diff --git a/system/CLI/CLI.php b/system/CLI/CLI.php index a21996507d55..b8ea4714f39b 100644 --- a/system/CLI/CLI.php +++ b/system/CLI/CLI.php @@ -107,12 +107,12 @@ class CLI /** * List of array segments. * - * @var array + * @var list */ protected static $segments = []; /** - * @var array + * @var array */ protected static $options = []; @@ -944,6 +944,8 @@ public static function getSegment(int $index) /** * Returns the raw array of segments found. + * + * @return list */ public static function getSegments(): array { @@ -971,6 +973,8 @@ public static function getOption(string $name) /** * Returns the raw array of options found. + * + * @return array */ public static function getOptions(): array { diff --git a/system/CLI/Commands.php b/system/CLI/Commands.php index 732cb370382d..aecd22c3f348 100644 --- a/system/CLI/Commands.php +++ b/system/CLI/Commands.php @@ -21,13 +21,15 @@ /** * Core functionality for running, listing, etc commands. + * + * @phpstan-type commands_list array, 'file': string, 'group': string,'description': string}> */ class Commands { /** * The found commands. * - * @var array + * @var commands_list */ protected $commands = []; @@ -52,6 +54,8 @@ public function __construct($logger = null) /** * Runs a command given * + * @param array $params + * * @return int Exit code */ public function run(string $command, array $params) @@ -77,7 +81,7 @@ public function run(string $command, array $params) /** * Provide access to the list of commands. * - * @return array + * @return commands_list */ public function getCommands() { @@ -96,7 +100,7 @@ public function discoverCommands() return; } - /** @var FileLocatorInterface $locator */ + /** @var FileLocatorInterface */ $locator = service('locator'); $files = $locator->listFiles('Commands/'); @@ -109,6 +113,7 @@ public function discoverCommands() // Loop over each file checking to see if a command with that // alias exists in the class. foreach ($files as $file) { + /** @var class-string|false */ $className = $locator->findQualifiedNameFromPath($file); if ($className === false || ! class_exists($className)) { @@ -122,7 +127,6 @@ public function discoverCommands() continue; } - /** @var BaseCommand $class */ $class = new $className($this->logger, $this); if (isset($class->group) && ! isset($this->commands[$class->name])) { @@ -146,6 +150,8 @@ public function discoverCommands() /** * Verifies if the command being sought is found * in the commands list. + * + * @param commands_list $commands */ public function verifyCommand(string $command, array $commands): bool { @@ -153,9 +159,9 @@ public function verifyCommand(string $command, array $commands): bool return true; } - $message = lang('CLI.commandNotFound', [$command]); - + $message = lang('CLI.commandNotFound', [$command]); $alternatives = $this->getCommandAlternatives($command, $commands); + if ($alternatives !== []) { if (count($alternatives) === 1) { $message .= "\n\n" . lang('CLI.altCommandSingular') . "\n "; @@ -175,11 +181,17 @@ public function verifyCommand(string $command, array $commands): bool /** * Finds alternative of `$name` among collection * of commands. + * + * @param commands_list $collection + * + * @return list */ protected function getCommandAlternatives(string $name, array $collection): array { + /** @var array */ $alternatives = []; + /** @var string $commandName */ foreach (array_keys($collection) as $commandName) { $lev = levenshtein($name, $commandName); diff --git a/system/CLI/Console.php b/system/CLI/Console.php index 5cb8b57a0ad3..325eaa7b455c 100644 --- a/system/CLI/Console.php +++ b/system/CLI/Console.php @@ -38,14 +38,13 @@ public function run() $appConfig = config(App::class); Services::createRequest($appConfig, true); // Load Routes - Services::routes()->loadRoutes(); + service('routes')->loadRoutes(); - $runner = Services::commands(); $params = array_merge(CLI::getSegments(), CLI::getOptions()); $params = $this->parseParamsForHelpOption($params); $command = array_shift($params) ?? 'list'; - return $runner->run($command, $params); + return service('commands')->run($command, $params); } /** @@ -75,6 +74,8 @@ public function showHeader(bool $suppress = false) * If present, it will be found as `['help' => null]`. * We'll remove that as an option from `$params` and * unshift it as argument instead. + * + * @param array $params */ private function parseParamsForHelpOption(array $params): array { diff --git a/system/Common.php b/system/Common.php index fafaccd31921..910ec9b1cc28 100644 --- a/system/Common.php +++ b/system/Common.php @@ -124,7 +124,6 @@ function clean_path(string $path): string */ function command(string $command) { - $runner = service('commands'); $regexString = '([^\s]+?)(?:\s|(? */ $params = []; + $command = array_shift($args); $optionValue = false; foreach ($args as $i => $arg) { @@ -187,7 +187,7 @@ function command(string $command) } ob_start(); - $runner->run($command, $params); + service('commands')->run($command, $params); return ob_get_clean(); } diff --git a/system/Config/BaseConfig.php b/system/Config/BaseConfig.php index e056664e24be..ce6594d45d36 100644 --- a/system/Config/BaseConfig.php +++ b/system/Config/BaseConfig.php @@ -11,6 +11,7 @@ namespace CodeIgniter\Config; +use CodeIgniter\Autoloader\FileLocatorInterface; use CodeIgniter\Exceptions\ConfigException; use CodeIgniter\Exceptions\RuntimeException; use Config\Encryption; @@ -252,6 +253,7 @@ protected function registerProperties() static::$discovering = true; + /** @var FileLocatorInterface */ $locator = service('locator'); $registrarsFiles = $locator->search('Config/Registrar.php'); diff --git a/system/Config/Factories.php b/system/Config/Factories.php index 797f9fd9c7de..dc74c45e76f7 100644 --- a/system/Config/Factories.php +++ b/system/Config/Factories.php @@ -13,6 +13,7 @@ namespace CodeIgniter\Config; +use CodeIgniter\Autoloader\FileLocatorInterface; use CodeIgniter\Database\ConnectionInterface; use CodeIgniter\Exceptions\InvalidArgumentException; use CodeIgniter\Model; @@ -299,6 +300,7 @@ class_exists($alias, false) } // Have to do this the hard way... + /** @var FileLocatorInterface */ $locator = service('locator'); // Check if the class alias was namespaced diff --git a/system/Publisher/Publisher.php b/system/Publisher/Publisher.php index d65765947db1..8d9f6b72327a 100644 --- a/system/Publisher/Publisher.php +++ b/system/Publisher/Publisher.php @@ -109,7 +109,7 @@ final public static function discover(string $directory = 'Publishers', string $ self::$discovered[$key] = []; - /** @var FileLocatorInterface $locator */ + /** @var FileLocatorInterface */ $locator = service('locator'); $files = $namespace === '' @@ -125,6 +125,7 @@ final public static function discover(string $directory = 'Publishers', string $ $className = $locator->findQualifiedNameFromPath($file); if ($className !== false && class_exists($className) && is_a($className, self::class, true)) { + /** @var class-string $className */ self::$discovered[$key][] = new $className(); } } diff --git a/tests/system/CLI/CLITest.php b/tests/system/CLI/CLITest.php index 5c17fd820044..b29ef7bfc12f 100644 --- a/tests/system/CLI/CLITest.php +++ b/tests/system/CLI/CLITest.php @@ -539,6 +539,7 @@ public function testParseCommandMultipleOptions(): void $this->assertSame(['parm' => 'pvalue', 'p2' => null, 'p3' => 'value 3'], CLI::getOptions()); $this->assertSame('pvalue', CLI::getOption('parm')); + $this->assertTrue(CLI::getOption('p2')); $this->assertSame('-parm pvalue -p2 -p3 "value 3" ', CLI::getOptionString()); $this->assertSame('-parm pvalue -p2 -p3 "value 3"', CLI::getOptionString(false, true)); $this->assertSame('--parm pvalue --p2 --p3 "value 3" ', CLI::getOptionString(true)); diff --git a/tests/system/CLI/ConsoleTest.php b/tests/system/CLI/ConsoleTest.php index 5962f3a9b21e..4d66a48447b4 100644 --- a/tests/system/CLI/ConsoleTest.php +++ b/tests/system/CLI/ConsoleTest.php @@ -169,7 +169,7 @@ public function testHelpArgumentAndHelpOptionCombined(): void } /** - * @param array $command + * @param string ...$command */ protected function initCLI(...$command): void { diff --git a/tests/system/Commands/CommandTest.php b/tests/system/Commands/CommandTest.php index 84fa6ecac993..1c6e81033e63 100644 --- a/tests/system/Commands/CommandTest.php +++ b/tests/system/Commands/CommandTest.php @@ -19,6 +19,7 @@ use CodeIgniter\Test\StreamFilterTrait; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; +use Tests\Support\Commands\AppInfo; use Tests\Support\Commands\ParamsReveal; /** @@ -74,9 +75,12 @@ public function testShowError(): void { command('app:info'); $commands = $this->commands->getCommands(); - $command = new $commands['app:info']['class']($this->logger, $this->commands); + + /** @var AppInfo */ + $command = new $commands['app:info']['class']($this->logger, $this->commands); $command->helpme(); + $this->assertStringContainsString('Displays basic usage information.', $this->getBuffer()); } @@ -84,9 +88,12 @@ public function testCommandCall(): void { command('app:info'); $commands = $this->commands->getCommands(); - $command = new $commands['app:info']['class']($this->logger, $this->commands); + + /** @var AppInfo */ + $command = new $commands['app:info']['class']($this->logger, $this->commands); $command->bomb(); + $this->assertStringContainsString('Invalid "background" color:', $this->getBuffer()); } diff --git a/utils/phpstan-baseline/codeigniter.superglobalAccessAssign.neon b/utils/phpstan-baseline/codeigniter.superglobalAccessAssign.neon index 365455768963..665b2a986ce5 100644 --- a/utils/phpstan-baseline/codeigniter.superglobalAccessAssign.neon +++ b/utils/phpstan-baseline/codeigniter.superglobalAccessAssign.neon @@ -58,7 +58,7 @@ parameters: path: ../../tests/system/CLI/ConsoleTest.php - - message: '#^Assigning non\-empty\-array\ directly on offset ''argv'' of \$_SERVER is discouraged\.$#' + message: '#^Assigning non\-empty\-array\ directly on offset ''argv'' of \$_SERVER is discouraged\.$#' count: 1 path: ../../tests/system/CLI/ConsoleTest.php diff --git a/utils/phpstan-baseline/missingType.iterableValue.neon b/utils/phpstan-baseline/missingType.iterableValue.neon index 1dc74c4561e9..9f19deb6ea78 100644 --- a/utils/phpstan-baseline/missingType.iterableValue.neon +++ b/utils/phpstan-baseline/missingType.iterableValue.neon @@ -1,4 +1,4 @@ -# total 1671 errors +# total 1659 errors parameters: ignoreErrors: @@ -182,16 +182,6 @@ parameters: count: 1 path: ../../system/BaseModel.php - - - message: '#^Method CodeIgniter\\CLI\\CLI\:\:getOptions\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/CLI.php - - - - message: '#^Method CodeIgniter\\CLI\\CLI\:\:getSegments\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/CLI.php - - message: '#^Method CodeIgniter\\CLI\\CLI\:\:isZeroOptions\(\) has parameter \$options with no value type specified in iterable type array\.$#' count: 1 @@ -247,51 +237,6 @@ parameters: count: 1 path: ../../system/CLI/CLI.php - - - message: '#^Property CodeIgniter\\CLI\\CLI\:\:\$options type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/CLI.php - - - - message: '#^Property CodeIgniter\\CLI\\CLI\:\:\$segments type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/CLI.php - - - - message: '#^Method CodeIgniter\\CLI\\Commands\:\:getCommandAlternatives\(\) has parameter \$collection with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/Commands.php - - - - message: '#^Method CodeIgniter\\CLI\\Commands\:\:getCommandAlternatives\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/Commands.php - - - - message: '#^Method CodeIgniter\\CLI\\Commands\:\:getCommands\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/Commands.php - - - - message: '#^Method CodeIgniter\\CLI\\Commands\:\:run\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/Commands.php - - - - message: '#^Method CodeIgniter\\CLI\\Commands\:\:verifyCommand\(\) has parameter \$commands with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/Commands.php - - - - message: '#^Property CodeIgniter\\CLI\\Commands\:\:\$commands type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/Commands.php - - - - message: '#^Method CodeIgniter\\CLI\\Console\:\:parseParamsForHelpOption\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/CLI/Console.php - - message: '#^Method CodeIgniter\\CLI\\Console\:\:parseParamsForHelpOption\(\) return type has no value type specified in iterable type array\.$#' count: 1 @@ -6652,11 +6597,6 @@ parameters: count: 1 path: ../../tests/system/CLI/CLITest.php - - - message: '#^Method CodeIgniter\\CLI\\ConsoleTest\:\:initCLI\(\) has parameter \$command with no value type specified in iterable type array\.$#' - count: 1 - path: ../../tests/system/CLI/ConsoleTest.php - - message: '#^Method CodeIgniter\\Cache\\Handlers\\BaseHandlerTest\:\:provideValidateKeyInvalidType\(\) return type has no value type specified in iterable type iterable\.$#' count: 1