chore: complete update
This commit is contained in:
147
src/Framework/Debug/DebugEntry.php
Normal file
147
src/Framework/Debug/DebugEntry.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug;
|
||||
|
||||
/**
|
||||
* Repräsentiert einen einzelnen Debug-Eintrag mit allen relevanten Informationen.
|
||||
*/
|
||||
final class DebugEntry
|
||||
{
|
||||
/** @var \DateTimeImmutable */
|
||||
private \DateTimeImmutable $timestamp;
|
||||
|
||||
/** @var string */
|
||||
private string $memory;
|
||||
|
||||
/** @var string */
|
||||
private string $peakMemory;
|
||||
|
||||
/**
|
||||
* Erstellt einen neuen Debug-Eintrag
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly mixed $data,
|
||||
private readonly string $label,
|
||||
private readonly string $file,
|
||||
private readonly int $line,
|
||||
private readonly EntryType $type = EntryType::STANDARD
|
||||
) {
|
||||
$this->timestamp = new \DateTimeImmutable(timezone: new \DateTimeZone('Europe/Berlin'));
|
||||
$this->memory = self::formatBytes(memory_get_usage(true));
|
||||
$this->peakMemory = self::formatBytes(memory_get_peak_usage(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Debug-Daten zurück
|
||||
*/
|
||||
public function getData(): mixed
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt das Label zurück
|
||||
*/
|
||||
public function getLabel(): string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Pfad der Datei zurück
|
||||
*/
|
||||
public function getFile(): string
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Zeilennummer zurück
|
||||
*/
|
||||
public function getLine(): int
|
||||
{
|
||||
return $this->line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Dateinamen ohne Pfad zurück
|
||||
*/
|
||||
public function getFilename(): string
|
||||
{
|
||||
return basename($this->file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Zeitstempel zurück
|
||||
*/
|
||||
public function getTimestamp(): \DateTimeImmutable
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den formatierten Zeitstempel zurück
|
||||
*/
|
||||
public function getFormattedTimestamp(string $format = 'Y-m-d H:i:s'): string
|
||||
{
|
||||
return $this->timestamp->format($format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den aktuellen Speicherverbrauch zurück
|
||||
*/
|
||||
public function getMemory(): string
|
||||
{
|
||||
return $this->memory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den höchsten Speicherverbrauch zurück
|
||||
*/
|
||||
public function getPeakMemory(): string
|
||||
{
|
||||
return $this->peakMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Typ des Eintrags zurück
|
||||
*/
|
||||
public function getType(): EntryType
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt ein Array mit allen Debug-Daten
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'data' => $this->data,
|
||||
'label' => $this->label,
|
||||
'file' => $this->file,
|
||||
'line' => $this->line,
|
||||
'timestamp' => $this->getFormattedTimestamp(),
|
||||
'memory' => $this->memory,
|
||||
'peak_memory' => $this->peakMemory,
|
||||
'type' => $this->type->name,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatiert Bytes in lesbare Größen
|
||||
*/
|
||||
private static function formatBytes(int $bytes): string
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB'];
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
$bytes /= (1 << (10 * $pow));
|
||||
|
||||
return round($bytes, 2) . ' ' . $units[$pow];
|
||||
}
|
||||
}
|
||||
69
src/Framework/Debug/DebugRegistry.php
Normal file
69
src/Framework/Debug/DebugRegistry.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug;
|
||||
|
||||
/**
|
||||
* Registry-Klasse zum Speichern aller Debug-Einträge.
|
||||
*/
|
||||
final class DebugRegistry
|
||||
{
|
||||
/** @var DebugEntry[] */
|
||||
private array $entries = [];
|
||||
|
||||
/** @var int */
|
||||
private int $maxEntries = 100;
|
||||
|
||||
/**
|
||||
* Fügt einen Debug-Eintrag hinzu
|
||||
*/
|
||||
public function addEntry(DebugEntry $entry): void
|
||||
{
|
||||
// Bei Überschreitung der maximalen Anzahl, ältesten Eintrag entfernen
|
||||
if (count($this->entries) >= $this->maxEntries) {
|
||||
array_shift($this->entries);
|
||||
}
|
||||
|
||||
$this->entries[] = $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle gespeicherten Debug-Einträge zurück
|
||||
*
|
||||
* @return DebugEntry[]
|
||||
*/
|
||||
public function getEntries(): array
|
||||
{
|
||||
return $this->entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Anzahl der gespeicherten Einträge zurück
|
||||
*/
|
||||
public function getCount(): int
|
||||
{
|
||||
return count($this->entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht alle gespeicherten Einträge
|
||||
*/
|
||||
public function clear(): void
|
||||
{
|
||||
$this->entries = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt die maximale Anzahl an Einträgen
|
||||
*/
|
||||
public function setMaxEntries(int $maxEntries): void
|
||||
{
|
||||
$this->maxEntries = max(1, $maxEntries);
|
||||
|
||||
// Überschüssige Einträge entfernen
|
||||
if (count($this->entries) > $this->maxEntries) {
|
||||
$this->entries = array_slice($this->entries, -$this->maxEntries);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/Framework/Debug/DebugServiceProvider.php
Normal file
51
src/Framework/Debug/DebugServiceProvider.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug;
|
||||
|
||||
use App\Framework\Debug\Formatters\ConsoleFormatter;
|
||||
use App\Framework\Debug\Formatters\HtmlFormatter;
|
||||
use App\Framework\Debug\Outputs\ConsoleOutput;
|
||||
use App\Framework\Debug\Outputs\HtmlOutput;
|
||||
|
||||
/**
|
||||
* Service Provider für die Einbindung des Debug-Moduls.
|
||||
*/
|
||||
final class DebugServiceProvider
|
||||
{
|
||||
/**
|
||||
* Registriert das Debug-Modul in der Anwendung.
|
||||
*/
|
||||
public function register(bool $enabled = true): void
|
||||
{
|
||||
// Standardkonfiguration basierend auf Umgebung
|
||||
$isConsole = PHP_SAPI === 'cli';
|
||||
|
||||
$output = $isConsole ? new ConsoleOutput() : new HtmlOutput();
|
||||
$formatter = $isConsole ? new ConsoleFormatter() : new HtmlFormatter();
|
||||
|
||||
Debugger::configure($enabled, $output, $formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguriert das Debug-Modul für die Produktionsumgebung.
|
||||
*/
|
||||
public function configureForProduction(): void
|
||||
{
|
||||
Debugger::disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguriert das Debug-Modul für die Entwicklungsumgebung.
|
||||
*/
|
||||
public function configureForDevelopment(): void
|
||||
{
|
||||
$isConsole = PHP_SAPI === 'cli';
|
||||
|
||||
$output = $isConsole ? new ConsoleOutput() : new HtmlOutput();
|
||||
$formatter = $isConsole ? new ConsoleFormatter() : new HtmlFormatter();
|
||||
|
||||
Debugger::configure(true, $output, $formatter);
|
||||
}
|
||||
}
|
||||
237
src/Framework/Debug/Debugger.php
Normal file
237
src/Framework/Debug/Debugger.php
Normal file
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug;
|
||||
|
||||
use App\Framework\Debug\Formatters\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Hauptklasse für das Framework-weite Debugging.
|
||||
* Bietet eine übersichtlichere Alternative zu var_dump.
|
||||
*/
|
||||
final class Debugger
|
||||
{
|
||||
/** @var bool */
|
||||
private static bool $enabled = true;
|
||||
|
||||
/** @var OutputInterface|null */
|
||||
private static ?OutputInterface $output = null;
|
||||
|
||||
/** @var FormatterInterface|null */
|
||||
private static ?FormatterInterface $formatter = null;
|
||||
|
||||
/** @var DebugRegistry */
|
||||
private static ?DebugRegistry $registry = null;
|
||||
|
||||
/**
|
||||
* Hauptfunktion für das Debugging - übersichtlichere Alternative zu var_dump
|
||||
*/
|
||||
public static function dump(mixed $data, string $label = '', bool $die = false): void
|
||||
{
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
|
||||
$caller = $backtrace[0] ?? [];
|
||||
|
||||
$debugInfo = new DebugEntry(
|
||||
$data,
|
||||
$label,
|
||||
$caller['file'] ?? 'unknown',
|
||||
$caller['line'] ?? 0
|
||||
);
|
||||
|
||||
self::getRegistry()->addEntry($debugInfo);
|
||||
self::getOutput()->output($debugInfo, self::getFormatter());
|
||||
|
||||
if ($die) {
|
||||
die(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spezielle Funktion für Arrays mit Formatierung
|
||||
*/
|
||||
public static function dumpArray(array $data, string $label = 'Array Debug'): void
|
||||
{
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
|
||||
$caller = $backtrace[0] ?? [];
|
||||
|
||||
$debugInfo = new DebugEntry(
|
||||
$data,
|
||||
$label ?: 'Array Debug',
|
||||
$caller['file'] ?? 'unknown',
|
||||
$caller['line'] ?? 0,
|
||||
EntryType::ARRAY
|
||||
);
|
||||
|
||||
self::getRegistry()->addEntry($debugInfo);
|
||||
self::getOutput()->output($debugInfo, self::getFormatter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug für Objekte mit Klasseninformationen
|
||||
*/
|
||||
public static function dumpObject(object $object, string $label = ''): void
|
||||
{
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
|
||||
$caller = $backtrace[0] ?? [];
|
||||
$reflection = new \ReflectionClass($object);
|
||||
|
||||
$objectInfo = [
|
||||
'class' => $reflection->getName(),
|
||||
'methods' => array_map(fn($method) => $method->getName(), $reflection->getMethods()),
|
||||
'properties' => array_map(fn($prop) => $prop->getName(), $reflection->getProperties()),
|
||||
'data' => $object
|
||||
];
|
||||
|
||||
$debugInfo = new DebugEntry(
|
||||
$objectInfo,
|
||||
$label ?: 'Object: ' . $reflection->getShortName(),
|
||||
$caller['file'] ?? 'unknown',
|
||||
$caller['line'] ?? 0,
|
||||
EntryType::OBJECT
|
||||
);
|
||||
|
||||
self::getRegistry()->addEntry($debugInfo);
|
||||
self::getOutput()->output($debugInfo, self::getFormatter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Einfaches Logging für Debugging-Zwecke
|
||||
*/
|
||||
public static function log(string $message, string $level = 'DEBUG'): void
|
||||
{
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
|
||||
$caller = $backtrace[0] ?? [];
|
||||
|
||||
$debugInfo = new DebugEntry(
|
||||
$message,
|
||||
$level,
|
||||
$caller['file'] ?? 'unknown',
|
||||
$caller['line'] ?? 0,
|
||||
EntryType::LOG
|
||||
);
|
||||
|
||||
self::getRegistry()->addEntry($debugInfo);
|
||||
self::getOutput()->output($debugInfo, self::getFormatter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt eine Übersicht aller Debug-Aufrufe
|
||||
*/
|
||||
public static function showHistory(): void
|
||||
{
|
||||
if (!self::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entries = self::getRegistry()->getEntries();
|
||||
|
||||
if (empty($entries)) {
|
||||
self::getOutput()->outputRaw("Keine Debug-Historie vorhanden.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
self::getOutput()->outputHistory($entries, self::getFormatter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Konfiguration des Debuggers
|
||||
*/
|
||||
public static function configure(
|
||||
bool $enabled = true,
|
||||
?OutputInterface $output = null,
|
||||
?FormatterInterface $formatter = null
|
||||
): void {
|
||||
self::$enabled = $enabled;
|
||||
|
||||
if ($output !== null) {
|
||||
self::$output = $output;
|
||||
}
|
||||
|
||||
if ($formatter !== null) {
|
||||
self::$formatter = $formatter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob der Debugger aktiviert ist
|
||||
*/
|
||||
public static function isEnabled(): bool
|
||||
{
|
||||
return self::$enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deaktiviert den Debugger
|
||||
*/
|
||||
public static function disable(): void
|
||||
{
|
||||
self::$enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktiviert den Debugger
|
||||
*/
|
||||
public static function enable(): void
|
||||
{
|
||||
self::$enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt das Output-Objekt zurück
|
||||
*/
|
||||
private static function getOutput(): OutputInterface
|
||||
{
|
||||
if (self::$output === null) {
|
||||
$isConsole = PHP_SAPI === 'cli';
|
||||
self::$output = $isConsole
|
||||
? new Outputs\ConsoleOutput()
|
||||
: new Outputs\HtmlOutput();
|
||||
}
|
||||
|
||||
return self::$output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Formatter zurück
|
||||
*/
|
||||
private static function getFormatter(): FormatterInterface
|
||||
{
|
||||
if (self::$formatter === null) {
|
||||
$isConsole = PHP_SAPI === 'cli';
|
||||
self::$formatter = $isConsole
|
||||
? new Formatters\ConsoleFormatter()
|
||||
: new Formatters\HtmlFormatter();
|
||||
}
|
||||
|
||||
return self::$formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Registry zurück
|
||||
*/
|
||||
private static function getRegistry(): DebugRegistry
|
||||
{
|
||||
if (self::$registry === null) {
|
||||
self::$registry = new DebugRegistry();
|
||||
}
|
||||
|
||||
return self::$registry;
|
||||
}
|
||||
}
|
||||
17
src/Framework/Debug/EntryType.php
Normal file
17
src/Framework/Debug/EntryType.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug;
|
||||
|
||||
/**
|
||||
* Enum für verschiedene Debug-Eintragstypen.
|
||||
*/
|
||||
enum EntryType
|
||||
{
|
||||
case STANDARD;
|
||||
case ARRAY;
|
||||
case OBJECT;
|
||||
case LOG;
|
||||
case TRACE;
|
||||
}
|
||||
101
src/Framework/Debug/Formatters/ConsoleFormatter.php
Normal file
101
src/Framework/Debug/Formatters/ConsoleFormatter.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug\Formatters;
|
||||
|
||||
use App\Framework\Console\ConsoleColor;
|
||||
use App\Framework\Debug\DebugEntry;
|
||||
use App\Framework\Debug\EntryType;
|
||||
|
||||
/**
|
||||
* Formatiert Debug-Einträge für Konsolen-Ausgabe.
|
||||
*/
|
||||
final class ConsoleFormatter implements FormatterInterface
|
||||
{
|
||||
/** Farbdefinitionen für die Konsole */
|
||||
private const RESET = ConsoleColor::YELLOW->value; # "\033[0m";
|
||||
private const BOLD = "\033[1m";
|
||||
private const GREEN = ConsoleColor::YELLOW->value; # "\033[32m";
|
||||
private const string YELLOW = ConsoleColor::YELLOW->value; # "\033[33m";
|
||||
private const string BLUE = ConsoleColor::BLUE->value; # "\033[34m";
|
||||
private const string MAGENTA = ConsoleColor::MAGENTA->value; # "\033[35m";
|
||||
private const string CYAN = ConsoleColor::CYAN->value; # "\033[36m";
|
||||
private const string GRAY = ConsoleColor::GRAY->value; # = "\033[90m";
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function format(DebugEntry $entry): string
|
||||
{
|
||||
$output = self::BOLD . str_repeat('=', 80) . self::RESET . "\n";
|
||||
$output .= self::BOLD . self::GREEN . "DEBUG: " . self::RESET;
|
||||
$output .= self::BOLD . ($entry->getLabel() ?: 'Debug Output') . self::RESET . "\n";
|
||||
$output .= self::YELLOW . "File: " . self::RESET . $entry->getFile() . ":" . $entry->getLine() . "\n";
|
||||
$output .= self::CYAN . "Time: " . self::RESET . $entry->getFormattedTimestamp() . "\n";
|
||||
$output .= self::MAGENTA . "Memory: " . self::RESET . $entry->getMemory();
|
||||
$output .= " | " . self::MAGENTA . "Peak: " . self::RESET . $entry->getPeakMemory() . "\n";
|
||||
|
||||
if ($entry->getType() === EntryType::ARRAY && is_array($entry->getData())) {
|
||||
$data = $entry->getData();
|
||||
$output .= self::BLUE . "Elements: " . self::RESET . count($data);
|
||||
if (!empty($data)) {
|
||||
$output .= " | " . self::BLUE . "Keys: " . self::RESET;
|
||||
$output .= implode(', ', array_map('strval', array_keys($data)));
|
||||
}
|
||||
$output .= "\n";
|
||||
}
|
||||
|
||||
$output .= str_repeat('-', 80) . "\n";
|
||||
$output .= $this->formatValue($entry->getData()) . "\n";
|
||||
$output .= str_repeat('=', 80) . "\n";
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formatHistory(array $entries): string
|
||||
{
|
||||
$output = self::BOLD . "=== Debug Historie ===" . self::RESET . "\n";
|
||||
$output .= "Anzahl Einträge: " . count($entries) . "\n";
|
||||
$output .= str_repeat('-', 80) . "\n";
|
||||
|
||||
foreach ($entries as $index => $entry) {
|
||||
$output .= self::BOLD . self::BLUE . "#" . ($index + 1) . self::RESET . " ";
|
||||
$output .= self::GREEN . $entry->getLabel() . self::RESET . " - ";
|
||||
$output .= self::YELLOW . $entry->getFilename() . ":" . $entry->getLine() . self::RESET . " - ";
|
||||
$output .= self::CYAN . $entry->getFormattedTimestamp() . self::RESET . " - ";
|
||||
$output .= self::MAGENTA . $entry->getMemory() . self::RESET . "\n";
|
||||
}
|
||||
|
||||
$output .= str_repeat('-', 80) . "\n";
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formatValue(mixed $value): string
|
||||
{
|
||||
$formatted = print_r($value, true);
|
||||
|
||||
// Hervorhebung von Schlüsseln in Arrays
|
||||
$formatted = preg_replace(
|
||||
'/\[(.*?)\]/',
|
||||
'[' . self::CYAN . '$1' . self::RESET . ']',
|
||||
$formatted
|
||||
);
|
||||
|
||||
// Hervorhebung von NULL, true, false
|
||||
$formatted = preg_replace(
|
||||
'/(NULL|true|false)/',
|
||||
self::YELLOW . '$1' . self::RESET,
|
||||
$formatted
|
||||
);
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
}
|
||||
30
src/Framework/Debug/Formatters/FormatterInterface.php
Normal file
30
src/Framework/Debug/Formatters/FormatterInterface.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug\Formatters;
|
||||
|
||||
use App\Framework\Debug\DebugEntry;
|
||||
|
||||
/**
|
||||
* Interface für alle Debug-Formatierungen.
|
||||
*/
|
||||
interface FormatterInterface
|
||||
{
|
||||
/**
|
||||
* Formatiert einen Debug-Eintrag zur Ausgabe
|
||||
*/
|
||||
public function format(DebugEntry $entry): string;
|
||||
|
||||
/**
|
||||
* Formatiert eine Liste von Debug-Einträgen
|
||||
*
|
||||
* @param DebugEntry[] $entries
|
||||
*/
|
||||
public function formatHistory(array $entries): string;
|
||||
|
||||
/**
|
||||
* Formatiert einen beliebigen Wert zur Ausgabe
|
||||
*/
|
||||
public function formatValue(mixed $value): string;
|
||||
}
|
||||
204
src/Framework/Debug/Formatters/HtmlFormatter.php
Normal file
204
src/Framework/Debug/Formatters/HtmlFormatter.php
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug\Formatters;
|
||||
|
||||
use App\Framework\Debug\DebugEntry;
|
||||
use App\Framework\Debug\EntryType;
|
||||
|
||||
/**
|
||||
* Formatiert Debug-Einträge für HTML-Ausgabe.
|
||||
*/
|
||||
final class HtmlFormatter implements FormatterInterface
|
||||
{
|
||||
/** @var bool */
|
||||
private bool $stylesInjected = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function format(DebugEntry $entry): string
|
||||
{
|
||||
$styles = $this->stylesInjected ? '' : $this->getStyles();
|
||||
$this->stylesInjected = true;
|
||||
|
||||
$html = $styles;
|
||||
$html .= '<div class="debug-output">';
|
||||
$html .= '<div class="debug-header">';
|
||||
$html .= '<span class="debug-label">' . htmlspecialchars($entry->getLabel() ?: 'Debug') . '</span>';
|
||||
$html .= '<span class="debug-location">' . $entry->getFilename() . ':' . $entry->getLine() . '</span>';
|
||||
$html .= '<span class="debug-time">' . $entry->getFormattedTimestamp() . '</span>';
|
||||
$html .= '</div>';
|
||||
|
||||
$html .= '<div class="debug-content">';
|
||||
$html .= '<pre>' . htmlspecialchars($this->formatValue($entry->getData())) . '</pre>';
|
||||
$html .= '</div>';
|
||||
|
||||
if ($entry->getType() === EntryType::ARRAY && is_array($entry->getData())) {
|
||||
$data = $entry->getData();
|
||||
$html .= '<div class="debug-array-info">';
|
||||
$html .= '<small>Anzahl Elemente: ' . count($data) . '</small>';
|
||||
if (!empty($data)) {
|
||||
$html .= '<small> | Schlüssel: ' . htmlspecialchars(implode(', ', array_map('strval', array_keys($data)))) . '</small>';
|
||||
}
|
||||
$html .= '</div>';
|
||||
}
|
||||
|
||||
$html .= '<div class="debug-footer">';
|
||||
$html .= '<small>Memory: ' . $entry->getMemory() . ' | Peak: ' . $entry->getPeakMemory() . '</small>';
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formatHistory(array $entries): string
|
||||
{
|
||||
$styles = $this->stylesInjected ? '' : $this->getStyles();
|
||||
$this->stylesInjected = true;
|
||||
|
||||
$html = $styles;
|
||||
$html .= '<div class="debug-history">';
|
||||
$html .= '<h3>Debug Historie (' . count($entries) . ' Einträge)</h3>';
|
||||
$html .= '<div class="debug-history-entries">';
|
||||
|
||||
foreach ($entries as $index => $entry) {
|
||||
$html .= '<div class="debug-history-entry">';
|
||||
$html .= '<div class="debug-history-header">';
|
||||
$html .= '<span class="debug-history-index">#' . ($index + 1) . '</span> ';
|
||||
$html .= '<span class="debug-history-label">' . htmlspecialchars($entry->getLabel()) . '</span> | ';
|
||||
$html .= '<span class="debug-history-file">' . $entry->getFilename() . ':' . $entry->getLine() . '</span> | ';
|
||||
$html .= '<span class="debug-history-time">' . $entry->getFormattedTimestamp() . '</span> | ';
|
||||
$html .= '<span class="debug-history-memory">' . $entry->getMemory() . '</span>';
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
}
|
||||
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formatValue(mixed $value): string
|
||||
{
|
||||
return print_r($value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die CSS-Styles zurück
|
||||
*/
|
||||
private function getStyles(): string
|
||||
{
|
||||
return '<style>
|
||||
.debug-output {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 12px;
|
||||
color: #212529;
|
||||
}
|
||||
.debug-header {
|
||||
background: #343a40;
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.debug-label {
|
||||
font-weight: bold;
|
||||
color: #28a745;
|
||||
}
|
||||
.debug-location {
|
||||
color: #ffc107;
|
||||
}
|
||||
.debug-time {
|
||||
color: #17a2b8;
|
||||
}
|
||||
.debug-content {
|
||||
padding: 12px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
}
|
||||
.debug-content pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.debug-array-info {
|
||||
background: #f1f3f5;
|
||||
padding: 4px 12px;
|
||||
border-top: 1px dashed #dee2e6;
|
||||
color: #6c757d;
|
||||
}
|
||||
.debug-footer {
|
||||
background: #e9ecef;
|
||||
padding: 4px 12px;
|
||||
border-top: 1px solid #dee2e6;
|
||||
color: #6c757d;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
.debug-history {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
font-family: "Courier New", monospace;
|
||||
}
|
||||
.debug-history h3 {
|
||||
margin: 0 0 10px 0;
|
||||
color: #343a40;
|
||||
font-size: 14px;
|
||||
}
|
||||
.debug-history-entries {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.debug-history-entry {
|
||||
margin-bottom: 4px;
|
||||
padding: 4px 8px;
|
||||
background: #fff;
|
||||
border-left: 3px solid #6c757d;
|
||||
font-size: 11px;
|
||||
}
|
||||
.debug-history-header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
}
|
||||
.debug-history-index {
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
}
|
||||
.debug-history-label {
|
||||
color: #28a745;
|
||||
}
|
||||
.debug-history-file {
|
||||
color: #fd7e14;
|
||||
}
|
||||
.debug-history-time {
|
||||
color: #6c757d;
|
||||
}
|
||||
.debug-history-memory {
|
||||
color: #6610f2;
|
||||
}
|
||||
</style>';
|
||||
}
|
||||
}
|
||||
30
src/Framework/Debug/OutputInterface.php
Normal file
30
src/Framework/Debug/OutputInterface.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug;
|
||||
|
||||
use App\Framework\Debug\Formatters\FormatterInterface;
|
||||
|
||||
/**
|
||||
* Interface für alle Debug-Ausgabemethoden.
|
||||
*/
|
||||
interface OutputInterface
|
||||
{
|
||||
/**
|
||||
* Gibt einen Debug-Eintrag aus
|
||||
*/
|
||||
public function output(DebugEntry $entry, FormatterInterface $formatter): void;
|
||||
|
||||
/**
|
||||
* Gibt eine Historie von Debug-Einträgen aus
|
||||
*
|
||||
* @param DebugEntry[] $entries
|
||||
*/
|
||||
public function outputHistory(array $entries, FormatterInterface $formatter): void;
|
||||
|
||||
/**
|
||||
* Gibt einen Rohtext aus
|
||||
*/
|
||||
public function outputRaw(string $text): void;
|
||||
}
|
||||
39
src/Framework/Debug/Outputs/ConsoleOutput.php
Normal file
39
src/Framework/Debug/Outputs/ConsoleOutput.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug\Outputs;
|
||||
|
||||
use App\Framework\Debug\DebugEntry;
|
||||
use App\Framework\Debug\Formatters\FormatterInterface;
|
||||
use App\Framework\Debug\OutputInterface;
|
||||
|
||||
/**
|
||||
* Ausgabeklasse für Konsolen-Umgebungen.
|
||||
*/
|
||||
final class ConsoleOutput implements OutputInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function output(DebugEntry $entry, FormatterInterface $formatter): void
|
||||
{
|
||||
fwrite(STDOUT, $formatter->format($entry));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function outputHistory(array $entries, FormatterInterface $formatter): void
|
||||
{
|
||||
fwrite(STDOUT, $formatter->formatHistory($entries));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function outputRaw(string $text): void
|
||||
{
|
||||
fwrite(STDOUT, $text);
|
||||
}
|
||||
}
|
||||
39
src/Framework/Debug/Outputs/HtmlOutput.php
Normal file
39
src/Framework/Debug/Outputs/HtmlOutput.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Debug\Outputs;
|
||||
|
||||
use App\Framework\Debug\DebugEntry;
|
||||
use App\Framework\Debug\Formatters\FormatterInterface;
|
||||
use App\Framework\Debug\OutputInterface;
|
||||
|
||||
/**
|
||||
* Ausgabeklasse für HTML-Umgebungen.
|
||||
*/
|
||||
final class HtmlOutput implements OutputInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function output(DebugEntry $entry, FormatterInterface $formatter): void
|
||||
{
|
||||
echo $formatter->format($entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function outputHistory(array $entries, FormatterInterface $formatter): void
|
||||
{
|
||||
echo $formatter->formatHistory($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function outputRaw(string $text): void
|
||||
{
|
||||
echo "<div class='debug-raw'>", htmlspecialchars($text), "</div>";
|
||||
}
|
||||
}
|
||||
23
src/Framework/Debug/composer.json
Normal file
23
src/Framework/Debug/composer.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "app/framework-debug",
|
||||
"description": "Debug-Modul für das Framework",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Framework Team",
|
||||
"email": "team@framework.example"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\Framework\\Debug\\": ""
|
||||
},
|
||||
"files": [
|
||||
"helpers.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
65
src/Framework/Debug/helpers.php
Normal file
65
src/Framework/Debug/helpers.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\Debug\Debugger;
|
||||
|
||||
if (!function_exists('dd')) {
|
||||
/**
|
||||
* Debug und beende (dump and die)
|
||||
*/
|
||||
function dd(mixed $data, string $label = ''): void
|
||||
{
|
||||
Debugger::dump($data, $label, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('debug')) {
|
||||
/**
|
||||
* Debug ohne beenden
|
||||
*/
|
||||
function debug(mixed $data, string $label = ''): void
|
||||
{
|
||||
Debugger::dump($data, $label);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('debug_array')) {
|
||||
/**
|
||||
* Debug speziell für Arrays
|
||||
*/
|
||||
function debug_array(array $data, string $label = ''): void
|
||||
{
|
||||
Debugger::dumpArray($data, $label);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('debug_object')) {
|
||||
/**
|
||||
* Debug speziell für Objekte
|
||||
*/
|
||||
function debug_object(object $object, string $label = ''): void
|
||||
{
|
||||
Debugger::dumpObject($object, $label);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('debug_log')) {
|
||||
/**
|
||||
* Einfaches Debug-Logging
|
||||
*/
|
||||
function debug_log(string $message, string $level = 'DEBUG'): void
|
||||
{
|
||||
Debugger::log($message, $level);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('debug_history')) {
|
||||
/**
|
||||
* Debug-Historie anzeigen
|
||||
*/
|
||||
function debug_history(): void
|
||||
{
|
||||
Debugger::showHistory();
|
||||
}
|
||||
}
|
||||
104
src/Framework/Debug/readme.md
Normal file
104
src/Framework/Debug/readme.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Framework Debug-Modul
|
||||
|
||||
Dieses Modul bietet eine übersichtlichere Alternative zu `var_dump` und vereinfacht das Debugging in der Anwendung.
|
||||
|
||||
## Features
|
||||
|
||||
- **Übersichtliche Formatierung** - Besser lesbar als `var_dump`
|
||||
- **Kontext-Informationen** - Zeigt Datei, Zeile, Zeit und Speicherverbrauch
|
||||
- **Verschiedene Ausgabeformate** - HTML für Browser, Konsole für CLI
|
||||
- **Spezialisierte Funktionen** - Für Arrays und Objekte optimiert
|
||||
- **Debug-Historie** - Verfolgt alle Debug-Aufrufe
|
||||
- **Einfache Konfiguration** - Kann für verschiedene Umgebungen aktiviert/deaktiviert werden
|
||||
- **Erweiterbar** - Eigene Formatter und Outputs möglich
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Einfaches Debugging
|
||||
|
||||
```php
|
||||
// Variable debuggen
|
||||
debug($variable, 'Meine Variable');
|
||||
|
||||
// Debuggen und Ausführung beenden
|
||||
dd($variable, 'Fehleranalyse');
|
||||
|
||||
// Array-spezifisches Debugging
|
||||
debug_array(['a' => 1, 'b' => 2], 'Mein Array');
|
||||
|
||||
// Objekt-Debugging mit Methoden- und Property-Liste
|
||||
$user = new User();
|
||||
debug_object($user, 'Benutzer');
|
||||
|
||||
// Einfache Log-Nachrichten
|
||||
debug_log('Benutzer wurde erstellt', 'INFO');
|
||||
|
||||
// Historie aller Debug-Aufrufe anzeigen
|
||||
debug_history();
|
||||
```
|
||||
|
||||
### Konfiguration
|
||||
|
||||
```php
|
||||
// In Bootstrap-Datei oder Service-Provider
|
||||
use App\Framework\Debug\Debugger;
|
||||
use App\Framework\Debug\Outputs\HtmlOutput;
|
||||
use App\Framework\Debug\Formatters\HtmlFormatter;
|
||||
|
||||
// Für Entwicklungsumgebung aktivieren
|
||||
Debugger::configure(
|
||||
enabled: true,
|
||||
output: new HtmlOutput(),
|
||||
formatter: new HtmlFormatter()
|
||||
);
|
||||
|
||||
// In Produktionsumgebung deaktivieren
|
||||
if ($app->isProduction()) {
|
||||
Debugger::disable();
|
||||
}
|
||||
```
|
||||
|
||||
## Eigene Formatierung und Ausgabe
|
||||
|
||||
Das Modul kann durch eigene Implementierungen von `FormatterInterface` und `OutputInterface` erweitert werden.
|
||||
|
||||
```php
|
||||
namespace App\Debug;
|
||||
|
||||
use App\Framework\Debug\Formatters\FormatterInterface;
|
||||
use App\Framework\Debug\DebugEntry;
|
||||
|
||||
class MyCustomFormatter implements FormatterInterface
|
||||
{
|
||||
public function format(DebugEntry $entry): string
|
||||
{
|
||||
// Eigene Formatierung implementieren
|
||||
}
|
||||
|
||||
public function formatHistory(array $entries): string
|
||||
{
|
||||
// Eigene Formatierung für Historie
|
||||
}
|
||||
|
||||
public function formatValue(mixed $value): string
|
||||
{
|
||||
// Eigene Formatierung für Werte
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
Um die Helper-Funktionen global verfügbar zu machen, fügen Sie die folgende Zeile zu Ihrer Composer-Datei hinzu:
|
||||
|
||||
```json
|
||||
{
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/Framework/Debug/helpers.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Dann führen Sie `composer dump-autoload` aus.
|
||||
Reference in New Issue
Block a user