Files
server/bin/console
2026-02-10 18:46:11 -05:00

118 lines
3.8 KiB
PHP
Executable File

#!/usr/bin/env php
<?php
/**
* Console Entry Point
*
* Bootstraps the application container and registers console commands
* from core and modules using lazy loading via Symfony Console.
*/
declare(strict_types=1);
use KTXC\Application;
use KTXC\Kernel;
use KTXC\Module\ModuleManager;
use KTXF\Module\ModuleConsoleInterface;
use Symfony\Component\Console\Application as ConsoleApplication;
use Symfony\Component\Console\Command\LazyCommand;
// Check dependencies
if (!is_dir(dirname(__DIR__).'/vendor')) {
fwrite(STDERR, "Dependencies are missing. Run 'composer install' first.\n");
exit(1);
}
require_once dirname(__DIR__).'/vendor/autoload.php';
try {
// Bootstrap the application
$projectRoot = dirname(__DIR__);
$app = new Application($projectRoot);
// Boot kernel to initialize container and modules
$app->kernel()->boot();
// Get the container
$container = $app->container();
// Create Symfony Console Application
$console = new ConsoleApplication('Ktrix Console', Kernel::VERSION);
// Collect all command classes
$commandClasses = [];
// Collect commands from modules
/** @var ModuleManager $moduleManager */
$moduleManager = $container->get(ModuleManager::class);
foreach ($moduleManager->list() as $module) {
$moduleInstance = $module->instance();
// Skip if module instance is not available
if ($moduleInstance === null) {
continue;
}
// Check if module implements console command provider
if ($moduleInstance instanceof ModuleConsoleInterface) {
try {
$commands = $moduleInstance->registerCI();
foreach ($commands as $commandClass) {
if (!class_exists($commandClass)) {
fwrite(STDERR, "Warning: Command class not found: {$commandClass}\n");
continue;
}
$commandClasses[] = $commandClass;
}
} catch (\Throwable $e) {
fwrite(STDERR, "Warning: Failed to load commands from module {$module->handle()}: {$e->getMessage()}\n");
}
}
}
// Register commands using lazy loading
foreach ($commandClasses as $commandClass) {
try {
// Use reflection to read #[AsCommand] attribute without instantiation
$reflection = new \ReflectionClass($commandClass);
$attributes = $reflection->getAttributes(\Symfony\Component\Console\Attribute\AsCommand::class);
if (empty($attributes)) {
fwrite(STDERR, "Warning: Command {$commandClass} missing #[AsCommand] attribute\n");
continue;
}
// Get attribute instance
/** @var \Symfony\Component\Console\Attribute\AsCommand $commandAttr */
$commandAttr = $attributes[0]->newInstance();
// Create lazy command wrapper that defers instantiation
$lazyCommand = new LazyCommand(
$commandAttr->name,
[],
$commandAttr->description ?? '',
$commandAttr->hidden ?? false,
fn() => $container->get($commandClass) // Only instantiate when executed
);
$console->add($lazyCommand);
} catch (\Throwable $e) {
fwrite(STDERR, "Warning: Failed to register command {$commandClass}: {$e->getMessage()}\n");
}
}
// Run the console application
$exitCode = $console->run();
exit($exitCode);
} catch (\Throwable $e) {
fwrite(STDERR, "Fatal error: " . $e->getMessage() . "\n");
if (isset($app) && $app->debug()) {
fwrite(STDERR, $e->getTraceAsString() . "\n");
}
exit(1);
}