# Discovery Cache Refactoring - Analyse und Vorschläge ## Problem-Analyse ### Identifiziertes Problem Die Debug-Logs zeigen: ``` [DEBUG ComponentRegistry] LiveComponent: class=App\Application\LiveComponents\Popover\PopoverComponent, name=popover, arguments={"name":"popover"} ``` **Befund:** 1. ✅ Die Datei enthält korrekt `popover-live` (bestätigt durch direkte Reflection) 2. ❌ Der Discovery-Cache enthält noch `{"name":"popover"}` 3. ❌ Die Discovery wird nicht neu durchgeführt - es erscheint "Cached registry loaded", aber kein "Performing fresh discovery" 4. ❌ Der Cache wird sofort nach dem Löschen neu erstellt, bevor die Discovery-Registry die Attribute aus den Dateien liest ### Root Cause Der Discovery-Cache wird geladen, bevor die Discovery-Registry die Attribute aus den Dateien liest. Die Arguments werden aus dem Cache geladen, nicht direkt aus den Dateien. **Cache-Load-Reihenfolge:** 1. `DiscoveryCacheManager::get()` wird aufgerufen 2. Cache wird geladen (enthält noch alte Arguments) 3. `ComponentRegistry::buildNameMap()` verwendet die gecachten Arguments 4. Kollision tritt auf, weil beide Komponenten als `popover` registriert werden ## Refactoring-Vorschläge ### Vorschlag 1: Staleness-Prüfung verbessern **Problem:** Die Staleness-Prüfung prüft nicht auf die spezifischen Dateien, die geändert wurden. **Lösung:** Erweitere `StalenessChecker` um eine Methode, die prüft, ob bestimmte Dateien geändert wurden: ```php // In StalenessChecker.php public function areFilesModifiedSince(array $filePaths, Timestamp $since): bool { foreach ($filePaths as $filePath) { if ($this->fileSystemService->exists($filePath)) { $modifiedTime = $this->fileSystemService->getModifiedTime($filePath); if ($modifiedTime->toInt() > $since->toInt()) { return true; } } } return false; } ``` **Vorteil:** Präzise Prüfung auf geänderte Dateien statt allgemeine Staleness-Prüfung. ### Vorschlag 2: Cache-Invalidierung bei Datei-Änderungen **Problem:** Der Cache wird nicht automatisch invalidated, wenn Dateien geändert werden. **Lösung:** Füge einen File-Watcher hinzu, der Datei-Änderungen erkennt und den Cache automatisch invalidated: ```php // In DiscoveryCacheManager.php public function invalidateIfFilesChanged(array $filePaths): bool { $key = $this->buildCacheKey($context); $entry = $this->getStandardCache($context); if (!$entry->found || !$entry->entry) { return false; } $cacheCreatedAt = $entry->entry->createdAt; foreach ($filePaths as $filePath) { if ($this->fileSystemService->exists($filePath)) { $modifiedTime = $this->fileSystemService->getModifiedTime($filePath); if ($modifiedTime->toInt() > $cacheCreatedAt->toInt()) { $this->invalidate($context); return true; } } } return false; } ``` **Vorteil:** Automatische Cache-Invalidierung bei Datei-Änderungen. ### Vorschlag 3: Arguments direkt aus Dateien lesen (Bypass Cache) **Problem:** Die Arguments werden aus dem Cache geladen, nicht direkt aus den Dateien. **Lösung:** Füge eine Option hinzu, die Arguments direkt aus den Dateien liest, wenn der Cache als stale erkannt wird: ```php // In ComponentRegistry.php, buildNameMap() private function buildNameMap(): ComponentNameMap { $mappings = []; $liveComponentClassNames = []; // Get all LiveComponent attributes from DiscoveryRegistry $liveComponents = $this->discoveryRegistry->attributes()->get(LiveComponent::class); foreach ($liveComponents as $discoveredAttribute) { /** @var LiveComponent|null $attribute */ $attribute = $discoveredAttribute->createAttributeInstance(); if ($attribute && ! empty($attribute->name)) { $className = $discoveredAttribute->className->toString(); // DEBUG: Log für Popover-Komponenten if (str_contains($className, 'Popover')) { // Prüfe ob Datei geändert wurde seit Cache-Erstellung $filePath = $discoveredAttribute->filePath?->toString(); if ($filePath && $this->isFileModifiedSinceCache($filePath)) { // Lese Arguments direkt aus Datei $attribute = $this->readAttributeFromFile($className, LiveComponent::class); } error_log(sprintf( "[DEBUG ComponentRegistry] LiveComponent: class=%s, name=%s, arguments=%s, source=%s", $className, $attribute->name, json_encode($discoveredAttribute->arguments), $filePath ?? 'unknown' )); } $mappings[] = new ComponentMapping($attribute->name, $className); $liveComponentClassNames[] = $className; } } // ... rest of method } ``` **Vorteil:** Umgeht den Cache, wenn Dateien geändert wurden. ### Vorschlag 4: Cache-Versionierung **Problem:** Der Cache hat keine Versionierung, die bei Änderungen der Cache-Struktur hilft. **Lösung:** Füge eine Cache-Version hinzu, die bei Änderungen der Cache-Struktur erhöht wird: ```php // In CacheEntry.php private const int CACHE_VERSION = 2; // Erhöhe bei Cache-Struktur-Änderungen public function __construct( public readonly DiscoveryRegistry|array $registry, public readonly Timestamp $createdAt, public readonly string $version = '', public readonly CacheLevel $cacheLevel = CacheLevel::NORMAL, public readonly CacheTier $cacheTier = CacheTier::HOT, public readonly int $cacheVersion = self::CACHE_VERSION ) { } // In DiscoveryCacheManager.php private function isCacheVersionValid(CacheEntry $entry): bool { return $entry->cacheVersion === CacheEntry::CACHE_VERSION; } ``` **Vorteil:** Automatische Cache-Invalidierung bei Cache-Struktur-Änderungen. ### Vorschlag 5: Discovery-Registry Refresh-Mechanismus **Problem:** Die Discovery-Registry wird nicht automatisch aktualisiert, wenn Dateien geändert werden. **Lösung:** Füge einen Refresh-Mechanismus hinzu, der die Discovery-Registry automatisch aktualisiert: ```php // In DiscoveryServiceBootstrapper.php public function refreshIfNeeded(): void { $discoveryCacheManager = $this->container->get(DiscoveryCacheManager::class); $context = $this->createDiscoveryContext(); $cachedRegistry = $discoveryCacheManager->get($context); if ($cachedRegistry === null) { // Cache ist leer, führe Discovery durch $this->bootstrap(); return; } // Prüfe ob Registry stale ist if ($this->isRegistryStale($cachedRegistry, $context)) { // Registry ist stale, führe Discovery durch $this->bootstrap(); } } ``` **Vorteil:** Automatische Aktualisierung der Discovery-Registry bei Änderungen. ## Empfohlene Implementierungsreihenfolge 1. **Vorschlag 1** (Staleness-Prüfung verbessern) - Sofortige Verbesserung 2. **Vorschlag 2** (Cache-Invalidierung bei Datei-Änderungen) - Langfristige Lösung 3. **Vorschlag 3** (Arguments direkt aus Dateien lesen) - Workaround für aktuelles Problem 4. **Vorschlag 4** (Cache-Versionierung) - Präventive Maßnahme 5. **Vorschlag 5** (Discovery-Registry Refresh-Mechanismus) - Automatisierung ## Sofortige Lösung (Workaround) Für das aktuelle Problem sollte **Vorschlag 3** implementiert werden, da er das Problem direkt behebt, ohne die gesamte Cache-Architektur zu ändern. ## Langfristige Lösung **Vorschlag 2** sollte langfristig implementiert werden, da er das Problem an der Wurzel löst und verhindert, dass ähnliche Probleme in Zukunft auftreten.