Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions REFACTORING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
7 changes: 6 additions & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,12 @@ <h2>CLI</h2>
<p>The CLI entry point is <code>shift</code>. Built-in commands live under <code>Console\Commands</code>, and module commands are loaded from module command mappings.</p>

<pre><code>./shift route:list
./shift health</code></pre>
./shift test
./shift health
./shift about
./shift env:check
./shift db:check
./shift module:list</code></pre>

<p>Create commands scaffold modules and module-owned classes:</p>

Expand Down
49 changes: 49 additions & 0 deletions src/Console/Commands/About.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Console\Commands;

use Shift\Config\Env;
use Shift\Console\Cli;
use Shift\Console\CommandInterface;

class About implements CommandInterface
{
public function execute(mixed ...$args): void
{
$cli = new Cli();
$composer = $this->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 : [];
}
}
41 changes: 41 additions & 0 deletions src/Console/Commands/DbCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Console\Commands;

use Shift\Console\Cli;
use Shift\Console\CommandInterface;
use Shift\Database\Database;
use Shift\Database\DatabaseConfig;
use Shift\Database\DatabaseException;
use Throwable;

class DbCheck implements CommandInterface
{
public function execute(mixed ...$args): void
{
$cli = new Cli();
$config = DatabaseConfig::fromEnv();

$cli->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.';
}
}
73 changes: 73 additions & 0 deletions src/Console/Commands/EnvCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Console\Commands;

use Shift\Config\Env;
use Shift\Console\Cli;
use Shift\Console\CommandInterface;

class EnvCheck implements CommandInterface
{
/** @var list<string> */
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;
}
}
44 changes: 44 additions & 0 deletions src/Console/Commands/ModuleList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace Console\Commands;

use Shift\Console\Cli;
use Shift\Console\CommandInterface;
use Shift\Modules\ModuleLoader;

class ModuleList implements CommandInterface
{
public function execute(mixed ...$args): void
{
$cli = new Cli();
$loader = (new ModuleLoader())->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.';
}
}
40 changes: 40 additions & 0 deletions src/Console/Commands/Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace Console\Commands;

use Shift\Console\Cli;
use Shift\Console\CommandInterface;

class Test implements CommandInterface
{
public function execute(mixed ...$args): void
{
$cli = new Cli();
$testFile = APP_ROOT . '/tests/ApiCoreTest.php';

if (!is_file($testFile)) {
$cli->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.';
}
}
Loading