Files
michaelschiemer/src/Framework/Display/Components/Console/TreeHelper.php
Michael Schiemer 36ef2a1e2c
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
fix: Gitea Traefik routing and connection pool optimization
- Remove middleware reference from Gitea Traefik labels (caused routing issues)
- Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s)
- Add explicit service reference in Traefik labels
- Fix intermittent 504 timeouts by improving PostgreSQL connection handling

Fixes Gitea unreachability via git.michaelschiemer.de
2025-11-09 14:46:15 +01:00

284 lines
8.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Display\Components\Console;
use App\Framework\Console\ConsoleColor;
use App\Framework\Console\ConsoleFormat;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ConsoleStyle;
use App\Framework\Display\Components\DisplayComponentInterface;
use App\Framework\Display\ValueObjects\ArrayStructure;
use App\Framework\Display\ValueObjects\ObjectStructure;
use App\Framework\Display\ValueObjects\OutputFormat;
/**
* TreeHelper zum Anzeigen hierarchischer Baumstrukturen in der Konsole.
* Ähnlich dem Symfony TreeHelper, aber angepasst an unser Styling-System.
*/
final class TreeHelper implements DisplayComponentInterface
{
private string $prefix = '';
private bool $isLastElement = true;
private ?ConsoleStyle $nodeStyle = null;
private ?ConsoleStyle $leafStyle = null;
private ?ConsoleStyle $lineStyle = null;
/**
* @var array<array{title: string, node: ?self, isLeaf: bool}>
*/
private array $nodes = [];
public function __construct(
private string $title = '',
#private readonly ConsoleOutput $output = new ConsoleOutput(),
) {
$this->nodeStyle = ConsoleStyle::create(color: ConsoleColor::BRIGHT_YELLOW, format: ConsoleFormat::BOLD);
$this->leafStyle = ConsoleStyle::create(color: ConsoleColor::WHITE);
$this->lineStyle = ConsoleStyle::create(color: ConsoleColor::GRAY);
}
/**
* Setzt den Stil für Knotentitel (Verzeichnisse/Kategorien).
*/
public function setNodeStyle(?ConsoleStyle $style): self
{
$this->nodeStyle = $style;
return $this;
}
/**
* Setzt den Stil für Blätter/Endpunkte.
*/
public function setLeafStyle(?ConsoleStyle $style): self
{
$this->leafStyle = $style;
return $this;
}
/**
* Setzt den Stil für Baumlinien.
*/
public function setLineStyle(?ConsoleStyle $style): self
{
$this->lineStyle = $style;
return $this;
}
/**
* Setzt den Haupttitel des Baums.
*/
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
/**
* Fügt einen Unterknoten (z.B. Unterverzeichnis) hinzu.
*/
public function addNode(string $title): self
{
$node = new self($title);
$node->nodeStyle = $this->nodeStyle;
$node->leafStyle = $this->leafStyle;
$node->lineStyle = $this->lineStyle;
$this->nodes[] = [
'title' => $title,
'node' => $node,
'isLeaf' => false,
];
return $node;
}
/**
* Fügt einen Endpunkt (z.B. Datei) hinzu.
*/
public function addLeaf(string $title): self
{
$this->nodes[] = [
'title' => $title,
'node' => null,
'isLeaf' => true,
];
return $this;
}
/**
* Zeigt die vollständige Baumstruktur an.
*/
public function display(ConsoleOutput $output): void
{
if (! empty($this->title)) {
$output->writeLine($this->title, $this->nodeStyle);
}
$this->displayTree($output);
}
/**
* Rendert die Baumstruktur und gibt den Text zurück.
*/
public function render(): string
{
$output = '';
if (! empty($this->title)) {
$output .= $this->nodeStyle->apply($this->title) . "\n";
}
$output .= $this->renderTree();
return $output;
}
/**
* Setzt den Präfix für die aktuelle Ebene.
* (Interne Methode für rekursives Rendern)
*/
private function setPrefix(string $prefix, bool $isLastElement): self
{
$this->prefix = $prefix;
$this->isLastElement = $isLastElement;
return $this;
}
/**
* Zeigt die Baumstruktur mit dem aktuellen Präfix an.
* (Interne Methode für rekursives Rendern)
*/
private function displayTree(ConsoleOutput $output): void
{
$count = count($this->nodes);
foreach ($this->nodes as $index => $item) {
$isLast = ($index === $count - 1);
$nodePrefix = $this->prefix . ($this->isLastElement ? ' ' : '│ ');
// Baumlinien und Verbindungen
$connector = $isLast ? '└── ' : '├── ';
$linePrefix = $this->prefix . $connector;
// Titel anzeigen
$style = $item['isLeaf'] ? $this->leafStyle : $this->nodeStyle;
$title = $linePrefix . $item['title'];
$output->writeLine(
$this->lineStyle->apply($linePrefix) .
$style->apply($item['title'])
);
// Unterelemente rekursiv anzeigen
if (! $item['isLeaf'] && $item['node'] !== null) {
$item['node']
->setPrefix($nodePrefix, $isLast)
->displayTree($output);
}
}
}
/**
* Rendert die Baumstruktur mit dem aktuellen Präfix und gibt den Text zurück.
* (Interne Methode für rekursives Rendern)
*/
private function renderTree(): string
{
$output = '';
$count = count($this->nodes);
foreach ($this->nodes as $index => $item) {
$isLast = ($index === $count - 1);
$nodePrefix = $this->prefix . ($this->isLastElement ? ' ' : '│ ');
// Baumlinien und Verbindungen
$connector = $isLast ? '└── ' : '├── ';
$linePrefix = $this->prefix . $connector;
// Titel formatieren
$style = $item['isLeaf'] ? $this->leafStyle : $this->nodeStyle;
$title = $item['title'];
$output .= $this->lineStyle->apply($linePrefix) .
$style->apply($title) . "\n";
// Unterelemente rekursiv rendern
if (! $item['isLeaf'] && $item['node'] !== null) {
$childOutput = $item['node']
->setPrefix($nodePrefix, $isLast)
->renderTree();
$output .= $childOutput;
}
}
return $output;
}
public function getOutputFormat(): OutputFormat
{
return OutputFormat::CONSOLE;
}
/**
* Build tree from Display ArrayStructure
*/
public function fromArrayStructure(ArrayStructure $structure, string $separator = ': '): self
{
foreach ($structure->items as $key => $value) {
if (isset($structure->nestedArrays[$key])) {
$nested = $structure->nestedArrays[$key];
if ($nested->isCircularReference) {
$this->addLeaf((string) $key . $separator . '[circular reference]');
} else {
$node = $this->addNode((string) $key);
$node->fromArrayStructure($nested, $separator);
}
} else {
$valueStr = is_scalar($value) ? (string) $value : get_debug_type($value);
$this->addLeaf((string) $key . $separator . $valueStr);
}
}
return $this;
}
/**
* Build tree from Display ObjectStructure
*/
public function fromObjectStructure(ObjectStructure $structure, string $separator = ': '): self
{
foreach ($structure->properties as $property) {
if (isset($structure->nestedObjects[$property->name])) {
$nested = $structure->nestedObjects[$property->name];
if ($nested->isCircularReference) {
$this->addLeaf($property->name . $separator . '[circular reference]');
} else {
$node = $this->addNode($property->name . $separator . $nested->className);
$node->fromObjectStructure($nested, $separator);
}
} else {
$valueStr = $property->isValueObject && is_object($property->value)
? (string) $property->value
: (is_scalar($property->value) ? (string) $property->value : get_debug_type($property->value));
$this->addLeaf($property->name . $separator . $valueStr);
}
}
return $this;
}
}