Files
michaelschiemer/src/Framework/Console/ErrorRecovery/ConsoleErrorHandler.php
Michael Schiemer 5050c7d73a docs: consolidate documentation into organized structure
- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
2025-10-05 11:05:04 +02:00

220 lines
7.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Console\ErrorRecovery;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleOutputInterface;
use App\Framework\Console\ExitCode;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
final readonly class ConsoleErrorHandler
{
public function __construct(
private ErrorRecoveryService $recoveryService,
private ?Logger $logger = null
) {
}
public function handleCommandNotFound(string $command, ConsoleOutputInterface $output): ExitCode
{
$this->logError("Command not found: {$command}");
$this->recoveryService->handleCommandNotFound($command, $output);
return ExitCode::COMMAND_NOT_FOUND;
}
public function handleCommandExecutionError(
string $command,
\Throwable $error,
ConsoleOutputInterface $output
): ExitCode {
$this->logError("Command execution error in '{$command}': " . $error->getMessage(), [
'command' => $command,
'error_type' => get_class($error),
'trace' => $error->getTraceAsString(),
]);
if ($error instanceof FrameworkException) {
return $this->handleFrameworkException($command, $error, $output);
}
$this->recoveryService->handleCommandExecutionError($command, $error, $output);
return $this->determineExitCode($error);
}
public function handleValidationError(
string $command,
string $validationError,
ConsoleOutputInterface $output
): ExitCode {
$this->logError("Validation error in '{$command}': {$validationError}");
$this->recoveryService->handleArgumentError($command, $validationError, $output);
return ExitCode::INVALID_INPUT;
}
public function handlePermissionError(
string $command,
ConsoleOutputInterface $output
): ExitCode {
$this->logError("Permission denied for command: {$command}");
$this->recoveryService->handlePermissionError($command, $output);
return ExitCode::PERMISSION_DENIED;
}
public function handleUnexpectedError(
string $command,
\Throwable $error,
ConsoleOutputInterface $output
): ExitCode {
$this->logError("Unexpected error in '{$command}': " . $error->getMessage(), [
'command' => $command,
'error_type' => get_class($error),
'file' => $error->getFile(),
'line' => $error->getLine(),
'trace' => $error->getTraceAsString(),
]);
$output->writeLine("💥 An unexpected error occurred:", ConsoleColor::RED);
$output->writeLine(" {$error->getMessage()}", ConsoleColor::RED);
$output->newLine();
$output->writeLine("🔍 Debug information:", ConsoleColor::GRAY);
$output->writeLine(" Error: " . get_class($error), ConsoleColor::GRAY);
$output->writeLine(" File: {$error->getFile()}:{$error->getLine()}", ConsoleColor::GRAY);
$output->newLine();
$this->recoveryService->handleGeneralError($command, $error, $output);
return ExitCode::GENERAL_ERROR;
}
public function handleGracefulShutdown(string $reason, ConsoleOutputInterface $output): ExitCode
{
$this->logInfo("Graceful shutdown: {$reason}");
$output->writeLine("🛑 Operation interrupted: {$reason}", ConsoleColor::YELLOW);
$output->writeLine(" The command was safely terminated.", ConsoleColor::GRAY);
$output->newLine();
return ExitCode::INTERRUPTED;
}
private function handleFrameworkException(
string $command,
FrameworkException $exception,
ConsoleOutputInterface $output
): ExitCode {
$errorCode = $exception->getErrorCode();
return match ($errorCode) {
ErrorCode::CON_COMMAND_NOT_FOUND => $this->handleCommandNotFound($command, $output),
ErrorCode::CON_INVALID_ARGUMENTS => $this->handleValidationError(
$command,
$exception->getMessage(),
$output
),
ErrorCode::AUTH_UNAUTHORIZED,
ErrorCode::AUTH_INSUFFICIENT_PRIVILEGES => $this->handlePermissionError($command, $output),
ErrorCode::DB_CONNECTION_FAILED,
ErrorCode::DB_QUERY_FAILED => $this->handleDatabaseError($command, $exception, $output),
ErrorCode::HTTP_RATE_LIMIT_EXCEEDED => $this->handleRateLimitError($command, $exception, $output),
default => $this->handleGeneralFrameworkError($command, $exception, $output)
};
}
private function handleDatabaseError(
string $command,
FrameworkException $exception,
ConsoleOutputInterface $output
): ExitCode {
$output->writeLine("🗄️ Database error in command '{$command}':", ConsoleColor::RED);
$output->writeLine(" {$exception->getMessage()}", ConsoleColor::RED);
$output->newLine();
$output->writeLine("💡 Database troubleshooting:", ConsoleColor::CYAN);
$output->writeLine(" • Check database connection settings", ConsoleColor::WHITE);
$output->writeLine(" • Verify database server is running", ConsoleColor::WHITE);
$output->writeLine(" • Check network connectivity", ConsoleColor::WHITE);
$output->writeLine(" • Verify database credentials", ConsoleColor::WHITE);
$output->newLine();
return ExitCode::DATABASE_ERROR;
}
private function handleRateLimitError(
string $command,
FrameworkException $exception,
ConsoleOutputInterface $output
): ExitCode {
$output->writeLine("⏳ Rate limit exceeded for command '{$command}':", ConsoleColor::YELLOW);
$output->writeLine(" {$exception->getMessage()}", ConsoleColor::YELLOW);
$output->newLine();
if ($exception->hasRetryAfter()) {
$retryAfter = $exception->getRetryAfter();
$output->writeLine("🕐 You can retry this command in {$retryAfter} seconds.", ConsoleColor::CYAN);
} else {
$output->writeLine("🕐 Please wait before retrying this command.", ConsoleColor::CYAN);
}
$output->newLine();
return ExitCode::RATE_LIMITED;
}
private function handleGeneralFrameworkError(
string $command,
FrameworkException $exception,
ConsoleOutputInterface $output
): ExitCode {
$this->recoveryService->handleCommandExecutionError($command, $exception, $output);
return $this->determineExitCode($exception);
}
private function determineExitCode(\Throwable $error): ExitCode
{
if ($error instanceof FrameworkException) {
return match ($error->getErrorCode()) {
ErrorCode::CON_COMMAND_NOT_FOUND => ExitCode::COMMAND_NOT_FOUND,
ErrorCode::CON_INVALID_ARGUMENTS => ExitCode::INVALID_INPUT,
ErrorCode::AUTH_UNAUTHORIZED,
ErrorCode::AUTH_INSUFFICIENT_PRIVILEGES => ExitCode::PERMISSION_DENIED,
ErrorCode::DB_CONNECTION_FAILED,
ErrorCode::DB_QUERY_FAILED => ExitCode::DATABASE_ERROR,
ErrorCode::HTTP_RATE_LIMIT_EXCEEDED => ExitCode::RATE_LIMITED,
default => ExitCode::GENERAL_ERROR
};
}
return match (true) {
$error instanceof \ArgumentCountError,
$error instanceof \TypeError => ExitCode::INVALID_INPUT,
$error instanceof \Error => ExitCode::FATAL_ERROR,
default => ExitCode::GENERAL_ERROR
};
}
private function logError(string $message, array $context = []): void
{
$this->logger?->error($message, LogContext::withData($context));
}
private function logInfo(string $message, array $context = []): void
{
$this->logger?->info($message, LogContext::withData($context));
}
}