From e3f23539ef50a751abdab977f79c9c96ca18e6cb Mon Sep 17 00:00:00 2001 From: rtcoder Date: Wed, 17 Jun 2026 15:43:20 +0200 Subject: [PATCH] Add CLI diagnostic commands --- README.md | 25 ++++++++++ REFACTORING.md | 1 + docs/index.html | 7 ++- src/Console/Commands/About.php | 49 +++++++++++++++++++ src/Console/Commands/DbCheck.php | 41 ++++++++++++++++ src/Console/Commands/EnvCheck.php | 73 +++++++++++++++++++++++++++++ src/Console/Commands/ModuleList.php | 44 +++++++++++++++++ src/Console/Commands/Test.php | 40 ++++++++++++++++ 8 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 src/Console/Commands/About.php create mode 100644 src/Console/Commands/DbCheck.php create mode 100644 src/Console/Commands/EnvCheck.php create mode 100644 src/Console/Commands/ModuleList.php create mode 100644 src/Console/Commands/Test.php diff --git a/README.md b/README.md index 0805042..7ee2c24 100644 --- a/README.md +++ b/README.md @@ -353,12 +353,37 @@ List registered API routes: ./shift route:list ``` +Run the test suite: + +```sh +./shift test +``` + Run the example module command: ```sh ./shift health ``` +Inspect framework/runtime information: + +```sh +./shift about +``` + +Check local environment and database configuration: + +```sh +./shift env:check +./shift db:check +``` + +List discovered modules: + +```sh +./shift module:list +``` + Generate module scaffolding: ```sh diff --git a/REFACTORING.md b/REFACTORING.md index 7ca3cd4..9b0d00e 100644 --- a/REFACTORING.md +++ b/REFACTORING.md @@ -30,6 +30,7 @@ ShiftPHP is moving toward an API-only modular monolith. View templates, compiled - [x] `.env` loading for application configuration. - [x] Native PDO database configuration, lazy connection, and basic query API. - [x] Fluent query builder and attribute-driven database models. +- [x] CLI diagnostics for tests, runtime info, environment, database, and modules. - [x] Removal of view storage and example page assets from runtime. - [x] Removal of legacy `application/controllers` and `application/routes.php`. - [x] Domain-oriented framework namespaces: diff --git a/docs/index.html b/docs/index.html index ac2979b..0f45edd 100644 --- a/docs/index.html +++ b/docs/index.html @@ -474,7 +474,12 @@

CLI

The CLI entry point is shift. Built-in commands live under Console\Commands, and module commands are loaded from module command mappings.

./shift route:list
-./shift health
+./shift test +./shift health +./shift about +./shift env:check +./shift db:check +./shift module:list

Create commands scaffold modules and module-owned classes:

