0, 'misses' => 0, 'renders' => 0, 'cached_templates' => [], ]; public function __construct( private readonly TemplateLoader $loader, private readonly TemplateAnalyzer $analyzer, private readonly FragmentCacheManager $fragmentCache, private readonly Cache $cache, private readonly TemplateProcessor $processor = new TemplateProcessor(), private readonly bool $cacheEnabled = true, private readonly bool $debugMode = true, private readonly TemplatePreprocessor $preprocessor = new TemplatePreprocessor(), ) { dd('oldcacheengine'); } public function render(RenderContext $context): string { $this->cacheStats['renders']++; if (!$this->cacheEnabled) { $this->logDebug("Cache disabled - rendering without cache", $context); return $this->renderWithoutCache($context); } $startTime = microtime(true); // Lade und analysiere Template $template = $this->loader->load($context->template, $context->controllerClass, $context); // Preprocessing für optimierte Cache-Keys $normalizedTemplate = $this->preprocessor->normalizeTemplate($template, $context); $cacheableBlocks = $this->preprocessor->extractCacheableBlocks($template); $analysis = $this->analyzer->analyzeCacheability($normalizedTemplate, $context); // Erweitere Analysis mit extrahierten Blöcken foreach ($cacheableBlocks as $block) { if ($block['type'] === 'explicit') { $analysis->addStaticFragment($block['id']); } } $this->logDebug("Template loaded, length: " . strlen($template) . " chars", $context); $this->logDebug("Cache strategy: {$analysis->cacheStrategy->value}, TTL: {$analysis->ttl}s", $context); $this->logDebug("Cacheable blocks found: " . count($cacheableBlocks), $context); $result = match($analysis->cacheStrategy) { CacheStrategy::STATIC => $this->renderStaticCached($context, $template, $analysis), CacheStrategy::PARTIAL => $this->renderPartialCached($context, $template, $analysis), CacheStrategy::FRAGMENT => $this->renderFragmentCached($context, $template, $analysis), CacheStrategy::DYNAMIC => $this->processor->render($context, $template), CacheStrategy::DISABLED => $this->renderWithoutCache($context), }; $renderTime = microtime(true) - $startTime; $this->logCachePerformance($context, $analysis, $renderTime); return $result; } private function renderStaticCached(RenderContext $context, string $template, CacheabilityInfo $analysis): string { $cacheKey = $this->buildStaticCacheKey($context, $analysis); // Prüfe zuerst, ob bereits im Cache $wasInCache = $this->cache->has($cacheKey); // Nutze remember-Pattern $cacheItem = $this->cache->remember( $cacheKey, function() use ($context, $template) { $this->logDebug("CACHE MISS - Generating new content", $context); $this->cacheStats['misses']++; return $this->processor->render($context, $template); }, $analysis->ttl ); if ($wasInCache) { $this->logDebug("CACHE HIT - Using cached content", $context); $this->cacheStats['hits']++; } $this->cacheStats['cached_templates'][$context->template] = [ 'strategy' => 'static', 'key' => $cacheKey, 'ttl' => $analysis->ttl, 'hit' => $wasInCache ]; return $cacheItem->value; } private function renderPartialCached(RenderContext $context, string $template, CacheabilityInfo $analysis): string { $cacheKey = $this->buildPartialCacheKey($context, $analysis); $wasInCache = $this->cache->has($cacheKey); $cacheItem = $this->cache->remember( $cacheKey, function() use ($context, $template) { $this->cacheStats['misses']++; return $this->processor->render($context, $template); }, $analysis->ttl ); if ($wasInCache) { $this->cacheStats['hits']++; } return $cacheItem->value; } private function renderFragmentCached(RenderContext $context, string $template, CacheabilityInfo $analysis): string { // Wenn keine Fragmente erkannt wurden, verwende ein Template-weites Fragment if (empty($analysis->staticFragments) && empty($analysis->dynamicFragments)) { $this->logDebug("No fragments found - creating template-wide fragment", $context); $templateFragmentKey = "template_{$context->template}"; $wasInCache = $this->fragmentCache->hasFragment($templateFragmentKey, $analysis->dependencies ?? [], $analysis->ttl); if ($wasInCache) { $this->logDebug("FRAGMENT HIT - Using cached template", $context); $this->cacheStats['hits']++; $content = $this->fragmentCache->getFragment($templateFragmentKey, $analysis->dependencies ?? []); } else { $this->logDebug("FRAGMENT MISS - Generating new template content", $context); $this->cacheStats['misses']++; $content = $this->fragmentCache->cacheFragment( $templateFragmentKey, function() use ($context, $template) { return $this->processor->render($context, $template); }, $analysis->dependencies ?? [], $analysis->ttl ); } $this->cacheStats['cached_templates'][$context->template] = [ 'strategy' => 'fragment', 'key' => $templateFragmentKey, 'ttl' => $analysis->ttl, 'hit' => $wasInCache ]; // Request-spezifische Statistiken $hitRate = $this->cacheStats['hits'] + $this->cacheStats['misses'] > 0 ? round(($this->cacheStats['hits'] / ($this->cacheStats['hits'] + $this->cacheStats['misses'])) * 100, 1) : 0; error_log("SmartCache Request Stats: Hits: " . $this->cacheStats['hits'] . ", Misses: " . $this->cacheStats['misses'] . ", Hit Rate: {$hitRate}%"); return $content; } else { $this->logDebug("Found " . count($analysis->staticFragments) . " static fragments and " . count($analysis->dynamicFragments) . " dynamic fragments", $context); } // Verarbeite explizite Fragmente $finalContent = ''; foreach ($analysis->staticFragments as $fragmentId) { $wasInCache = $this->fragmentCache->hasFragment($fragmentId, $analysis->dependencies ?? [], $analysis->ttl); if ($wasInCache) { $this->logDebug("FRAGMENT HIT - Using cached fragment: {$fragmentId}", $context); $this->cacheStats['hits']++; // Direkt aus Cache holen ohne Generator zu verwenden $fragmentContent = $this->fragmentCache->getFragment($fragmentId, $analysis->dependencies ?? []); } else { $this->logDebug("FRAGMENT MISS - Generating fragment: {$fragmentId}", $context); $this->cacheStats['misses']++; $fragmentContent = $this->fragmentCache->cacheFragment( $fragmentId, function() use ($fragmentId, $template, $context) { return $this->renderFragmentFromTemplate($fragmentId, $template, $context); }, $analysis->dependencies ?? [], $analysis->ttl ); } $finalContent .= $fragmentContent; } // Rendere dynamische Fragmente ohne Caching foreach ($analysis->dynamicFragments as $fragmentId) { $fragmentContent = $this->renderFragmentFromTemplate($fragmentId, $template, $context); $finalContent .= $fragmentContent; } return $finalContent ?: $this->processor->render($context, $template); } private function renderFragmentFromTemplate(string $fragmentId, string $template, RenderContext $context): string { // Für jetzt: Wenn es ein Template-Fragment ist, rendere das komplette Template if (str_starts_with($fragmentId, 'template_')) { return $this->processor->render($context, $template); } // TODO: Hier könnten wir später spezifische Fragment-Extraktion implementieren // Für jetzt verwende DOM-Wrapper für Fragment-Extraktion try { $domWrapper = DomWrapper::fromString($template); return $this->renderFragment($fragmentId, $domWrapper, $context); } catch (\Exception $e) { $this->logDebug("Fragment rendering failed for {$fragmentId}: " . $e->getMessage(), $context); // Fallback: Rendere komplettes Template return $this->processor->render($context, $template); } } private function renderWithoutCache(RenderContext $context): string { $template = $this->loader->load($context->template, $context->controllerClass, $context); return $this->processor->render($context, $template); } private function renderFragment(string $fragmentId, DomWrapper $domWrapper, RenderContext $context): string { // Vereinfachte Fragment-Rendering-Logik // In einer vollständigen Implementierung würde hier das spezifische Fragment extrahiert $elements = $domWrapper->getElementsByAttribute('data-fragment-id', $fragmentId); if (count($elements) > 0) { $element = $elements[0]; return $domWrapper->document->saveHTML($element); } return ''; } private function extractStaticContent(string $content, CacheabilityInfo $analysis): string { // Entferne dynamische Platzhalter für statisches Caching $staticContent = $content; // Ersetze dynamische Teile mit Platzhaltern $staticContent = preg_replace('/\{\{[^}]+}}/', '{{DYNAMIC_PLACEHOLDER}}', $staticContent); return $staticContent; } private function buildStaticCacheKey(RenderContext $context, $analysis): string { $dependencies = is_object($analysis) && method_exists($analysis, 'dependencies') ? $analysis->dependencies : ($analysis->dependencies ?? []); $optimizedKey = $this->preprocessor->generateOptimizedCacheKey($context, $dependencies); return "static:{$optimizedKey}"; } private function buildPartialCacheKey(RenderContext $context, $analysis): string { $dependencies = is_object($analysis) && method_exists($analysis, 'dependencies') ? $analysis->dependencies : ($analysis->dependencies ?? []); $optimizedKey = $this->preprocessor->generateOptimizedCacheKey($context, $dependencies); return "partial:{$optimizedKey}"; } public function getCacheStats(): array { $hitRate = $this->cacheStats['hits'] + $this->cacheStats['misses'] > 0 ? round(($this->cacheStats['hits'] / ($this->cacheStats['hits'] + $this->cacheStats['misses'])) * 100, 2) : 0; return [ 'smart_cache' => [ 'enabled' => $this->cacheEnabled, 'debug_mode' => $this->debugMode, 'cache_interface' => get_class($this->cache), 'performance' => [ 'total_renders' => $this->cacheStats['renders'], 'cache_hits' => $this->cacheStats['hits'], 'cache_misses' => $this->cacheStats['misses'], 'hit_rate' => $hitRate . '%', ], 'cached_templates' => $this->cacheStats['cached_templates'], 'fragment_stats' => $this->fragmentCache->getFragmentStats(), ] ]; } public function invalidateCache(?string $template = null): int { $invalidated = 0; if ($template) { // Invalidiere spezifisches Template $patterns = [ "static:{$template}:", "partial:{$template}:", "template_{$template}", ]; foreach ($patterns as $pattern) { // Vereinfachte Implementierung - da das Cache-Interface keine Pattern-Suche unterstützt // könnte hier eine erweiterte Lösung implementiert werden $invalidated++; } } else { // Alle Caches löschen und Stats zurücksetzen if ($this->cache->clear()) { $this->resetStats(); $invalidated = 1; } } return $invalidated; } public function resetStats(): void { $this->cacheStats = [ 'hits' => 0, 'misses' => 0, 'renders' => 0, 'cached_templates' => [], ]; } public function warmupCache(array $templates = []): array { if (!$this->warmer) { return ['error' => 'CacheWarmer not configured']; } return $this->warmer->warmupTemplates($templates); } public function addPopularTemplate(string $template, array $data = []): void { if ($this->warmer) { $this->warmer->addPopularTemplate($template, $data); } } public function diagnoseCaching(): void { echo "\n=== CACHE DIAGNOSTICS ===\n"; echo "Smart Cache Enabled: " . ($this->cacheEnabled ? 'YES' : 'NO') . "\n"; echo "Cache Interface: " . get_class($this->cache) . "\n"; echo "Debug Mode: " . ($this->debugMode ? 'YES' : 'NO') . "\n"; echo "Total Renders: " . $this->cacheStats['renders'] . "\n"; echo "Cache Hits: " . $this->cacheStats['hits'] . "\n"; echo "Cache Misses: " . $this->cacheStats['misses'] . "\n"; $hitRate = $this->cacheStats['hits'] + $this->cacheStats['misses'] > 0 ? round(($this->cacheStats['hits'] / ($this->cacheStats['hits'] + $this->cacheStats['misses'])) * 100, 2) : 0; echo "Hit Rate: " . $hitRate . "%\n"; echo "\n=== ANALYSIS ===\n"; if ($this->cacheStats['hits'] === 0 && $this->cacheStats['misses'] === 0) { echo "⚠️ PROBLEM: No cache operations detected!\n"; echo " This suggests that templates are being rendered with CacheStrategy::DYNAMIC or CacheStrategy::DISABLED\n"; echo " Check the TemplateAnalyzer logic to ensure templates are being marked as cacheable.\n"; } echo "========================\n"; } private function logCachePerformance(RenderContext $context, CacheabilityInfo $analysis, float $renderTime): void { if (!$this->debugMode) { return; } $logData = [ 'template' => $context->template, 'strategy' => $analysis->cacheStrategy->value, 'render_time' => round($renderTime * 1000, 2) . 'ms', 'complexity' => $analysis->getCacheComplexity(), 'cacheable' => $analysis->cacheStrategy->shouldCache(), 'request_hits' => $this->cacheStats['hits'], 'request_misses' => $this->cacheStats['misses'], 'static_fragments' => count($analysis->staticFragments), 'dynamic_fragments' => count($analysis->dynamicFragments), 'ttl' => $analysis->ttl, ]; error_log('SmartCache Performance: ' . json_encode($logData)); // Zusätzliche Fragment-Details wenn Fragment-Strategy if ($analysis->cacheStrategy === CacheStrategy::FRAGMENT) { error_log('SmartCache Fragments: static=[' . implode(',', $analysis->staticFragments) . '] dynamic=[' . implode(',', $analysis->dynamicFragments) . ']'); } } private function logDebug(string $message, RenderContext $context): void { if (!$this->debugMode) { return; } error_log("SmartCache DEBUG [{$context->template}]: {$message}"); } }