641 lines
23 KiB
PHP
641 lines
23 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Console;
|
|
|
|
use App\Framework\Config\AppConfig;
|
|
use App\Framework\Console\Components\ConsoleTUI;
|
|
use App\Framework\Console\Components\TuiCommandExecutor;
|
|
use App\Framework\Console\Components\TuiInputHandler;
|
|
use App\Framework\Console\Components\TuiRenderer;
|
|
use App\Framework\Console\Components\TuiState;
|
|
use App\Framework\Console\ErrorRecovery\CommandSuggestionEngine;
|
|
use App\Framework\Console\ErrorRecovery\ConsoleErrorHandler;
|
|
use App\Framework\Console\ErrorRecovery\ErrorRecoveryService;
|
|
use App\Framework\Console\Exceptions\CommandNotFoundException;
|
|
use App\Framework\DI\Container;
|
|
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
|
use App\Framework\Exception\ErrorCode;
|
|
use App\Framework\Exception\FrameworkException;
|
|
use App\Framework\Logging\Logger;
|
|
use App\Framework\Logging\ValueObjects\LogContext;
|
|
use Throwable;
|
|
|
|
final class ConsoleApplication
|
|
{
|
|
private ConsoleOutputInterface $output;
|
|
|
|
private bool $shutdownRequested = false;
|
|
|
|
private CommandRegistry $commandRegistry;
|
|
|
|
private ConsoleErrorHandler $errorHandler;
|
|
|
|
public function __construct(
|
|
private readonly Container $container,
|
|
private readonly string $scriptName = 'console',
|
|
private readonly string $title = 'Console Application',
|
|
?ConsoleOutputInterface $output = null,
|
|
) {
|
|
$this->output = $output ?? new ConsoleOutput();
|
|
|
|
// Setup signal handlers für graceful shutdown
|
|
$this->setupSignalHandlers();
|
|
|
|
// Setze den Fenstertitel
|
|
$this->output->writeWindowTitle($this->title);
|
|
|
|
try {
|
|
$this->initializeCommandRegistry();
|
|
$this->initializeErrorHandler();
|
|
} catch (Throwable $e) {
|
|
// Log the original error for debugging
|
|
error_log("Console initialization failed: " . $e->getMessage());
|
|
error_log("Stack trace: " . $e->getTraceAsString());
|
|
|
|
throw FrameworkException::create(
|
|
ErrorCode::SYS_INITIALIZATION_FAILED,
|
|
'Failed to initialize console application: ' . $e->getMessage()
|
|
);
|
|
}
|
|
}
|
|
|
|
private function initializeErrorHandler(): void
|
|
{
|
|
// Create error recovery components
|
|
$suggestionEngine = new CommandSuggestionEngine($this->commandRegistry->getCommandList());
|
|
$recoveryService = new ErrorRecoveryService(
|
|
$suggestionEngine,
|
|
$this->commandRegistry->getCommandList(),
|
|
$this->commandRegistry
|
|
);
|
|
|
|
// Get logger if available
|
|
$logger = $this->container->has(Logger::class) ? $this->container->get(Logger::class) : null;
|
|
|
|
$this->errorHandler = new ConsoleErrorHandler($recoveryService, $logger);
|
|
}
|
|
|
|
private function setupSignalHandlers(): void
|
|
{
|
|
if (function_exists('pcntl_signal')) {
|
|
pcntl_signal(SIGTERM, [$this, 'handleShutdown']);
|
|
pcntl_signal(SIGINT, [$this, 'handleShutdown']);
|
|
pcntl_signal(SIGHUP, [$this, 'handleShutdown']);
|
|
}
|
|
}
|
|
|
|
public function handleShutdown(int $signal): void
|
|
{
|
|
$this->shutdownRequested = true;
|
|
$this->output->writeLine("Shutdown signal received ({$signal}). Cleaning up...", ConsoleColor::YELLOW);
|
|
|
|
// Cleanup resources
|
|
$this->cleanup();
|
|
exit(ExitCode::SUCCESS->value);
|
|
}
|
|
|
|
private function cleanup(): void
|
|
{
|
|
// Reset window title
|
|
$this->output->writeWindowTitle('Terminal');
|
|
|
|
// No specific cleanup needed for CommandRegistry
|
|
}
|
|
|
|
private function initializeCommandRegistry(): void
|
|
{
|
|
$discoveryRegistry = $this->container->get(DiscoveryRegistry::class);
|
|
$this->commandRegistry = new CommandRegistry($this->container, $discoveryRegistry);
|
|
|
|
// Fallback: Force fresh discovery if no commands found
|
|
if ($this->commandRegistry->getCommandList()->count() === 0) {
|
|
if ($this->container->has(Logger::class)) {
|
|
$logger = $this->container->get(Logger::class);
|
|
$logger->warning('No commands found, forcing fresh discovery...', LogContext::withData([
|
|
'component' => 'ConsoleApplication',
|
|
]));
|
|
}
|
|
|
|
// Force fresh discovery
|
|
$bootstrapper = new \App\Framework\Discovery\DiscoveryServiceBootstrapper(
|
|
$this->container,
|
|
$this->container->get(\App\Framework\DateTime\Clock::class)
|
|
);
|
|
|
|
$freshRegistry = $bootstrapper->performBootstrap(
|
|
$this->container->get(\App\Framework\Core\PathProvider::class),
|
|
$this->container->get(\App\Framework\Cache\Cache::class),
|
|
null
|
|
);
|
|
|
|
// Update container with fresh registry
|
|
$this->container->instance(\App\Framework\Discovery\Results\DiscoveryRegistry::class, $freshRegistry);
|
|
|
|
// Re-initialize command registry with fresh discovery
|
|
$this->commandRegistry = new CommandRegistry($this->container, $freshRegistry);
|
|
|
|
if ($this->container->has(Logger::class)) {
|
|
$logger = $this->container->get(Logger::class);
|
|
$commandCount = count($freshRegistry->attributes->get(\App\Framework\Console\ConsoleCommand::class));
|
|
$logger->info('Fresh discovery completed', LogContext::withData([
|
|
'commands_found' => $commandCount,
|
|
'component' => 'ConsoleApplication',
|
|
]));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Führt ein Kommando aus
|
|
* @param array<int, string> $argv
|
|
*/
|
|
public function run(array $argv): int
|
|
{
|
|
try {
|
|
// Validate and sanitize input
|
|
$argv = $this->validateAndSanitizeInput($argv);
|
|
|
|
// Check for shutdown signal
|
|
if (function_exists('pcntl_signal_dispatch')) {
|
|
pcntl_signal_dispatch();
|
|
}
|
|
|
|
if ($this->shutdownRequested) {
|
|
return ExitCode::INTERRUPTED->value;
|
|
}
|
|
|
|
if (count($argv) < 2) {
|
|
// Launch interactive TUI when no arguments provided
|
|
return $this->launchInteractiveTUI();
|
|
}
|
|
|
|
$commandName = $argv[1];
|
|
$arguments = array_slice($argv, 2);
|
|
|
|
// Handle TUI launch flags
|
|
if (in_array($commandName, ['--interactive', '--tui', '-i'])) {
|
|
return $this->launchInteractiveTUI();
|
|
}
|
|
|
|
// Handle built-in commands
|
|
if (in_array($commandName, ['help', '--help', '-h'])) {
|
|
// Spezifische Command-Hilfe
|
|
if (! empty($arguments) && isset($arguments[0])) {
|
|
$this->showCommandHelp($arguments[0]);
|
|
} else {
|
|
$this->showHelp();
|
|
}
|
|
|
|
return ExitCode::SUCCESS->value;
|
|
}
|
|
|
|
$commandList = $this->commandRegistry->getCommandList();
|
|
|
|
// Prüfe ob es ein direktes Kommando ist
|
|
if ($commandList->has($commandName)) {
|
|
return $this->executeCommand($commandName, $arguments)->value;
|
|
}
|
|
|
|
// Prüfe ob es eine Kategorie ist
|
|
$categories = $this->categorizeCommands($commandList);
|
|
if (isset($categories[$commandName])) {
|
|
$this->showCategoryCommands($commandName, $categories[$commandName]);
|
|
|
|
return ExitCode::SUCCESS->value;
|
|
}
|
|
|
|
// Command/Kategorie nicht gefunden
|
|
return $this->errorHandler->handleCommandNotFound($commandName, $this->output)->value;
|
|
|
|
} catch (Throwable $e) {
|
|
$this->output->writeError("Critical error: " . $e->getMessage());
|
|
$this->cleanup();
|
|
|
|
return ExitCode::GENERAL_ERROR->value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array<int, string> $argv
|
|
* @return array<int, string>
|
|
*/
|
|
private function validateAndSanitizeInput(array $argv): array
|
|
{
|
|
if (empty($argv)) {
|
|
throw new \InvalidArgumentException('No arguments provided');
|
|
}
|
|
|
|
// Validate argv array structure
|
|
if (! is_array($argv) || ! isset($argv[0])) {
|
|
throw new \InvalidArgumentException('Invalid argv structure');
|
|
}
|
|
|
|
// Sanitize each argument
|
|
return array_map(function ($arg) {
|
|
if (! is_string($arg)) {
|
|
throw new \InvalidArgumentException('All arguments must be strings');
|
|
}
|
|
|
|
// Remove null bytes and control characters
|
|
$sanitized = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $arg);
|
|
|
|
// Limit argument length to prevent memory issues
|
|
if (strlen($sanitized) > 4096) {
|
|
throw new \InvalidArgumentException('Argument too long (max 4096 characters)');
|
|
}
|
|
|
|
return $sanitized;
|
|
}, $argv);
|
|
}
|
|
|
|
/**
|
|
* @param array<int, string> $arguments
|
|
*/
|
|
private function executeCommand(string $commandName, array $arguments): ExitCode
|
|
{
|
|
try {
|
|
// Check for shutdown signal before execution
|
|
if (function_exists('pcntl_signal_dispatch')) {
|
|
pcntl_signal_dispatch();
|
|
}
|
|
|
|
if ($this->shutdownRequested) {
|
|
return ExitCode::INTERRUPTED;
|
|
}
|
|
|
|
// Setze den Fenstertitel für das aktuelle Kommando
|
|
$this->output->writeWindowTitle("{$this->scriptName} - {$commandName}");
|
|
|
|
// Execute command via registry
|
|
$result = $this->commandRegistry->executeCommand($commandName, $arguments, $this->output);
|
|
|
|
// Handle ConsoleResult (new) or ExitCode (legacy)
|
|
return $this->processCommandResult($result);
|
|
|
|
} catch (CommandNotFoundException $e) {
|
|
return $this->errorHandler->handleCommandNotFound($commandName, $this->output);
|
|
|
|
} catch (FrameworkException $e) {
|
|
return $this->errorHandler->handleCommandExecutionError($commandName, $e, $this->output);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
return $this->errorHandler->handleValidationError($commandName, $e->getMessage(), $this->output);
|
|
|
|
} catch (\RuntimeException $e) {
|
|
return $this->errorHandler->handleCommandExecutionError($commandName, $e, $this->output);
|
|
|
|
} catch (Throwable $e) {
|
|
return $this->errorHandler->handleUnexpectedError($commandName, $e, $this->output);
|
|
} finally {
|
|
// Reset window title after command execution
|
|
$this->output->writeWindowTitle($this->title);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process command result - supports both ConsoleResult and ExitCode
|
|
*/
|
|
private function processCommandResult(mixed $result): ExitCode
|
|
{
|
|
// New ConsoleResult pattern
|
|
if ($result instanceof \App\Framework\Console\Result\ConsoleResult) {
|
|
// Render result to output
|
|
$result->render($this->output);
|
|
|
|
// Return exit code from result
|
|
return $result->exitCode;
|
|
}
|
|
|
|
// Legacy ExitCode pattern
|
|
if ($result instanceof ExitCode) {
|
|
return $result;
|
|
}
|
|
|
|
// Legacy int pattern (for backwards compatibility)
|
|
if (is_int($result)) {
|
|
return ExitCode::from($result);
|
|
}
|
|
|
|
// Invalid return type - log warning and return error
|
|
if ($this->container->has(Logger::class)) {
|
|
$logger = $this->container->get(Logger::class);
|
|
$logger->warning('Command returned invalid result type', LogContext::withData([
|
|
'result_type' => get_debug_type($result),
|
|
'component' => 'ConsoleApplication',
|
|
]));
|
|
}
|
|
|
|
return ExitCode::GENERAL_ERROR;
|
|
}
|
|
|
|
private function showCommandUsage(string $commandName): void
|
|
{
|
|
try {
|
|
$commandList = $this->commandRegistry->getCommandList();
|
|
if (! $commandList->has($commandName)) {
|
|
return;
|
|
}
|
|
|
|
$command = $commandList->get($commandName);
|
|
|
|
$this->output->writeLine("Usage:", ConsoleColor::BRIGHT_YELLOW);
|
|
$this->output->writeLine(" php {$this->scriptName} {$commandName} [arguments]");
|
|
|
|
if (! empty($command->description)) {
|
|
$this->output->newLine();
|
|
$this->output->writeLine("Description:", ConsoleColor::BRIGHT_YELLOW);
|
|
$this->output->writeLine(" " . $command->description);
|
|
}
|
|
} catch (Throwable $e) {
|
|
// Ignore errors in usage display
|
|
}
|
|
}
|
|
|
|
private function showHelp(): void
|
|
{
|
|
$this->output->writeLine("Console Commands", ConsoleColor::BRIGHT_CYAN);
|
|
$this->output->newLine();
|
|
|
|
$commandList = $this->commandRegistry->getCommandList();
|
|
|
|
if ($commandList->isEmpty()) {
|
|
$this->output->writeLine(" Keine Kommandos verfügbar.", ConsoleColor::YELLOW);
|
|
|
|
return;
|
|
}
|
|
|
|
// Kategorisiere Commands
|
|
$categories = $this->categorizeCommands($commandList);
|
|
|
|
// Zeige Kategorien-Übersicht
|
|
$this->showCategoryOverview($categories);
|
|
|
|
$this->output->newLine();
|
|
$this->output->writeLine("Verwendung:", ConsoleColor::BRIGHT_YELLOW);
|
|
$this->output->writeLine(" php {$this->scriptName} # Interaktive TUI starten");
|
|
$this->output->writeLine(" php {$this->scriptName} --interactive # Interaktive TUI explizit starten");
|
|
$this->output->writeLine(" php {$this->scriptName} <kategorie> # Commands einer Kategorie anzeigen");
|
|
$this->output->writeLine(" php {$this->scriptName} <kommando> [argumente] # Kommando direkt ausführen");
|
|
$this->output->writeLine(" php {$this->scriptName} help <kommando> # Hilfe für spezifisches Kommando");
|
|
|
|
$this->output->newLine();
|
|
$this->output->writeLine("Hinweis:", ConsoleColor::CYAN);
|
|
$this->output->writeLine(" Ohne Argumente wird automatisch die interaktive TUI gestartet.");
|
|
$this->output->writeLine(" Die TUI bietet eine grafische Navigation durch alle verfügbaren Commands.");
|
|
}
|
|
|
|
/**
|
|
* Kategorisiert Commands basierend auf ihrem Präfix
|
|
*/
|
|
private function categorizeCommands(CommandList $commandList): array
|
|
{
|
|
$categories = [];
|
|
|
|
foreach ($commandList as $command) {
|
|
$parts = explode(':', $command->name);
|
|
$category = $parts[0];
|
|
|
|
if (! isset($categories[$category])) {
|
|
$categories[$category] = [];
|
|
}
|
|
|
|
$categories[$category][] = $command;
|
|
}
|
|
|
|
// Sortiere Kategorien
|
|
ksort($categories);
|
|
|
|
return $categories;
|
|
}
|
|
|
|
/**
|
|
* Zeigt eine übersichtliche Kategorien-Übersicht
|
|
*/
|
|
private function showCategoryOverview(array $categories): void
|
|
{
|
|
$this->output->writeLine("Verfügbare Kategorien:", ConsoleColor::BRIGHT_YELLOW);
|
|
$this->output->newLine();
|
|
|
|
$categoryInfo = [
|
|
'db' => 'Database operations (migrations, health checks)',
|
|
'errors' => 'Error management and analytics',
|
|
'backup' => 'Backup and restore operations',
|
|
'secrets' => 'Secret management and encryption',
|
|
'cache' => 'Cache management operations',
|
|
'demo' => 'Demo and example commands',
|
|
'logs' => 'Log management and rotation',
|
|
'alerts' => 'Alert system management',
|
|
'mcp' => 'Model Context Protocol server',
|
|
'make' => 'Code generation commands',
|
|
'docs' => 'Documentation generation',
|
|
'openapi' => 'OpenAPI specification generation',
|
|
'static' => 'Static file generation',
|
|
'redis' => 'Redis cache operations',
|
|
'routes' => 'Route management',
|
|
'discovery' => 'Framework discovery system',
|
|
];
|
|
|
|
foreach ($categories as $category => $commands) {
|
|
$count = count($commands);
|
|
$description = $categoryInfo[$category] ?? 'Various commands';
|
|
|
|
$categoryName = str_pad($category, 12);
|
|
$commandCount = str_pad("({$count} commands)", 15);
|
|
|
|
$this->output->writeLine(
|
|
" {$categoryName} {$commandCount} {$description}",
|
|
ConsoleColor::WHITE
|
|
);
|
|
}
|
|
|
|
$this->output->newLine();
|
|
$this->output->writeLine("Für Commands einer Kategorie: php {$this->scriptName} <kategorie>", ConsoleColor::CYAN);
|
|
}
|
|
|
|
/**
|
|
* Zeigt Commands einer spezifischen Kategorie
|
|
*/
|
|
private function showCategoryCommands(string $category, array $commands): void
|
|
{
|
|
$this->output->writeLine("Commands der Kategorie '{$category}':", ConsoleColor::BRIGHT_CYAN);
|
|
$this->output->newLine();
|
|
|
|
foreach ($commands as $command) {
|
|
$description = $command->description ?: 'Keine Beschreibung verfügbar';
|
|
$commandName = str_pad($command->name, 25);
|
|
|
|
$this->output->writeLine(" {$commandName} {$description}", ConsoleColor::WHITE);
|
|
}
|
|
|
|
$this->output->newLine();
|
|
$this->output->writeLine("Verwendung:", ConsoleColor::BRIGHT_YELLOW);
|
|
$this->output->writeLine(" php {$this->scriptName} <kommando> [argumente]");
|
|
$this->output->writeLine(" php {$this->scriptName} help <kommando> # Für detaillierte Hilfe");
|
|
}
|
|
|
|
/**
|
|
* Zeigt detaillierte Hilfe für ein spezifisches Kommando
|
|
*/
|
|
private function showCommandHelp(string $commandName): void
|
|
{
|
|
$commandList = $this->commandRegistry->getCommandList();
|
|
|
|
if (! $commandList->has($commandName)) {
|
|
$this->errorHandler->handleCommandNotFound($commandName, $this->output);
|
|
|
|
return;
|
|
}
|
|
|
|
$command = $commandList->get($commandName);
|
|
|
|
$this->output->writeLine("Kommando: {$command->name}", ConsoleColor::BRIGHT_CYAN);
|
|
$this->output->newLine();
|
|
|
|
if ($command->description) {
|
|
$this->output->writeLine("Beschreibung:", ConsoleColor::BRIGHT_YELLOW);
|
|
$this->output->writeLine(" {$command->description}");
|
|
$this->output->newLine();
|
|
}
|
|
|
|
// Versuche Parameter-Informationen anzuzeigen
|
|
try {
|
|
$this->showCommandParameters($command);
|
|
} catch (Throwable $e) {
|
|
// Fallback zu Standard-Verwendung
|
|
$this->output->writeLine("Verwendung:", ConsoleColor::BRIGHT_YELLOW);
|
|
$this->output->writeLine(" php {$this->scriptName} {$command->name} [argumente]");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zeigt Parameter-Informationen für ein Kommando
|
|
*/
|
|
private function showCommandParameters(ConsoleCommand $command): void
|
|
{
|
|
try {
|
|
// Hole die DiscoveredAttribute für das Command
|
|
$discoveredAttribute = $this->commandRegistry->getDiscoveredAttribute($command->name);
|
|
|
|
// Hole Reflection Information
|
|
$reflection = new \ReflectionMethod($discoveredAttribute->className->toString(), $discoveredAttribute->methodName->toString());
|
|
|
|
// Prüfe ob es moderne Parameter-Parsing verwendet
|
|
if ($this->commandRegistry->usesReflectionParameters($reflection)) {
|
|
// Nutze den CommandParameterResolver für moderne Parameter-Hilfe
|
|
$resolver = $this->container->resolve(CommandParameterResolver::class);
|
|
$help = $resolver->generateMethodHelp($reflection, $command->name);
|
|
$this->output->writeLine($help);
|
|
} else {
|
|
// Fallback für Legacy-Commands
|
|
$this->output->writeLine("Verwendung:", ConsoleColor::BRIGHT_YELLOW);
|
|
$this->output->writeLine(" php {$this->scriptName} {$command->name} [argumente]");
|
|
}
|
|
} catch (\Throwable $e) {
|
|
// Fallback bei Fehlern
|
|
$this->output->writeLine("Verwendung:", ConsoleColor::BRIGHT_YELLOW);
|
|
$this->output->writeLine(" php {$this->scriptName} {$command->name} [argumente]");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Startet die interaktive TUI
|
|
*/
|
|
private function launchInteractiveTUI(): int
|
|
{
|
|
try {
|
|
// Prüfe ob Terminal kompatibel ist
|
|
if (! $this->isTerminalCompatible()) {
|
|
$this->output->writeError("Interactive TUI requires a compatible terminal.");
|
|
$this->output->writeLine("Use 'php {$this->scriptName} help' for command-line help.");
|
|
|
|
return ExitCode::SOFTWARE_ERROR->value;
|
|
}
|
|
|
|
// Get DiscoveryRegistry for TUI components
|
|
$discoveryRegistry = $this->container->get(DiscoveryRegistry::class);
|
|
|
|
// Create CommandHistory
|
|
$commandHistory = new CommandHistory();
|
|
|
|
// Create new services
|
|
$groupRegistry = new CommandGroupRegistry($discoveryRegistry);
|
|
$workflowExecutor = new SimpleWorkflowExecutor($this->commandRegistry, $groupRegistry, $this->output);
|
|
|
|
// Create TUI components
|
|
$state = new TuiState();
|
|
$renderer = new TuiRenderer($this->output);
|
|
$menuBar = $renderer->getMenuBar();
|
|
$commandExecutor = new TuiCommandExecutor(
|
|
$this->output,
|
|
$this->commandRegistry,
|
|
$this->container,
|
|
$discoveryRegistry,
|
|
$commandHistory,
|
|
new CommandValidator(),
|
|
new CommandHelpGenerator(new ParameterInspector()),
|
|
$this->scriptName
|
|
);
|
|
$inputHandler = new TuiInputHandler($commandExecutor, $menuBar);
|
|
|
|
// Erstelle TUI Instanz
|
|
$tui = new ConsoleTUI(
|
|
$this->output,
|
|
$this->container,
|
|
$discoveryRegistry,
|
|
$state,
|
|
$renderer,
|
|
$inputHandler,
|
|
$commandExecutor,
|
|
$commandHistory,
|
|
$groupRegistry,
|
|
$workflowExecutor
|
|
);
|
|
|
|
// Starte TUI
|
|
return $tui->run()->value;
|
|
|
|
} catch (Throwable $e) {
|
|
$this->output->writeError("Failed to launch interactive TUI: " . $e->getMessage());
|
|
|
|
$config = $this->container->get(AppConfig::class);
|
|
if ($config->isDevelopment()) {
|
|
$this->output->writeLine("Stack trace:", ConsoleColor::RED);
|
|
$this->output->writeLine($e->getTraceAsString());
|
|
}
|
|
|
|
// Fallback to help
|
|
$this->output->newLine();
|
|
$this->output->writeLine("Falling back to command-line help:", ConsoleColor::YELLOW);
|
|
$this->showHelp();
|
|
|
|
return ExitCode::SOFTWARE_ERROR->value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prüft ob das Terminal für TUI kompatibel ist
|
|
*/
|
|
private function isTerminalCompatible(): bool
|
|
{
|
|
// Prüfe ob wir in einem Terminal sind
|
|
if (! function_exists('posix_isatty') || ! posix_isatty(STDOUT)) {
|
|
return false;
|
|
}
|
|
|
|
// Prüfe TERM environment variable
|
|
$term = $_SERVER['TERM'] ?? '';
|
|
if (empty($term) || $term === 'dumb') {
|
|
return false;
|
|
}
|
|
|
|
// Prüfe ob das Terminal interaktiv ist
|
|
if (! stream_isatty(STDIN) || ! stream_isatty(STDOUT)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|