From fa84a28b4619fc2e0bf45947187c430288bc9c2b Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Thu, 29 Oct 2026 15:20:57 +0800 Subject: [PATCH] refactor: write the Utilities commands through `$this->getOutput()` --- system/Commands/Utilities/ConfigCheck.php | 17 ++++----- system/Commands/Utilities/Environment.php | 28 ++++++++------- system/Commands/Utilities/FilterCheck.php | 35 ++++++++++-------- system/Commands/Utilities/Namespaces.php | 4 +-- system/Commands/Utilities/Optimize.php | 36 ++++++++++--------- system/Commands/Utilities/PhpIniCheck.php | 3 +- system/Commands/Utilities/Publish.php | 21 +++++------ system/Commands/Utilities/Routes.php | 23 ++++++------ .../Commands/Utilities/OptimizeTest.php | 14 ++++---- 9 files changed, 98 insertions(+), 83 deletions(-) diff --git a/system/Commands/Utilities/ConfigCheck.php b/system/Commands/Utilities/ConfigCheck.php index 9010ac0504c5..a278fa9edf1a 100644 --- a/system/Commands/Utilities/ConfigCheck.php +++ b/system/Commands/Utilities/ConfigCheck.php @@ -16,8 +16,8 @@ use CodeIgniter\Cache\FactoriesCache; use CodeIgniter\CLI\AbstractCommand; use CodeIgniter\CLI\Attributes\Command; -use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\Input\Argument; +use CodeIgniter\CLI\Output; use CodeIgniter\Config\BaseConfig; use Config\Optimize; use Kint\Kint; @@ -49,18 +49,19 @@ protected function execute(array $arguments, array $options): int } $config = config($class); + $output = $this->getOutput(); if ($config === null) { - CLI::error(sprintf('Config class "%s" not found.', $class)); + $output->error(sprintf('Config class "%s" not found.', $class)); return EXIT_ERROR; } - CLI::write($this->getDump($config)); + $output->write($this->getDump($output, $config)); - CLI::newLine(); - $state = CLI::color($configCacheEnabled ? 'enabled' : 'disabled', 'green'); - CLI::write(sprintf('Config caching: %s', $state)); + $output->newLine(); + $state = $output->color($configCacheEnabled ? 'enabled' : 'disabled', 'green'); + $output->write(sprintf('Config caching: %s', $state)); return EXIT_SUCCESS; } @@ -68,13 +69,13 @@ protected function execute(array $arguments, array $options): int /** * Renders the config object using Kint when available, otherwise var_dump(). */ - private function getDump(object $config): string + private function getDump(Output $output, object $config): string { if (defined('KINT_DIR') && Kint::$enabled_mode !== false) { return $this->getKintD($config); } - return CLI::color($this->getVarDump($config), 'cyan'); + return $output->color($this->getVarDump($config), 'cyan'); } /** diff --git a/system/Commands/Utilities/Environment.php b/system/Commands/Utilities/Environment.php index 74b94776a95d..faab81128bdf 100644 --- a/system/Commands/Utilities/Environment.php +++ b/system/Commands/Utilities/Environment.php @@ -15,8 +15,8 @@ use CodeIgniter\CLI\AbstractCommand; use CodeIgniter\CLI\Attributes\Command; -use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\Input\Argument; +use CodeIgniter\CLI\Output; use CodeIgniter\Config\DotEnv; use Config\Paths; @@ -52,10 +52,12 @@ protected function execute(array $arguments, array $options): int $env = $arguments['environment']; assert(is_string($env)); + $output = $this->getOutput(); + if ($env === '') { - CLI::write(sprintf( + $output->write(sprintf( 'Your environment is currently set as %s.', - CLI::color(service('superglobals')->server('CI_ENVIRONMENT', ENVIRONMENT), 'green'), + $output->color(service('superglobals')->server('CI_ENVIRONMENT', ENVIRONMENT), 'green'), )); return EXIT_SUCCESS; @@ -64,14 +66,14 @@ protected function execute(array $arguments, array $options): int $env = strtolower($env); if ($env === 'testing') { - CLI::error('The "testing" environment is reserved for PHPUnit testing.', 'light_gray', 'red'); - CLI::error('You will not be able to run spark under a "testing" environment.', 'light_gray', 'red'); + $output->error('The "testing" environment is reserved for PHPUnit testing.', 'light_gray', 'red'); + $output->error('You will not be able to run spark under a "testing" environment.', 'light_gray', 'red'); return EXIT_ERROR; } if (! in_array($env, self::$knownTypes, true)) { - CLI::error(sprintf( + $output->error(sprintf( 'Invalid environment type "%s". Expected one of "%s".', $env, implode('" and "', self::$knownTypes), @@ -80,8 +82,8 @@ protected function execute(array $arguments, array $options): int return EXIT_ERROR; } - if (! $this->writeNewEnvironmentToEnvFile($env)) { - CLI::error('Error in writing new environment to .env file.', 'light_gray', 'red'); + if (! $this->writeNewEnvironmentToEnvFile($output, $env)) { + $output->error('Error in writing new environment to .env file.', 'light_gray', 'red'); return EXIT_ERROR; } @@ -93,8 +95,8 @@ protected function execute(array $arguments, array $options): int service('superglobals')->unsetServer('CI_ENVIRONMENT'); (new DotEnv((new Paths())->envDirectory ?? ROOTPATH))->load(); // @phpstan-ignore nullCoalesce.property - CLI::write(sprintf('Environment is successfully changed to "%s".', $env), 'green'); - CLI::write('The ENVIRONMENT constant will be changed in the next script execution.'); + $output->write(sprintf('Environment is successfully changed to "%s".', $env), 'green'); + $output->write('The ENVIRONMENT constant will be changed in the next script execution.'); return EXIT_SUCCESS; } @@ -102,15 +104,15 @@ protected function execute(array $arguments, array $options): int /** * @see https://regex101.com/r/4sSORp/1 for the regex in action */ - private function writeNewEnvironmentToEnvFile(string $newEnv): bool + private function writeNewEnvironmentToEnvFile(Output $output, string $newEnv): bool { $baseEnv = ROOTPATH . 'env'; $envFile = ((new Paths())->envDirectory ?? ROOTPATH) . '.env'; // @phpstan-ignore nullCoalesce.property if (! is_file($envFile)) { if (! is_file($baseEnv)) { - CLI::write('Both default shipped `env` file and custom `.env` are missing.', 'yellow'); - CLI::write('It is impossible to write the new environment type.', 'yellow'); + $output->write('Both default shipped `env` file and custom `.env` are missing.', 'yellow'); + $output->write('It is impossible to write the new environment type.', 'yellow'); return false; } diff --git a/system/Commands/Utilities/FilterCheck.php b/system/Commands/Utilities/FilterCheck.php index fdfa14def791..fede0a163cfb 100644 --- a/system/Commands/Utilities/FilterCheck.php +++ b/system/Commands/Utilities/FilterCheck.php @@ -15,8 +15,9 @@ use CodeIgniter\CLI\AbstractCommand; use CodeIgniter\CLI\Attributes\Command; -use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\Input\Argument; +use CodeIgniter\CLI\Output; +use CodeIgniter\CLI\Table; use CodeIgniter\Commands\Utilities\Routes\FilterCollector; /** @@ -53,19 +54,20 @@ protected function execute(array $arguments, array $options): int $filterCollector = new FilterCollector(); $filters = $filterCollector->get($method, $route); + $output = $this->getOutput(); // PageNotFoundException if ($filters['before'] === ['']) { - CLI::error(sprintf( + $output->error(sprintf( "Can't find a route: %s", - CLI::color(sprintf('"%s %s"', strtoupper($method), $route), 'black', 'light_gray'), + $output->color(sprintf('"%s %s"', strtoupper($method), $route), 'black', 'light_gray'), )); return EXIT_ERROR; } - $this->showTable($filterCollector, $filters, $method, $route); - $this->showFilterClasses($filterCollector, $method, $route); + $this->showTable($output, $filterCollector, $filters, $method, $route); + $this->showFilterClasses($output, $filterCollector, $method, $route); return EXIT_SUCCESS; } @@ -74,12 +76,13 @@ protected function execute(array $arguments, array $options): int * @param array{before: list, after: list} $filters */ private function showTable( + Output $output, FilterCollector $filterCollector, array $filters, string $method, string $route, ): void { - $merged = $this->mergeFilters($filterCollector->getRequiredFilters(), $filters); + $merged = $this->mergeFilters($output, $filterCollector->getRequiredFilters(), $filters); $thead = ['Method', 'Route', 'Before Filters', 'After Filters']; $tbody = [ @@ -91,15 +94,17 @@ private function showTable( ], ]; - CLI::table($tbody, $thead); + $output->write((new Table($tbody, $thead))->render()); } private function showFilterClasses( + Output $output, FilterCollector $filterCollector, string $method, string $route, ): void { $merged = $this->mergeFilters( + $output, $filterCollector->getRequiredFilterClasses(), $filterCollector->getClasses($method, $route), ); @@ -107,11 +112,11 @@ private function showFilterClasses( $lastPosition = array_key_last($merged); foreach ($merged as $position => $classes) { - CLI::write(sprintf('%s Filter Classes:', ucfirst($position)), 'cyan'); - CLI::write(implode(' → ', $classes)); + $output->write(sprintf('%s Filter Classes:', ucfirst($position)), 'cyan'); + $output->write(implode(' → ', $classes)); if ($position !== $lastPosition) { - CLI::newLine(); + $output->newLine(); } } } @@ -125,11 +130,11 @@ private function showFilterClasses( * * @return array{before: list, after: list} */ - private function mergeFilters(array $required, array $filters): array + private function mergeFilters(Output $output, array $required, array $filters): array { return [ - 'before' => array_merge($this->highlight($required['before']), $filters['before']), - 'after' => array_merge($filters['after'], $this->highlight($required['after'])), + 'before' => array_merge($this->highlight($output, $required['before']), $filters['before']), + 'after' => array_merge($filters['after'], $this->highlight($output, $required['after'])), ]; } @@ -140,8 +145,8 @@ private function mergeFilters(array $required, array $filters): array * * @return list */ - private function highlight(array $items): array + private function highlight(Output $output, array $items): array { - return array_map(static fn (string $item): string => CLI::color($item, 'yellow'), $items); + return array_map(static fn (string $item): string => $output->color($item, 'yellow'), $items); } } diff --git a/system/Commands/Utilities/Namespaces.php b/system/Commands/Utilities/Namespaces.php index 54b6353e4b17..a14b90e4fbec 100644 --- a/system/Commands/Utilities/Namespaces.php +++ b/system/Commands/Utilities/Namespaces.php @@ -15,8 +15,8 @@ use CodeIgniter\CLI\AbstractCommand; use CodeIgniter\CLI\Attributes\Command; -use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\Input\Option; +use CodeIgniter\CLI\Table; use Config\Autoload; /** @@ -63,7 +63,7 @@ protected function execute(array $arguments, array $options): int (int) $options['max-length'], ); - CLI::table($tbody, ['Namespace', 'Path', 'Found?']); + $this->getOutput()->write((new Table($tbody, ['Namespace', 'Path', 'Found?']))->render()); return EXIT_SUCCESS; } diff --git a/system/Commands/Utilities/Optimize.php b/system/Commands/Utilities/Optimize.php index 4c4a9aeb6f18..223fd9707df0 100644 --- a/system/Commands/Utilities/Optimize.php +++ b/system/Commands/Utilities/Optimize.php @@ -17,7 +17,7 @@ use CodeIgniter\Autoloader\FileLocatorCached; use CodeIgniter\CLI\AbstractCommand; use CodeIgniter\CLI\Attributes\Command; -use CodeIgniter\CLI\CLI; +use CodeIgniter\CLI\Output; use CodeIgniter\Exceptions\RuntimeException; use CodeIgniter\Publisher\Publisher; @@ -29,20 +29,22 @@ final class Optimize extends AbstractCommand { protected function execute(array $arguments, array $options): int { + $output = $this->getOutput(); + try { - $this->enableCaching(); - $this->clearCache(); - $this->removeDevPackages(); + $this->enableCaching($output); + $this->clearCache($output); + $this->removeDevPackages($output); return EXIT_SUCCESS; } catch (RuntimeException) { - CLI::error('The "spark optimize" failed.'); + $output->error('The "spark optimize" failed.'); return EXIT_ERROR; } } - private function clearCache(): void + private function clearCache(Output $output): void { $locator = service('locator'); @@ -51,29 +53,29 @@ private function clearCache(): void } $locator->deleteCache(); - CLI::write('Removed FileLocatorCache.', 'green'); + $output->write('Removed FileLocatorCache.', 'green'); - $this->removeFile(WRITEPATH . 'cache/FactoriesCache_config'); + $this->removeFile($output, WRITEPATH . 'cache/FactoriesCache_config'); } - private function removeFile(string $file): void + private function removeFile(Output $output, string $file): void { if (! is_file($file)) { return; } if (unlink($file)) { - CLI::write(sprintf('Removed "%s".', clean_path($file)), 'green'); + $output->write(sprintf('Removed "%s".', clean_path($file)), 'green'); return; } - CLI::error(sprintf('Error in removing file: %s', clean_path($file))); + $output->error(sprintf('Error in removing file: %s', clean_path($file))); throw new RuntimeException(__METHOD__); } - private function enableCaching(): void + private function enableCaching(Output $output): void { $publisher = new Publisher(APPPATH, APPPATH); @@ -88,7 +90,7 @@ private function enableCaching(): void ); if ($result) { - CLI::write( + $output->write( 'Config Caching and FileLocator Caching are enabled in "app/Config/Optimize.php".', 'green', ); @@ -96,12 +98,12 @@ private function enableCaching(): void return; } - CLI::error(sprintf('Error in updating file: %s', clean_path($config))); + $output->error(sprintf('Error in updating file: %s', clean_path($config))); throw new RuntimeException(__METHOD__); } - private function removeDevPackages(): void + private function removeDevPackages(Output $output): void { if (! defined('VENDORPATH')) { return; @@ -111,12 +113,12 @@ private function removeDevPackages(): void passthru('composer install --no-dev', $status); if ($status === 0) { - CLI::write('Removed Composer dev packages.', 'green'); + $output->write('Removed Composer dev packages.', 'green'); return; } - CLI::error('Error in removing Composer dev packages.'); + $output->error('Error in removing Composer dev packages.'); throw new RuntimeException(__METHOD__); } diff --git a/system/Commands/Utilities/PhpIniCheck.php b/system/Commands/Utilities/PhpIniCheck.php index c8207df5999e..83f9ab30616d 100644 --- a/system/Commands/Utilities/PhpIniCheck.php +++ b/system/Commands/Utilities/PhpIniCheck.php @@ -15,7 +15,6 @@ use CodeIgniter\CLI\AbstractCommand; use CodeIgniter\CLI\Attributes\Command; -use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\Input\Argument; use CodeIgniter\Security\CheckPhpIni; @@ -45,7 +44,7 @@ protected function execute(array $arguments, array $options): int $section = $arguments['section']; if ($section !== '' && $section !== 'opcache') { - CLI::error('You must specify a correct argument.'); + $this->getOutput()->error('You must specify a correct argument.'); return EXIT_ERROR; } diff --git a/system/Commands/Utilities/Publish.php b/system/Commands/Utilities/Publish.php index 2a7fc52e239e..e13d6b9c219b 100644 --- a/system/Commands/Utilities/Publish.php +++ b/system/Commands/Utilities/Publish.php @@ -15,9 +15,9 @@ use CodeIgniter\CLI\AbstractCommand; use CodeIgniter\CLI\Attributes\Command; -use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\Input\Argument; use CodeIgniter\CLI\Input\Option; +use CodeIgniter\CLI\Output; use CodeIgniter\Publisher\Publisher; /** @@ -54,9 +54,10 @@ protected function execute(array $arguments, array $options): int $namespace = $options['namespace']; $publishers = Publisher::discover($directory, $namespace); + $output = $this->getOutput(); if ($publishers === []) { - CLI::write($namespace === '' + $output->write($namespace === '' ? lang('Publisher.publishMissing', [$directory]) : lang('Publisher.publishMissingNamespace', [$directory, $namespace])); @@ -65,9 +66,9 @@ protected function execute(array $arguments, array $options): int $failed = array_reduce( $publishers, - function (bool $carry, Publisher $publisher): bool { + function (bool $carry, Publisher $publisher) use ($output): bool { // Kept out of the return expression so `||` cannot skip publishing after a failure. - $published = $this->publishOne($publisher); + $published = $this->publishOne($output, $publisher); return $carry || ! $published; }, @@ -77,10 +78,10 @@ function (bool $carry, Publisher $publisher): bool { return (int) $failed; } - private function publishOne(Publisher $publisher): bool + private function publishOne(Output $output, Publisher $publisher): bool { if ($publisher->publish()) { - CLI::write(lang('Publisher.publishSuccess', [ + $output->write(lang('Publisher.publishSuccess', [ $publisher::class, count($publisher->getPublished()), $publisher->getDestination(), @@ -89,15 +90,15 @@ private function publishOne(Publisher $publisher): bool return true; } - CLI::error(lang('Publisher.publishFailure', [ + $output->error(lang('Publisher.publishFailure', [ $publisher::class, $publisher->getDestination(), ]), 'light_gray', 'red'); foreach ($publisher->getErrors() as $file => $exception) { - CLI::write($file); - CLI::error($exception->getMessage()); - CLI::newLine(); + $output->write($file); + $output->error($exception->getMessage()); + $output->newLine(); } return false; diff --git a/system/Commands/Utilities/Routes.php b/system/Commands/Utilities/Routes.php index 62c495985aa5..4db0a56a01fd 100644 --- a/system/Commands/Utilities/Routes.php +++ b/system/Commands/Utilities/Routes.php @@ -15,8 +15,9 @@ use CodeIgniter\CLI\AbstractCommand; use CodeIgniter\CLI\Attributes\Command; -use CodeIgniter\CLI\CLI; use CodeIgniter\CLI\Input\Option; +use CodeIgniter\CLI\Output; +use CodeIgniter\CLI\Table; use CodeIgniter\Commands\Utilities\Routes\AutoRouteCollector; use CodeIgniter\Commands\Utilities\Routes\AutoRouterImproved\AutoRouteCollector as AutoRouteCollectorImproved; use CodeIgniter\Commands\Utilities\Routes\FilterCollector; @@ -83,20 +84,22 @@ protected function execute(array $arguments, array $options): int usort($tbody, static fn (array $route1, array $route2): int => strcmp($route1[3], $route2[3])); } + $output = $this->getOutput(); + if ($host !== '') { - CLI::write(sprintf('Host: %s', $host)); + $output->write(sprintf('Host: %s', $host)); } - CLI::table($tbody, [ + $output->write((new Table($tbody, [ 'Method', 'Route', 'Name', $sortByHandler ? 'Handler ↓' : 'Handler', 'Before Filters', 'After Filters', - ]); + ]))->render()); - return $this->showRequiredFilters(); + return $this->showRequiredFilters($output); } /** @@ -194,12 +197,12 @@ private function basenames(array $filters): string return implode(' ', array_map(class_basename(...), $filters)); } - private function showRequiredFilters(): int + private function showRequiredFilters(Output $output): int { $required = (new FilterCollector())->getRequiredFilters(); - CLI::write(sprintf('Required Before Filters: %s', $this->highlight($required['before']))); - CLI::write(sprintf(' Required After Filters: %s', $this->highlight($required['after']))); + $output->write(sprintf('Required Before Filters: %s', $this->highlight($output, $required['before']))); + $output->write(sprintf(' Required After Filters: %s', $this->highlight($output, $required['after']))); return EXIT_SUCCESS; } @@ -207,10 +210,10 @@ private function showRequiredFilters(): int /** * @param list $filters */ - private function highlight(array $filters): string + private function highlight(Output $output, array $filters): string { return implode(', ', array_map( - static fn (string $filter): string => CLI::color($filter, 'yellow'), + static fn (string $filter): string => $output->color($filter, 'yellow'), $filters, )); } diff --git a/tests/system/Commands/Utilities/OptimizeTest.php b/tests/system/Commands/Utilities/OptimizeTest.php index 7239a443bb12..72fb93b07e09 100644 --- a/tests/system/Commands/Utilities/OptimizeTest.php +++ b/tests/system/Commands/Utilities/OptimizeTest.php @@ -17,6 +17,7 @@ use CodeIgniter\Autoloader\FileLocator; use CodeIgniter\Autoloader\FileLocatorCached; use CodeIgniter\Cache\FactoriesCache\FileVarExportHandler; +use CodeIgniter\CLI\Output; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\ReflectionHelper; use CodeIgniter\Test\StreamFilterTrait; @@ -72,7 +73,7 @@ protected function tearDown(): void } /** - * @return Closure(string): void + * @return Closure(Output, string): void */ private function getRemoveFile(): Closure { @@ -80,7 +81,7 @@ private function getRemoveFile(): Closure } /** - * @return Closure(): void + * @return Closure(Output): void */ private function getClearCache(): Closure { @@ -91,7 +92,7 @@ public function testRemoveFileDeletesTheFile(): void { file_put_contents($this->file, 'getRemoveFile())($this->file); + ($this->getRemoveFile())(new Output(), $this->file); $this->assertFileDoesNotExist($this->file); $this->assertStringContainsString('Removed', $this->getStreamFilterBuffer()); @@ -101,7 +102,7 @@ public function testRemoveFileDoesNothingWhenFileIsAbsent(): void { $this->assertFileDoesNotExist($this->file); - ($this->getRemoveFile())($this->file); + ($this->getRemoveFile())(new Output(), $this->file); $this->assertSame('', $this->getStreamFilterBuffer()); } @@ -112,7 +113,7 @@ public function testClearCacheDiscardsSharedLocatorCache(): void $locator->search('Config/App'); Services::injectMock('locator', $locator); - ($this->getClearCache())(); + ($this->getClearCache())(new Output()); $locator->search('Config/Cache'); $locator->__destruct(); @@ -142,6 +143,7 @@ public function testClearCacheDeletesTheFileWithoutSharedLocatorCache(): void require {$escapedBootstrap}; use CodeIgniter\\Cache\\FactoriesCache\\FileVarExportHandler; + use CodeIgniter\\CLI\\Output; use CodeIgniter\\Commands\\Utilities\\Optimize; use CodeIgniter\\Test\\ReflectionHelper; @@ -152,7 +154,7 @@ public function testClearCacheDeletesTheFileWithoutSharedLocatorCache(): void new Optimize(service('commands')), 'clearCache' ); - \$clearCache(); + \$clearCache(new Output()); if (\$handler->get('FileLocatorCache') !== false) { exit(1);