diff --git a/src/Console/Commands/About.php b/src/Console/Commands/About.php new file mode 100644 index 0000000..6f1d14c --- /dev/null +++ b/src/Console/Commands/About.php @@ -0,0 +1,49 @@ +composer(); + + $cli->table(['Name', 'Value'], [ + ['Framework', $composer['name'] ?? 'ShiftPHP'], + ['Description', $composer['description'] ?? 'API framework'], + ['PHP', PHP_VERSION], + ['Environment', (string) Env::get('APP_ENV', 'local')], + ['Root', APP_ROOT], + ['Application', APP_PATH], + ['CLI', APP_ROOT . '/shift'], + ]); + } + + public function getHelp(): string + { + return 'Usage: ./shift about'; + } + + public function getDescription(): string + { + return 'Show framework and runtime information.'; + } + + private function composer(): array + { + $path = APP_ROOT . '/composer.json'; + + if (!is_file($path)) { + return []; + } + + $data = json_decode((string) file_get_contents($path), true); + + return is_array($data) ? $data : []; + } +} diff --git a/src/Console/Commands/DbCheck.php b/src/Console/Commands/DbCheck.php new file mode 100644 index 0000000..593174b --- /dev/null +++ b/src/Console/Commands/DbCheck.php @@ -0,0 +1,41 @@ +info('Checking database connection...'); + $cli->debug('Driver: ' . $config->driver); + $cli->debug('Database: ' . ($config->database !== '' ? $config->database : '(not configured)')); + + try { + (new Database($config))->pdo(); + $cli->success('Database connection OK.'); + } catch (DatabaseException | Throwable $exception) { + $cli->error('Database connection failed.'); + $cli->debug($exception->getMessage()); + } + } + + public function getHelp(): string + { + return 'Usage: ./shift db:check'; + } + + public function getDescription(): string + { + return 'Check database connectivity from environment configuration.'; + } +} diff --git a/src/Console/Commands/EnvCheck.php b/src/Console/Commands/EnvCheck.php new file mode 100644 index 0000000..2cf7f65 --- /dev/null +++ b/src/Console/Commands/EnvCheck.php @@ -0,0 +1,73 @@ + */ + private array $required = [ + 'APP_ENV', + 'DB_CONNECTION', + 'DB_DATABASE', + ]; + + public function execute(mixed ...$args): void + { + $cli = new Cli(); + $rows = []; + $missing = []; + + foreach ($this->required as $key) { + $value = Env::get($key); + $ok = $value !== null && $value !== ''; + + if (!$ok) { + $missing[] = $key; + } + + $rows[] = [ + $key, + $ok ? 'ok' : 'missing', + $this->mask($key, $value), + ]; + } + + $envFile = is_file(APP_ROOT . '/.env') ? 'yes' : 'no'; + $cli->debug('.env file: ' . $envFile); + $cli->table(['Variable', 'Status', 'Value'], $rows); + + if ($missing === []) { + $cli->success('Environment configuration OK.'); + return; + } + + $cli->warning('Missing required environment variables: ' . implode(', ', $missing)); + } + + public function getHelp(): string + { + return 'Usage: ./shift env:check'; + } + + public function getDescription(): string + { + return 'Validate required environment variables.'; + } + + private function mask(string $key, mixed $value): string + { + if ($value === null || $value === '') { + return ''; + } + + if (str_contains($key, 'PASSWORD') || str_contains($key, 'SECRET') || str_contains($key, 'TOKEN')) { + return '***'; + } + + return (string) $value; + } +} diff --git a/src/Console/Commands/ModuleList.php b/src/Console/Commands/ModuleList.php new file mode 100644 index 0000000..9642760 --- /dev/null +++ b/src/Console/Commands/ModuleList.php @@ -0,0 +1,44 @@ +load(); + $rows = []; + + foreach ($loader->getModules() as $module) { + $config = $loader->getConfig($module->getName()); + $rows[] = [ + $module->getName(), + $module::class, + $config === [] ? 'no' : 'yes', + count($module->getCommandMappings()), + ]; + } + + if ($rows === []) { + $cli->warning('No modules found.'); + return; + } + + $cli->table(['Name', 'Class', 'Config', 'Commands'], $rows); + } + + public function getHelp(): string + { + return 'Usage: ./shift module:list'; + } + + public function getDescription(): string + { + return 'List discovered modules.'; + } +} diff --git a/src/Console/Commands/Test.php b/src/Console/Commands/Test.php new file mode 100644 index 0000000..c836539 --- /dev/null +++ b/src/Console/Commands/Test.php @@ -0,0 +1,40 @@ +error('Test runner not found: ' . $testFile); + return; + } + + $command = 'XDEBUG_MODE=off ' . escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($testFile); + passthru($command, $exitCode); + + if ($exitCode === 0) { + $cli->success('Test suite passed.'); + return; + } + + $cli->error('Test suite failed.'); + } + + public function getHelp(): string + { + return 'Usage: ./shift test'; + } + + public function getDescription(): string + { + return 'Run the project test suite.'; + } +}