Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -1,10 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Components;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ConsoleOutputInterface;
/**
* Klasse für interaktive Menüs in der Konsole.
@@ -14,12 +16,16 @@ use App\Framework\Console\ConsoleOutput;
final class InteractiveMenu
{
private ConsoleOutput $output;
private array $menuItems = [];
private string $title = '';
private int $selectedIndex = 0;
private bool $showNumbers = true;
public function __construct(ConsoleOutput $output)
public function __construct(ConsoleOutputInterface $output)
{
$this->output = $output;
}
@@ -30,6 +36,7 @@ final class InteractiveMenu
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
@@ -45,8 +52,9 @@ final class InteractiveMenu
$this->menuItems[] = [
'label' => $label,
'action' => $action,
'value' => $value ?? $label
'value' => $value ?? $label,
];
return $this;
}
@@ -59,8 +67,9 @@ final class InteractiveMenu
'label' => '---',
'action' => null,
'value' => null,
'separator' => true
'separator' => true,
];
return $this;
}
@@ -70,6 +79,7 @@ final class InteractiveMenu
public function showNumbers(bool $show = true): self
{
$this->showNumbers = $show;
return $this;
}
@@ -79,7 +89,7 @@ final class InteractiveMenu
public function showSimple(): mixed
{
// Verwende den ScreenManager anstelle des direkten clearScreen()
$this->output->screen()->newMenu();
$this->output->screen->newMenu();
if ($this->title) {
$this->output->writeLine($this->title, ConsoleColor::BRIGHT_CYAN);
@@ -90,6 +100,7 @@ final class InteractiveMenu
foreach ($this->menuItems as $index => $item) {
if (isset($item['separator'])) {
$this->output->writeLine($item['label'], ConsoleColor::GRAY);
continue;
}
@@ -104,7 +115,7 @@ final class InteractiveMenu
if (is_numeric($input)) {
$selectedIndex = (int)$input - 1;
if (isset($this->menuItems[$selectedIndex]) && !isset($this->menuItems[$selectedIndex]['separator'])) {
if (isset($this->menuItems[$selectedIndex]) && ! isset($this->menuItems[$selectedIndex]['separator'])) {
$item = $this->menuItems[$selectedIndex];
if ($item['action']) {
@@ -116,6 +127,7 @@ final class InteractiveMenu
}
$this->output->writeError('Ungültige Auswahl!');
return $this->showSimple();
}
@@ -124,7 +136,7 @@ final class InteractiveMenu
*/
public function showInteractive(): mixed
{
$this->output->screen()->setInteractiveMode();
$this->output->screen->setInteractiveMode();
// Terminal in Raw-Modus setzen für Tastatur-Input
$this->setRawMode(true);
@@ -137,9 +149,11 @@ final class InteractiveMenu
switch ($key) {
case "\033[A": // Pfeil hoch
$this->moveUp();
break;
case "\033[B": // Pfeil runter
$this->moveDown();
break;
case "\n": // Enter
case "\r":
@@ -162,7 +176,7 @@ final class InteractiveMenu
private function renderMenu(): void
{
// Verwende den ScreenManager anstelle des direkten clearScreen()
$this->output->screen()->newMenu();
$this->output->screen->newMenu();
if ($this->title) {
$this->output->writeLine($this->title, ConsoleColor::BRIGHT_CYAN);
@@ -173,6 +187,7 @@ final class InteractiveMenu
foreach ($this->menuItems as $index => $item) {
if (isset($item['separator'])) {
$this->output->writeLine($item['label'], ConsoleColor::GRAY);
continue;
}
@@ -229,6 +244,11 @@ final class InteractiveMenu
{
$key = fgetc(STDIN);
// Handle Docker/non-interactive environment
if ($key === false) {
return 'q'; // Auto-quit in non-interactive mode
}
// Behandle Escape-Sequenzen
if ($key === "\033") {
$key .= fgetc(STDIN);

View File

@@ -1,11 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Components;
use App\Framework\Console\ConsoleStyle;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleFormat;
use App\Framework\Console\ConsoleStyle;
/**
* Rendert eine Tabelle in der Konsole.
@@ -13,8 +14,11 @@ use App\Framework\Console\ConsoleFormat;
final class Table
{
private array $headers = [];
private array $rows = [];
private array $columnWidths = [];
private int $padding = 1;
public function __construct(
@@ -35,6 +39,7 @@ final class Table
{
$this->headers = $headers;
$this->calculateColumnWidths();
return $this;
}
@@ -45,6 +50,7 @@ final class Table
{
$this->rows[] = $row;
$this->calculateColumnWidths();
return $this;
}
@@ -55,6 +61,7 @@ final class Table
{
$this->rows = $rows;
$this->calculateColumnWidths();
return $this;
}
@@ -64,6 +71,7 @@ final class Table
public function setPadding(int $padding): self
{
$this->padding = max(0, $padding);
return $this;
}
@@ -83,7 +91,7 @@ final class Table
}
// Header
if (!empty($this->headers)) {
if (! empty($this->headers)) {
$output .= $this->renderRow($this->headers, $this->headerStyle) . "\n";
if ($this->showBorders) {

View File

@@ -1,11 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Components;
use App\Framework\Console\ConsoleStyle;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleFormat;
use App\Framework\Console\ConsoleStyle;
/**
* Rendert eine Textbox in der Konsole.
@@ -19,7 +19,8 @@ final readonly class TextBox
private ?ConsoleStyle $borderStyle = null,
private ?ConsoleStyle $contentStyle = null,
private ?string $title = null
) {}
) {
}
public function render(): string
{
@@ -104,7 +105,7 @@ final readonly class TextBox
if (mb_strlen($testLine) <= $width) {
$currentLine = $testLine;
} else {
if (!empty($currentLine)) {
if (! empty($currentLine)) {
$lines[] = $currentLine;
$currentLine = $word;
} else {
@@ -115,7 +116,7 @@ final readonly class TextBox
}
}
if (!empty($currentLine)) {
if (! empty($currentLine)) {
$lines[] = $currentLine;
}

View File

@@ -1,12 +1,13 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Components;
use App\Framework\Console\ConsoleStyle;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleFormat;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ConsoleStyle;
/**
* TreeHelper zum Anzeigen hierarchischer Baumstrukturen in der Konsole.
@@ -15,9 +16,13 @@ use App\Framework\Console\ConsoleOutput;
final class TreeHelper
{
private string $prefix = '';
private bool $isLastElement = true;
private ?ConsoleStyle $nodeStyle = null;
private ?ConsoleStyle $leafStyle = null;
private ?ConsoleStyle $lineStyle = null;
/**
@@ -40,6 +45,7 @@ final class TreeHelper
public function setNodeStyle(?ConsoleStyle $style): self
{
$this->nodeStyle = $style;
return $this;
}
@@ -49,6 +55,7 @@ final class TreeHelper
public function setLeafStyle(?ConsoleStyle $style): self
{
$this->leafStyle = $style;
return $this;
}
@@ -58,6 +65,7 @@ final class TreeHelper
public function setLineStyle(?ConsoleStyle $style): self
{
$this->lineStyle = $style;
return $this;
}
@@ -67,6 +75,7 @@ final class TreeHelper
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
@@ -83,7 +92,7 @@ final class TreeHelper
$this->nodes[] = [
'title' => $title,
'node' => $node,
'isLeaf' => false
'isLeaf' => false,
];
return $node;
@@ -97,7 +106,7 @@ final class TreeHelper
$this->nodes[] = [
'title' => $title,
'node' => null,
'isLeaf' => true
'isLeaf' => true,
];
return $this;
@@ -108,7 +117,7 @@ final class TreeHelper
*/
public function display(): void
{
if (!empty($this->title)) {
if (! empty($this->title)) {
$this->output->writeLine($this->title, $this->nodeStyle);
}
@@ -122,7 +131,7 @@ final class TreeHelper
{
$output = '';
if (!empty($this->title)) {
if (! empty($this->title)) {
$output .= $this->nodeStyle->apply($this->title) . "\n";
}
@@ -139,6 +148,7 @@ final class TreeHelper
{
$this->prefix = $prefix;
$this->isLastElement = $isLastElement;
return $this;
}
@@ -168,7 +178,7 @@ final class TreeHelper
);
// Unterelemente rekursiv anzeigen
if (!$item['isLeaf'] && $item['node'] !== null) {
if (! $item['isLeaf'] && $item['node'] !== null) {
$item['node']
->setPrefix($nodePrefix, $isLast)
->displayTree();
@@ -201,7 +211,7 @@ final class TreeHelper
$style->apply($title) . "\n";
// Unterelemente rekursiv rendern
if (!$item['isLeaf'] && $item['node'] !== null) {
if (! $item['isLeaf'] && $item['node'] !== null) {
$childOutput = $item['node']
->setPrefix($nodePrefix, $isLast)
->renderTree();

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
final readonly class Console
{
public function __construct(
public ConsoleInputInterface $input,
public ConsoleOutputInterface $output,
) {
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
@@ -6,7 +7,8 @@ namespace App\Framework\Console;
use App\Framework\Console\Components\InteractiveMenu;
use App\Framework\Console\Exceptions\CommandNotFoundException;
use App\Framework\DI\Container;
use App\Framework\Discovery\Results\DiscoveryResults;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Discovery\ValueObjects\DiscoveredAttribute;
use ReflectionClass;
use ReflectionMethod;
use Throwable;
@@ -14,6 +16,7 @@ use Throwable;
final class ConsoleApplication
{
private array $commands = [];
private ConsoleOutputInterface $output;
public function __construct(
@@ -21,22 +24,33 @@ final class ConsoleApplication
private readonly string $scriptName = 'console',
private readonly string $title = 'Console Application',
?ConsoleOutputInterface $output = null,
) {
$this->output = $output ?? new ConsoleOutput();
// Setze den Fenstertitel
$this->output->writeWindowTitle($this->title);
$results = $this->container->get(DiscoveryResults::class);
$registry = $this->container->get(DiscoveryRegistry::class);
foreach($results->get(ConsoleCommand::class) as $command) {
/** @var DiscoveredAttribute $discoveredAttribute */
foreach ($registry->attributes->get(ConsoleCommand::class) as $discoveredAttribute) {
$this->commands[$command['attribute_data']['name']] = [
'instance' => $this->container->get($command['class']),
'method' => $command['method'],
'description' => $command['attribute_data']['description'] ?? ['Keine Beschreibung verfügbar'],
'reflection' => new ReflectionMethod($command['class'], $command['method'])
/** @var ConsoleCommand $command */
$command = $discoveredAttribute->createAttributeInstance();
// Extract attribute data and class name from Value Object
$attributeData = $discoveredAttribute->arguments ?? [];
$className = $discoveredAttribute->className->getFullyQualified();
if ($command->name === '') {
continue; // Skip commands without proper attribute data
}
$this->commands[$command->name] = [
'instance' => $this->container->get($className),
'method' => $discoveredAttribute->methodName?->toString() ?? '__invoke',
'description' => $attributeData['description'] ?? 'Keine Beschreibung verfügbar',
'reflection' => new ReflectionMethod($className, $discoveredAttribute->methodName?->toString() ?? '__invoke'),
];
}
}
@@ -44,7 +58,6 @@ final class ConsoleApplication
/**
* Registriert alle Kommandos aus einer Klasse
*/
public function registerCommands(object $commandClass): void
{
$reflection = new ReflectionClass($commandClass);
@@ -60,13 +73,12 @@ final class ConsoleApplication
'instance' => $commandClass,
'method' => $method->getName(),
'description' => $command->description,
'reflection' => $method
'reflection' => $method,
];
}
}
}
/**
* Führt ein Kommando aus
*/
@@ -74,6 +86,7 @@ final class ConsoleApplication
{
if (count($argv) < 2) {
$this->showHelp();
return ExitCode::SUCCESS->value;
}
@@ -82,12 +95,14 @@ final class ConsoleApplication
if (in_array($commandName, ['help', '--help', '-h'])) {
$this->showHelp();
return ExitCode::SUCCESS->value;
}
if (!isset($this->commands[$commandName])) {
if (! isset($this->commands[$commandName])) {
$this->output->writeError("Kommando '{$commandName}' nicht gefunden.");
$this->showHelp();
return ExitCode::COMMAND_NOT_FOUND->value;
}
@@ -124,6 +139,7 @@ final class ConsoleApplication
} catch (CommandNotFoundException $e) {
$this->output->writeError("Kommando nicht gefunden: " . $e->getMessage());
return ExitCode::COMMAND_NOT_FOUND;
} catch (Throwable $e) {
$this->output->writeError("Fehler beim Ausführen des Kommandos: " . $e->getMessage());
@@ -149,6 +165,10 @@ final class ConsoleApplication
$menu = new InteractiveMenu($this->output);
$menu->setTitle("Kommandos");
if (empty($this->commands)) {
// TODO Add Default Commands
}
foreach ($this->commands as $name => $command) {
$description = $command['description'] ?: 'Keine Beschreibung verfügbar';

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
@@ -48,5 +49,4 @@ enum ConsoleColor: string
{
return "\033[{$this->value}m";
}
}

View File

@@ -1,15 +1,17 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
use Attribute;
#[Attribute(\Attribute::TARGET_METHOD)]
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class ConsoleCommand
{
public function __construct(
public string $name,
public string $description = ''
) {}
) {
}
}

View File

@@ -1,9 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
use App\Framework\Core\AttributeMapper;
use App\Framework\Reflection\WrappedReflectionClass;
use App\Framework\Reflection\WrappedReflectionMethod;
final readonly class ConsoleCommandMapper implements AttributeMapper
{
@@ -12,12 +15,18 @@ final readonly class ConsoleCommandMapper implements AttributeMapper
return ConsoleCommand::class;
}
public function map(object $reflectionTarget, object $attributeInstance): ?array
public function map(WrappedReflectionClass|WrappedReflectionMethod $reflectionTarget, object $attributeInstance): ?array
{
if (! $reflectionTarget instanceof WrappedReflectionMethod) {
return null; // ConsoleCommand can only be applied to methods
}
return [
'name' => $attributeInstance->name,
'description' => $attributeInstance->description,
'class' => $reflectionTarget->getDeclaringClass()->getName(),
'attribute_data' => [
'name' => $attributeInstance->name,
'description' => $attributeInstance->description,
],
'class' => $reflectionTarget->getDeclaringClass(),
'method' => $reflectionTarget->getName(),
];
}

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
enum ConsoleFormat: string

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
@@ -10,7 +11,9 @@ use App\Framework\Console\Components\InteractiveMenu;
final class ConsoleInput implements ConsoleInputInterface
{
private array $arguments;
private array $options = [];
private ?ConsoleOutputInterface $output = null;
public function __construct(array $arguments, ?ConsoleOutputInterface $output = null)

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
interface ConsoleInputInterface

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
@@ -10,7 +11,9 @@ use App\Framework\Console\Screen\ScreenManager;
final readonly class ConsoleOutput implements ConsoleOutputInterface
{
public Cursor $cursor;
public Display $display;
public ScreenManager $screen;
public function __construct()
@@ -20,30 +23,6 @@ final readonly class ConsoleOutput implements ConsoleOutputInterface
$this->screen = new ScreenManager($this);
}
/**
* Gibt den Cursor-Controller zurück.
*/
public function cursor(): Cursor
{
return $this->cursor;
}
/**
* Gibt den Display-Controller zurück.
*/
public function display(): Display
{
return $this->display;
}
/**
* Gibt den ScreenManager zurück.
*/
public function screen(): ScreenManager
{
return $this->screen;
}
/**
* Schreibt Text mit optionalem Stil.
*/

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
@@ -12,7 +13,7 @@ interface ConsoleOutputInterface
/**
* Schreibt Text in die Konsole
*/
public function write(string $message, ?ConsoleColor $color = null): void;
public function write(string $message, null|ConsoleColor|ConsoleStyle $style = null): void;
/**
* Schreibt Text mit Zeilenumbruch in die Konsole

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
final readonly class ConsoleStyle
@@ -8,7 +10,8 @@ final readonly class ConsoleStyle
public ?ConsoleColor $color = null,
public ?ConsoleFormat $format = null,
public ?ConsoleColor $background = null,
){}
) {
}
/**
* Erstellt einen Style mit den gewünschten Eigenschaften.
@@ -40,6 +43,7 @@ final readonly class ConsoleStyle
if (empty($codes)) {
return '';
}
return "\033[" . implode(';', $codes) . 'm';
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
@@ -7,7 +8,7 @@ use App\Framework\Console\Components\InteractiveMenu;
final readonly class DemoCommand
{
#[ConsoleCommand('demo:hello', 'Zeigt eine einfache Hallo-Welt-Nachricht')]
##[ConsoleCommand('demo:hello', 'Zeigt eine einfache Hallo-Welt-Nachricht')]
public function hello(ConsoleInput $input, ConsoleOutput $output): int
{
$output->writeWindowTitle('Help Title', 2);
@@ -23,7 +24,7 @@ final readonly class DemoCommand
return 0;
}
#[ConsoleCommand('demo:colors', 'Zeigt alle verfügbaren Farben')]
##[ConsoleCommand('demo:colors', 'Zeigt alle verfügbaren Farben')]
public function colors(ConsoleInput $input, ConsoleOutput $output): int
{
$output->writeLine('Verfügbare Farben:', ConsoleColor::BRIGHT_WHITE);
@@ -55,7 +56,7 @@ final readonly class DemoCommand
return 0;
}
#[ConsoleCommand('demo:interactive', 'Interaktive Demo mit Benutzereingaben')]
##[ConsoleCommand('demo:interactive', 'Interaktive Demo mit Benutzereingaben')]
public function interactive(ConsoleInput $input, ConsoleOutput $output): int
{
$output->writeLine('Interaktive Demo', ConsoleColor::BRIGHT_CYAN);
@@ -75,27 +76,30 @@ final readonly class DemoCommand
return 0;
}
#[ConsoleCommand('demo:menu', 'Zeigt ein interaktives Menü')]
##[ConsoleCommand('demo:menu', 'Zeigt ein interaktives Menü')]
public function menu(ConsoleInput $input, ConsoleOutput $output): int
{
$menu = new InteractiveMenu($output);
$result = $menu
->setTitle('Hauptmenü - Demo Application')
->addItem('Benutzer verwalten', function() use ($output) {
->addItem('Benutzer verwalten', function () use ($output) {
return $this->userMenu($output);
})
->addItem('Einstellungen', function() use ($output) {
->addItem('Einstellungen', function () use ($output) {
$output->writeInfo('Einstellungen werden geöffnet...');
return 'settings';
})
->addSeparator()
->addItem('Hilfe anzeigen', function() use ($output) {
->addItem('Hilfe anzeigen', function () use ($output) {
$output->writeInfo('Hilfe wird angezeigt...');
return 'help';
})
->addItem('Beenden', function() use ($output) {
->addItem('Beenden', function () use ($output) {
$output->writeSuccess('Auf Wiedersehen!');
return 'exit';
})
->showInteractive();
@@ -107,7 +111,7 @@ final readonly class DemoCommand
return 0;
}
#[ConsoleCommand('demo:simple-menu', 'Zeigt ein einfaches Nummern-Menü')]
##[ConsoleCommand('demo:simple-menu', 'Zeigt ein einfaches Nummern-Menü')]
public function simpleMenu(ConsoleInput $input, ConsoleOutput $output): int
{
$menu = new InteractiveMenu($output);
@@ -120,10 +124,11 @@ final readonly class DemoCommand
->showSimple();
$output->writeSuccess("Sie haben gewählt: {$result}");
return 0;
}
#[ConsoleCommand('demo:wizard', 'Zeigt einen Setup-Wizard')]
##[ConsoleCommand('demo:wizard', 'Zeigt einen Setup-Wizard')]
public function wizard(ConsoleInput $input, ConsoleOutput $output): int
{
$output->writeInfo('🧙 Setup-Wizard gestartet');
@@ -150,7 +155,7 @@ final readonly class DemoCommand
'Logging',
'Email-Versand',
'API-Schnittstelle',
'Admin-Panel'
'Admin-Panel',
]);
// Zusammenfassung
@@ -182,12 +187,12 @@ final readonly class DemoCommand
return $menu
->setTitle('Benutzer-Verwaltung')
->addItem('Benutzer auflisten', fn() => 'list_users')
->addItem('Neuen Benutzer erstellen', fn() => 'create_user')
->addItem('Benutzer bearbeiten', fn() => 'edit_user')
->addItem('Benutzer löschen', fn() => 'delete_user')
->addItem('Benutzer auflisten', fn () => 'list_users')
->addItem('Neuen Benutzer erstellen', fn () => 'create_user')
->addItem('Benutzer bearbeiten', fn () => 'edit_user')
->addItem('Benutzer löschen', fn () => 'delete_user')
->addSeparator()
->addItem('← Zurück zum Hauptmenü', fn() => 'back')
->addItem('← Zurück zum Hauptmenü', fn () => 'back')
->showInteractive() ?? 'back';
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\DemoCommand;
@@ -8,18 +9,24 @@ use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ConsoleStyle;
use App\Framework\Console\Screen\ScreenType;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\Timer;
final class ScreenDemoCommand
{
public function __construct(
private readonly Timer $timer
) {
}
#[ConsoleCommand('demo:screen', 'Zeigt die verschiedenen Screen-Management-Funktionen')]
public function __invoke(ConsoleInput $input, ConsoleOutput $output): int
{
// Aktiviere interaktiven Modus
$output->screen()->setInteractiveMode(true);
$output->screen->setInteractiveMode(true);
// Zeige ein Menü
$output->screen()->newMenu();
$output->screen->newMenu();
$output->writeLine('=== Screen-Management Demo ===', ConsoleStyle::success());
$output->writeLine('');
$output->writeLine('1. Cursor-Bewegungen');
@@ -33,15 +40,19 @@ final class ScreenDemoCommand
switch ($choice) {
case '1':
$this->demoCursor($input, $output);
break;
case '2':
$this->demoDisplay($input, $output);
break;
case '3':
$this->demoProgress($input, $output);
break;
case '4':
$this->demoTypeset($input, $output);
break;
default:
$output->writeError('Ungültige Auswahl!');
@@ -52,37 +63,37 @@ final class ScreenDemoCommand
private function demoCursor(ConsoleInput $input, ConsoleOutput $output): void
{
$output->screen()->newScreen(ScreenType::CONTENT);
$output->screen->new(ScreenType::CONTENT);
$output->writeLine('=== Cursor-Bewegungs-Demo ===', ConsoleStyle::success());
$output->writeLine('');
// Cursor-Bewegungen
$output->writeLine('Der Cursor wird jetzt bewegt...');
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
$output->cursor()->down(2)->right(5);
$output->cursor->down(2)->right(5);
$output->write('Hallo!', ConsoleStyle::success());
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
$output->cursor()->down(1)->left(5);
$output->cursor->down(1)->left(5);
$output->write('Welt!', ConsoleStyle::info());
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
$output->cursor()->moveTo(10, 20);
$output->cursor->moveTo(10, 20);
$output->write('Position 10,20', ConsoleStyle::warning());
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
$output->cursor()->home();
$output->cursor->home();
$output->writeLine("\nZurück zum Anfang!", ConsoleStyle::error());
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
$output->writeLine("\nDrücken Sie eine Taste, um fortzufahren...");
$output->screen()->waitForInput();
$output->screen->waitForInput();
}
private function demoDisplay(ConsoleInput $input, ConsoleOutput $output): void
{
$output->screen()->newScreen(ScreenType::CONTENT);
$output->screen->new(ScreenType::CONTENT);
$output->writeLine('=== Bildschirmlöschungs-Demo ===', ConsoleStyle::success());
$output->writeLine('');
@@ -91,36 +102,36 @@ final class ScreenDemoCommand
$output->writeLine("Zeile $i: Dies ist ein Test");
}
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
$output->writeLine("\nLösche in 3 Sekunden den Bildschirm...");
sleep(3);
$this->timer->sleep(Duration::fromSeconds(3));
// Bildschirm löschen
$output->display()->clear();
$output->display->clear();
$output->writeLine('Bildschirm wurde gelöscht!');
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
// Zeilen hinzufügen
for ($i = 1; $i <= 5; $i++) {
$output->writeLine("Neue Zeile $i");
}
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
$output->writeLine("\nLösche nur die aktuelle Zeile in 2 Sekunden...");
sleep(2);
$this->timer->sleep(Duration::fromSeconds(2));
// Zeile löschen
$output->display()->clearLine();
$output->display->clearLine();
$output->writeLine('Die Zeile wurde gelöscht und durch diese ersetzt!');
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
$output->writeLine("\nDrücken Sie eine Taste, um fortzufahren...");
$output->screen()->waitForInput();
$output->screen->waitForInput();
}
private function demoProgress(ConsoleInput $input, ConsoleOutput $output): void
{
$output->screen()->newScreen(ScreenType::PROGRESS);
$output->screen->new(ScreenType::PROGRESS);
$output->writeLine('=== Fortschrittsanzeige-Demo ===', ConsoleStyle::success());
$output->writeLine('');
@@ -131,22 +142,22 @@ final class ScreenDemoCommand
$bar = str_repeat('█', $i) . str_repeat('░', $total - $i);
// Zeile löschen und neue Fortschrittsanzeige
$output->display()->clearLine();
$output->display->clearLine();
$output->write("Fortschritt: [{$bar}] {$percent}%");
usleep(200000); // 200ms pause
$this->timer->sleep(Duration::fromMilliseconds(200));
}
$output->writeLine("\n\nFortschritt abgeschlossen!");
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
$output->writeLine("\nDrücken Sie eine Taste, um fortzufahren...");
$output->screen()->waitForInput();
$output->screen->waitForInput();
}
private function demoTypeset(ConsoleInput $input, ConsoleOutput $output): void
{
$output->screen()->newScreen(ScreenType::CONTENT);
$output->screen->new(ScreenType::CONTENT);
$output->writeLine('=== Typensatz-Demo ===', ConsoleStyle::success());
$output->writeLine('');
@@ -157,17 +168,17 @@ final class ScreenDemoCommand
// Typensatz-Effekt
foreach (str_split($text) as $char) {
$output->write($char);
usleep(rand(50000, 150000)); // 50-150ms zufällige Pause
$this->timer->sleep(Duration::fromMicroseconds(rand(50000, 150000)));
}
$output->writeLine("\n\nTypensatz abgeschlossen!");
sleep(1);
$this->timer->sleep(Duration::fromSeconds(1));
$output->writeLine("\nDrücken Sie eine Taste, um zurückzukehren...");
$output->screen()->waitForInput();
$output->screen->waitForInput();
// Zurück zum Hauptmenü
$output->screen()->newMenu();
$output->screen->newMenu();
$this->__invoke($input, $output);
}
}

View File

@@ -1,15 +1,24 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Examples;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ProgressBar;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\Timer;
class ProgressBarExample
{
#[ConsoleCommand(name: 'demo:progressbar', description: 'Zeigt eine Demonstration der Fortschrittsanzeige')]
public function __construct(
private readonly Timer $timer
) {
}
##[ConsoleCommand(name: 'demo:progressbar', description: 'Zeigt eine Demonstration der Fortschrittsanzeige')]
public function showProgressBarDemo(ConsoleInput $input, ConsoleOutput $output): int
{
$output->writeInfo('Demonstration der Fortschrittsanzeige');
@@ -22,7 +31,7 @@ class ProgressBarExample
for ($i = 0; $i < 10; $i++) {
// Simuliere Arbeit
usleep(200000);
$this->timer->sleep(Duration::fromMilliseconds(200));
$progress->advance();
}
@@ -37,7 +46,7 @@ class ProgressBarExample
for ($i = 0; $i < 5; $i++) {
// Simuliere Arbeit
usleep(500000);
$this->timer->sleep(Duration::fromMilliseconds(500));
$progress->advance();
}

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Examples;
use App\Framework\Console\ConsoleCommand;
@@ -7,10 +9,17 @@ use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\Spinner;
use App\Framework\Console\SpinnerStyle;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\Timer;
class SpinnerExample
{
#[ConsoleCommand(name: 'demo:spinner', description: 'Zeigt eine Demonstration der Spinner-Komponente')]
public function __construct(
private readonly Timer $timer
) {
}
##[ConsoleCommand(name: 'demo:spinner', description: 'Zeigt eine Demonstration der Spinner-Komponente')]
public function showSpinnerDemo(ConsoleInput $input, ConsoleOutput $output): int
{
$output->writeInfo('Demonstration der Spinner-Komponente');
@@ -22,7 +31,7 @@ class SpinnerExample
// Simuliere Arbeit
for ($i = 0; $i < 10; $i++) {
usleep(100000);
$this->timer->sleep(Duration::fromMilliseconds(100));
$spinner->update();
}
@@ -33,7 +42,7 @@ class SpinnerExample
SpinnerStyle::DOTS,
SpinnerStyle::LINE,
SpinnerStyle::BOUNCE,
SpinnerStyle::ARROW
SpinnerStyle::ARROW,
];
foreach ($styles as $style) {
@@ -42,7 +51,7 @@ class SpinnerExample
$spinner->start();
for ($i = 0; $i < 15; $i++) {
usleep(100000);
$this->timer->sleep(Duration::fromMilliseconds(100));
if ($i === 5) {
$spinner->setMessage('Fast fertig...');
@@ -59,7 +68,7 @@ class SpinnerExample
$spinner->start();
for ($i = 0; $i < 8; $i++) {
usleep(150000);
$this->timer->sleep(Duration::fromMilliseconds(150));
$spinner->update();
}

View File

@@ -1,11 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Examples;
use App\Framework\Console\Components\Table;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleFormat;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
@@ -13,7 +14,7 @@ use App\Framework\Console\ConsoleStyle;
final class TableExample
{
#[ConsoleCommand('demo:table', 'Zeigt eine Beispiel-Tabelle')]
##[ConsoleCommand('demo:table', 'Zeigt eine Beispiel-Tabelle')]
public function showTable(ConsoleInput $input, ConsoleOutput $output): int
{
$output->writeLine('Beispiel für die Table-Komponente', ConsoleStyle::create(

View File

@@ -1,11 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Examples;
use App\Framework\Console\Components\TextBox;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleFormat;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
@@ -13,7 +14,7 @@ use App\Framework\Console\ConsoleStyle;
final class TextBoxExample
{
#[ConsoleCommand('demo:textbox', 'Zeigt verschiedene TextBox-Beispiele')]
##[ConsoleCommand('demo:textbox', 'Zeigt verschiedene TextBox-Beispiele')]
public function showTextBox(ConsoleInput $input, ConsoleOutput $output): int
{
$output->writeLine('Beispiele für die TextBox-Komponente', ConsoleStyle::create(

View File

@@ -1,11 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Examples;
use App\Framework\Console\Components\TreeHelper;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\ConsoleFormat;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
@@ -13,7 +14,7 @@ use App\Framework\Console\ConsoleStyle;
final class TreeExample
{
#[ConsoleCommand('demo:tree', 'Zeigt ein Beispiel für die TreeHelper-Komponente')]
##[ConsoleCommand('demo:tree', 'Zeigt ein Beispiel für die TreeHelper-Komponente')]
public function showTreeExample(ConsoleInput $input, ConsoleOutput $output): int
{
$output->writeLine('Beispiel für den TreeHelper', ConsoleStyle::create(

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Exceptions;
class CommandNotFoundException extends ConsoleException

View File

@@ -1,7 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Exceptions;
class ConsoleException extends \Exception
use App\Framework\Exception\FrameworkException;
class ConsoleException extends FrameworkException
{
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
@@ -69,6 +70,6 @@ enum ExitCode: int
*/
public function isError(): bool
{
return !$this->isSuccess();
return ! $this->isSuccess();
}
}

View File

@@ -1,20 +1,33 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
class ProgressBar
{
private ConsoleOutputInterface $output;
private int $total;
private int $current = 0;
private int $width;
private float $startTime;
private string $format = '%bar% %percent%%';
private string $barChar = '=';
private string $emptyBarChar = '-';
private string $progressChar = '>';
private int $redrawFrequency = 1;
private int $writeCount = 0;
private bool $firstRun = true;
public function __construct(ConsoleOutputInterface $output, int $total = 100, int $width = 50)
@@ -38,6 +51,7 @@ class ProgressBar
public function setFormat(string $format): self
{
$this->format = $format;
return $this;
}
@@ -49,6 +63,7 @@ class ProgressBar
$this->barChar = $barChar;
$this->emptyBarChar = $emptyBarChar;
$this->progressChar = $progressChar;
return $this;
}
@@ -58,6 +73,7 @@ class ProgressBar
public function setRedrawFrequency(int $frequency): self
{
$this->redrawFrequency = max(1, $frequency);
return $this;
}
@@ -67,6 +83,7 @@ class ProgressBar
public function advance(int $step = 1): self
{
$this->setCurrent($this->current + $step);
return $this;
}
@@ -95,6 +112,7 @@ class ProgressBar
$this->current = 0;
$this->firstRun = true;
$this->display();
return $this;
}
@@ -118,7 +136,7 @@ class ProgressBar
*/
private function display(): void
{
if (!$this->firstRun) {
if (! $this->firstRun) {
// Bewege den Cursor eine Zeile nach oben
$this->output->write("\033[1A");
// Lösche die aktuelle Zeile

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Screen;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Screen;
@@ -8,11 +9,12 @@ use App\Framework\Console\ConsoleOutput;
/**
* Verantwortlich für Cursor-Positionierung.
*/
final class Cursor
final readonly class Cursor
{
public function __construct(
private ConsoleOutput $output
) {}
) {
}
/**
* Bewegt den Cursor zu einer bestimmten Position.
@@ -22,6 +24,7 @@ final class Cursor
if ($this->output->isTerminal()) {
$this->output->writeRaw(CursorControlCode::POSITION->format($row, $col));
}
return $this;
}
@@ -34,6 +37,7 @@ final class Cursor
// Die Home-Position ist 1,1 (obere linke Ecke)
$this->output->writeRaw(CursorControlCode::POSITION->format(1, 1));
}
return $this;
}
@@ -45,6 +49,7 @@ final class Cursor
if ($this->output->isTerminal() && $lines > 0) {
$this->output->writeRaw(CursorControlCode::UP->format($lines));
}
return $this;
}
@@ -56,6 +61,7 @@ final class Cursor
if ($this->output->isTerminal() && $lines > 0) {
$this->output->writeRaw(CursorControlCode::DOWN->format($lines));
}
return $this;
}
@@ -67,6 +73,7 @@ final class Cursor
if ($this->output->isTerminal() && $columns > 0) {
$this->output->writeRaw(CursorControlCode::LEFT->format($columns));
}
return $this;
}
@@ -78,6 +85,7 @@ final class Cursor
if ($this->output->isTerminal() && $columns > 0) {
$this->output->writeRaw(CursorControlCode::RIGHT->format($columns));
}
return $this;
}
@@ -89,6 +97,7 @@ final class Cursor
if ($this->output->isTerminal()) {
$this->output->writeRaw(CursorControlCode::HIDE->format());
}
return $this;
}
@@ -100,6 +109,7 @@ final class Cursor
if ($this->output->isTerminal()) {
$this->output->writeRaw(CursorControlCode::SHOW->format());
}
return $this;
}
@@ -111,6 +121,7 @@ final class Cursor
if ($this->output->isTerminal()) {
$this->output->writeRaw(CursorControlCode::SAVE->format());
}
return $this;
}
@@ -122,6 +133,7 @@ final class Cursor
if ($this->output->isTerminal()) {
$this->output->writeRaw(CursorControlCode::RESTORE->format());
}
return $this;
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Screen;
@@ -43,6 +44,7 @@ enum CursorControlCode: string
// Wenn Parameter vorhanden sind, formatieren
$paramStr = implode(';', $params);
return "\033[{$paramStr}{$this->value}";
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Screen;
@@ -12,7 +13,8 @@ final readonly class Display
{
public function __construct(
private ConsoleOutput $output
) {}
) {
}
/**
* Löscht den gesamten Bildschirm und setzt den Cursor an den Anfang.
@@ -24,6 +26,7 @@ final readonly class Display
$this->output->writeRaw(ScreenControlCode::CLEAR_ALL->format());
$this->output->writeRaw(CursorControlCode::POSITION->format(1, 1));
}
return $this;
}
@@ -36,6 +39,7 @@ final readonly class Display
$this->output->writeRaw(ScreenControlCode::CLEAR_LINE->format());
$this->output->writeRaw("\r"); // Cursor an den Zeilenanfang
}
return $this;
}
@@ -47,6 +51,7 @@ final readonly class Display
if ($this->output->isTerminal()) {
$this->output->writeRaw(ScreenControlCode::CLEAR_BELOW->format());
}
return $this;
}
@@ -58,6 +63,7 @@ final readonly class Display
if ($this->output->isTerminal()) {
$this->output->writeRaw(ScreenControlCode::CLEAR_ABOVE->format());
}
return $this;
}
@@ -69,6 +75,7 @@ final readonly class Display
if ($this->output->isTerminal()) {
$this->output->writeRaw(ScreenControlCode::CLEAR_LINE_RIGHT->format());
}
return $this;
}
@@ -80,6 +87,7 @@ final readonly class Display
if ($this->output->isTerminal()) {
$this->output->writeRaw(ScreenControlCode::CLEAR_LINE_LEFT->format());
}
return $this;
}
@@ -91,6 +99,7 @@ final readonly class Display
if ($this->output->isTerminal()) {
$this->output->writeRaw(ScreenControlCode::ALTERNATE_BUFFER->format());
}
return $this;
}
@@ -102,6 +111,7 @@ final readonly class Display
if ($this->output->isTerminal()) {
$this->output->writeRaw(ScreenControlCode::MAIN_BUFFER->format());
}
return $this;
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Screen;
@@ -33,6 +34,7 @@ enum ScreenControlCode: string
// Wenn Parameter vorhanden sind, formatieren
$paramStr = implode(';', $params);
return "\033[{$paramStr}{$this->value}";
}
}

View File

@@ -1,9 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Screen;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\Timer;
/**
* Verwaltet intelligentes Bildschirm-Management.
@@ -11,13 +14,17 @@ use App\Framework\Console\ConsoleOutput;
final class ScreenManager
{
private ClearStrategy $strategy = ClearStrategy::SMART;
private bool $interactiveMode = false;
private ?ScreenType $lastScreenType = null;
private int $screenCount = 0;
public function __construct(
private ConsoleOutput $output
) {}
private readonly ConsoleOutput $output
) {
}
/**
* Setzt die Löschstrategie.
@@ -25,6 +32,7 @@ final class ScreenManager
public function setStrategy(ClearStrategy $strategy): self
{
$this->strategy = $strategy;
return $this;
}
@@ -34,6 +42,7 @@ final class ScreenManager
public function setInteractiveMode(bool $interactive = true): self
{
$this->interactiveMode = $interactive;
return $this;
}
@@ -41,10 +50,10 @@ final class ScreenManager
* Markiert den Beginn eines neuen Bildschirms.
* Diese Methode rufst du VOR der Ausgabe auf.
*/
public function newScreen(ScreenType $type = ScreenType::CONTENT): self
public function new(ScreenType $type = ScreenType::CONTENT): self
{
if ($this->shouldClear($type)) {
$this->output->display()->clear();
$this->output->display->clear();
}
$this->lastScreenType = $type;
@@ -58,37 +67,38 @@ final class ScreenManager
*/
public function newMenu(): self
{
return $this->newScreen(ScreenType::MENU);
return $this->new(ScreenType::MENU);
}
public function newDialog(): self
{
return $this->newScreen(ScreenType::DIALOG);
return $this->new(ScreenType::DIALOG);
}
public function newContent(): self
{
return $this->newScreen(ScreenType::CONTENT);
return $this->new(ScreenType::CONTENT);
}
public function newLog(): self
{
return $this->newScreen(ScreenType::LOG);
return $this->new(ScreenType::LOG);
}
public function newProgress(): self
{
return $this->newScreen(ScreenType::PROGRESS);
return $this->new(ScreenType::PROGRESS);
}
/**
* Zeigt eine temporäre Nachricht für eine bestimmte Zeit an.
*/
public function temporary(string $message, int $seconds = 2): self
public function temporary(string $message, Timer $timer, int $seconds = 2): self
{
$this->output->writeLine($message);
sleep($seconds);
$this->output->display()->clearLine();
$timer->sleep(Duration::fromSeconds($seconds));
$this->output->display->clearLine();
return $this;
}
@@ -100,6 +110,7 @@ final class ScreenManager
if ($this->output->isTerminal()) {
fread(STDIN, 1);
}
return $this;
}
@@ -108,7 +119,7 @@ final class ScreenManager
*/
private function shouldClear(ScreenType $type): bool
{
if (!$this->output->isTerminal()) {
if (! $this->output->isTerminal()) {
return false;
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console\Screen;

View File

@@ -1,16 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
class Spinner
{
private ConsoleOutputInterface $output;
private string $message;
private array $frames;
private int $currentFrame = 0;
private float $startTime;
private bool $active = false;
private float $interval;
private int $updateCount = 0;
public function __construct(
@@ -33,6 +42,7 @@ class Spinner
$this->startTime = microtime(true);
$this->active = true;
$this->update();
return $this;
}
@@ -78,6 +88,7 @@ class Spinner
if ($this->active) {
$this->update();
}
return $this;
}
@@ -86,7 +97,7 @@ class Spinner
*/
public function update(): self
{
if (!$this->active) {
if (! $this->active) {
return $this;
}

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Framework\Console;
enum SpinnerStyle: string