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
- 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
254 lines
9.6 KiB
PHP
254 lines
9.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Display\Formatters;
|
|
|
|
use App\Framework\Display\Formatters\CsvFormatter;
|
|
use App\Framework\Display\Formatters\JsonFormatter;
|
|
use App\Framework\Display\Formatters\XmlFormatter;
|
|
use App\Framework\Display\Formatters\YamlFormatter;
|
|
use App\Framework\Display\ValueObjects\DisplayOptions;
|
|
use App\Framework\Display\ValueObjects\OutputFormat;
|
|
use App\Framework\Filesystem\ValueObjects\FilePath;
|
|
|
|
/**
|
|
* Auto-detecting formatter that automatically selects the appropriate formatter
|
|
* based on the input data type.
|
|
*
|
|
* This is the main entry point for the Display module. It automatically detects
|
|
* the type of input data and routes it to the appropriate formatter:
|
|
* - Arrays → ArrayFormatter
|
|
* - Objects → ObjectFormatter
|
|
* - JSON strings → JsonFormatter
|
|
* - YAML strings → YamlFormatter
|
|
* - XML strings → XmlFormatter
|
|
* - CSV strings → CsvFormatter
|
|
* - Class names → ClassFormatter
|
|
* - Directory paths → FilesystemFormatter
|
|
* - Scalars → Scalar formatting
|
|
*
|
|
* @example
|
|
* $formatter = new AutoFormatter();
|
|
* echo $formatter->formatForConsole($data);
|
|
*/
|
|
final readonly class AutoFormatter
|
|
{
|
|
public function __construct(
|
|
private ArrayFormatter $arrayFormatter = new ArrayFormatter(),
|
|
private ObjectFormatter $objectFormatter = new ObjectFormatter(),
|
|
private ClassFormatter $classFormatter = new ClassFormatter(),
|
|
private FilesystemFormatter $filesystemFormatter = new FilesystemFormatter(),
|
|
private JsonFormatter $jsonFormatter = new JsonFormatter(),
|
|
private YamlFormatter $yamlFormatter = new YamlFormatter(),
|
|
private XmlFormatter $xmlFormatter = new XmlFormatter(),
|
|
private CsvFormatter $csvFormatter = new CsvFormatter(),
|
|
private CodeFormatter $codeFormatter = new CodeFormatter(),
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Format data with specified options and output format.
|
|
*
|
|
* @param mixed $value The data to format (array, object, string, etc.)
|
|
* @param DisplayOptions $options Display configuration options
|
|
* @param OutputFormat $format Output format (CONSOLE or HTML)
|
|
* @return string Formatted output string
|
|
*/
|
|
public function format(mixed $value, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
return match (true) {
|
|
is_array($value) => $this->formatArray($value, $options, $format),
|
|
is_object($value) => $this->formatObject($value, $options, $format),
|
|
is_string($value) && $this->isJsonString($value) => $this->formatJsonString($value, $options, $format),
|
|
is_string($value) && $this->isYamlString($value) => $this->formatYamlString($value, $options, $format),
|
|
is_string($value) && $this->isXmlString($value) => $this->formatXmlString($value, $options, $format),
|
|
is_string($value) && $this->isCsvString($value) => $this->formatCsvString($value, $options, $format),
|
|
is_string($value) && CodeFormatter::isPhpCode($value) => $this->formatCode($value, $options, $format),
|
|
is_string($value) && class_exists($value) => $this->formatClass($value, $options, $format),
|
|
is_string($value) && (is_dir($value) || ($value instanceof FilePath && is_dir($value->toString()))) => $this->formatDirectory($value, $options, $format),
|
|
$value instanceof FilePath => $this->formatDirectory($value, $options, $format),
|
|
default => $this->formatScalar($value, $options, $format),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Format data for console output with default options.
|
|
*
|
|
* @param mixed $value The data to format
|
|
* @param DisplayOptions|null $options Optional display options (defaults to DisplayOptions::default())
|
|
* @return string Formatted console output with ANSI colors
|
|
*/
|
|
public function formatForConsole(mixed $value, ?DisplayOptions $options = null): string
|
|
{
|
|
$options ??= DisplayOptions::default();
|
|
|
|
return $this->format($value, $options, OutputFormat::CONSOLE);
|
|
}
|
|
|
|
/**
|
|
* Format data for HTML output with default options.
|
|
*
|
|
* @param mixed $value The data to format
|
|
* @param DisplayOptions|null $options Optional display options (defaults to DisplayOptions::default())
|
|
* @return string Formatted HTML output with semantic HTML/CSS classes
|
|
*/
|
|
public function formatForHtml(mixed $value, ?DisplayOptions $options = null): string
|
|
{
|
|
$options ??= DisplayOptions::default();
|
|
|
|
return $this->format($value, $options, OutputFormat::HTML);
|
|
}
|
|
|
|
private function formatArray(array $value, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
// Check if array contains objects - could use ObjectFormatter for items
|
|
$hasObjects = false;
|
|
foreach ($value as $item) {
|
|
if (is_object($item)) {
|
|
$hasObjects = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $this->arrayFormatter->format($value, $options, $format);
|
|
}
|
|
|
|
private function formatObject(object $value, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
return $this->objectFormatter->format($value, $options, $format);
|
|
}
|
|
|
|
private function formatJsonString(string $value, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
return $this->jsonFormatter->format($value, $options, $format);
|
|
}
|
|
|
|
private function formatYamlString(string $value, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
return $this->yamlFormatter->format($value, $options, $format);
|
|
}
|
|
|
|
private function formatXmlString(string $value, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
return $this->xmlFormatter->format($value, $options, $format);
|
|
}
|
|
|
|
private function formatCsvString(string $value, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
return $this->csvFormatter->format($value, $options, $format);
|
|
}
|
|
|
|
private function formatCode(string $value, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
return $this->codeFormatter->format($value, $options, $format);
|
|
}
|
|
|
|
private function formatClass(string $className, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
return $this->classFormatter->format($className, $options, $format);
|
|
}
|
|
|
|
private function formatDirectory(string|FilePath $path, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
return $this->filesystemFormatter->format($path, $options, $format);
|
|
}
|
|
|
|
private function formatScalar(mixed $value, DisplayOptions $options, OutputFormat $format): string
|
|
{
|
|
$formatted = match (true) {
|
|
is_string($value) => $format === OutputFormat::HTML
|
|
? '<span class="display-string">' . htmlspecialchars($value) . '</span>'
|
|
: "\033[32m\"{$value}\"\033[0m",
|
|
is_int($value) => $format === OutputFormat::HTML
|
|
? '<span class="display-number">' . $value . '</span>'
|
|
: "\033[94m{$value}\033[0m",
|
|
is_float($value) => $format === OutputFormat::HTML
|
|
? '<span class="display-number">' . $value . '</span>'
|
|
: "\033[94m{$value}\033[0m",
|
|
is_bool($value) => $format === OutputFormat::HTML
|
|
? '<span class="display-boolean">' . ($value ? 'true' : 'false') . '</span>'
|
|
: "\033[95m" . ($value ? 'true' : 'false') . "\033[0m",
|
|
is_null($value) => $format === OutputFormat::HTML
|
|
? '<span class="display-null">null</span>'
|
|
: "\033[90mnull\033[0m",
|
|
default => $format === OutputFormat::HTML
|
|
? '<span class="display-type">' . htmlspecialchars(get_debug_type($value)) . '</span>'
|
|
: get_debug_type($value),
|
|
};
|
|
|
|
return $formatted;
|
|
}
|
|
|
|
private function isJsonString(string $value): bool
|
|
{
|
|
if (empty($value)) {
|
|
return false;
|
|
}
|
|
|
|
$trimmed = trim($value);
|
|
if (! ($trimmed[0] === '{' || $trimmed[0] === '[')) {
|
|
return false;
|
|
}
|
|
|
|
return json_validate($value);
|
|
}
|
|
|
|
private function isYamlString(string $value): bool
|
|
{
|
|
if (empty($value)) {
|
|
return false;
|
|
}
|
|
|
|
$trimmed = trim($value);
|
|
|
|
// Basic YAML detection: starts with key: or has --- or ...
|
|
// More sophisticated detection would require a YAML parser
|
|
return preg_match('/^(\s*[a-zA-Z_][a-zA-Z0-9_]*\s*:|\s*---|\s*\.\.\.)/', $trimmed) === 1;
|
|
}
|
|
|
|
private function isXmlString(string $value): bool
|
|
{
|
|
if (empty($value)) {
|
|
return false;
|
|
}
|
|
|
|
$trimmed = trim($value);
|
|
|
|
// Basic XML detection: starts with < or <?xml
|
|
return str_starts_with($trimmed, '<') || str_starts_with($trimmed, '<?xml');
|
|
}
|
|
|
|
private function isCsvString(string $value): bool
|
|
{
|
|
if (empty($value)) {
|
|
return false;
|
|
}
|
|
|
|
$lines = explode("\n", $value);
|
|
if (count($lines) < 2) {
|
|
return false;
|
|
}
|
|
|
|
// Check if the first line contains common CSV separators
|
|
$firstLine = $lines[0];
|
|
$separators = [',', ';', "\t"];
|
|
|
|
foreach ($separators as $separator) {
|
|
$parts = explode($separator, $firstLine);
|
|
if (count($parts) >= 2) {
|
|
// Check if second line has same number of parts
|
|
if (isset($lines[1])) {
|
|
$secondParts = explode($separator, $lines[1]);
|
|
if (count($secondParts) >= 2 && abs(count($parts) - count($secondParts)) <= 1) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|