cacheManager = $cacheManager; $this->initializeService(); } /** * Generate and inject optimized CSS for restrictions * * @param array $restrictions Active restrictions data * @param array $options CSS generation options * @return array CSS injection results * @since 1.0.0 */ public function injectRestrictionCss(array $restrictions, array $options = []): array { $startTime = microtime(true); // Generate cache key based on restrictions and options $cacheKey = $this->generateCacheKey($restrictions, $options); // Try to get cached CSS first $cachedCss = $this->cacheManager->get( "css_injection_{$cacheKey}", function() use ($restrictions, $options) { return $this->generateOptimizedCss($restrictions, $options); }, self::CSS_CACHE_TTL, ['use_file_cache' => true] ); // Inject CSS based on strategy $injectionResults = $this->injectCssStrategically($cachedCss, $options); $executionTime = (microtime(true) - $startTime) * 1000; return [ 'css_generated' => !empty($cachedCss['critical']) || !empty($cachedCss['deferred']), 'critical_css_size' => strlen($cachedCss['critical'] ?? ''), 'deferred_css_size' => strlen($cachedCss['deferred'] ?? ''), 'injection_method' => $injectionResults['method'], 'execution_time' => $executionTime, 'cache_hit' => $injectionResults['cache_hit'] ?? false, 'fouc_prevention' => $injectionResults['fouc_prevention'] ?? false ]; } /** * Generate critical CSS for above-the-fold content * * @param array $restrictions Restriction data * @param array $context Page context information * @return string Critical CSS * @since 1.0.0 */ public function generateCriticalCss(array $restrictions, array $context = []): string { $criticalRules = []; // Generate hiding rules for restricted elements (most critical) foreach ($restrictions as $restriction) { $selector = $this->generateCssSelector($restriction); if ($selector && $this->isCriticalSelector($selector, $context)) { $criticalRules[] = $this->generateHidingRule($selector, $restriction); } } // Add essential visibility rules $criticalRules[] = $this->generateVisibilityHelpers(); // Add loading state styles to prevent FOUC $criticalRules[] = $this->generateLoadingStateStyles(); $criticalCss = implode("\n", array_filter($criticalRules)); // Minify critical CSS aggressively return $this->minifyCss($criticalCss, ['aggressive' => true]); } /** * Generate non-critical CSS for progressive enhancement * * @param array $restrictions Restriction data * @param array $context Page context information * @return string Non-critical CSS * @since 1.0.0 */ public function generateDeferredCss(array $restrictions, array $context = []): string { $deferredRules = []; // Generate enhancement styles foreach ($restrictions as $restriction) { $selector = $this->generateCssSelector($restriction); if ($selector && !$this->isCriticalSelector($selector, $context)) { $deferredRules[] = $this->generateEnhancementRule($selector, $restriction); } } // Add transition and animation styles $deferredRules[] = $this->generateTransitionStyles(); // Add responsive design enhancements $deferredRules[] = $this->generateResponsiveEnhancements(); $deferredCss = implode("\n", array_filter($deferredRules)); return $this->minifyCss($deferredCss); } /** * Preload critical CSS resources * * @param array $restrictions Current restrictions * @return void * @since 1.0.0 */ public function preloadCriticalResources(array $restrictions): void { // Preload critical CSS based on likely user paths $criticalPaths = $this->identifyCriticalPaths($restrictions); foreach ($criticalPaths as $path) { $cssUrl = $this->generateCssUrl($path); $this->addResourceHint('preload', $cssUrl, ['as' => 'style']); } // Prefetch non-critical resources $this->prefetchNonCriticalResources($restrictions); } /** * Optimize CSS delivery with advanced techniques * * @param string $css CSS content * @param array $options Optimization options * @return array Optimization results * @since 1.0.0 */ public function optimizeCssDelivery(string $css, array $options = []): array { $originalSize = strlen($css); // Apply optimization pipeline $optimizedCss = $css; // Step 1: Remove unused CSS (if analyzer available) if ($options['remove_unused'] ?? false) { $optimizedCss = $this->removeUnusedCss($optimizedCss, $options); } // Step 2: Minification if ($this->minificationEnabled && $originalSize > self::MINIFICATION_THRESHOLD) { $optimizedCss = $this->minifyCss($optimizedCss); } // Step 3: Compression (gzip-ready) $compressedSize = 0; if ($this->compressionEnabled) { $compressed = gzcompress($optimizedCss, 6); $compressedSize = strlen($compressed); } return [ 'original_size' => $originalSize, 'optimized_size' => strlen($optimizedCss), 'compressed_size' => $compressedSize, 'reduction_percentage' => $originalSize > 0 ? (($originalSize - strlen($optimizedCss)) / $originalSize) * 100 : 0, 'optimization_applied' => true ]; } /** * Monitor CSS performance and adjust strategies * * @return array Performance metrics * @since 1.0.0 */ public function getPerformanceMetrics(): array { return [ 'critical_css_size' => array_sum(array_map('strlen', $this->criticalCss)), 'deferred_css_size' => array_sum(array_map('strlen', $this->deferredCss)), 'inline_rules_count' => count($this->inlinedRules), 'cache_hit_rate' => $this->calculateCssHitRate(), 'average_injection_time' => $this->calculateAverageInjectionTime(), 'fouc_prevention_rate' => $this->calculateFoucPreventionRate(), 'compression_enabled' => $this->compressionEnabled, 'minification_enabled' => $this->minificationEnabled ]; } /** * Generate optimized CSS package * * @param array $restrictions Restriction data * @param array $options Generation options * @return array CSS package * @since 1.0.0 */ private function generateOptimizedCss(array $restrictions, array $options): array { $context = $this->getPageContext($options); // Generate critical and deferred CSS $criticalCss = $this->generateCriticalCss($restrictions, $context); $deferredCss = $this->generateDeferredCss($restrictions, $context); // Optimize each part separately $criticalOptimized = $this->optimizeCssDelivery($criticalCss, ['aggressive' => true]); $deferredOptimized = $this->optimizeCssDelivery($deferredCss); return [ 'critical' => $criticalOptimized['original_size'] > 0 ? $criticalCss : '', 'deferred' => $deferredOptimized['original_size'] > 0 ? $deferredCss : '', 'metadata' => [ 'critical_optimization' => $criticalOptimized, 'deferred_optimization' => $deferredOptimized, 'generation_time' => microtime(true), 'restrictions_count' => count($restrictions) ] ]; } /** * Inject CSS strategically based on content and performance requirements * * @param array $cssPackage CSS package * @param array $options Injection options * @return array Injection results * @since 1.0.0 */ private function injectCssStrategically(array $cssPackage, array $options): array { $results = [ 'method' => 'none', 'fouc_prevention' => false, 'cache_hit' => false ]; $criticalCss = $cssPackage['critical'] ?? ''; $deferredCss = $cssPackage['deferred'] ?? ''; // Strategy 1: Inline critical CSS (fastest, prevents FOUC) if (!empty($criticalCss) && strlen($criticalCss) < self::CRITICAL_CSS_THRESHOLD) { $this->inlineCriticalCss($criticalCss); $results['method'] = 'inline_critical'; $results['fouc_prevention'] = true; } // Strategy 2: Load deferred CSS asynchronously if (!empty($deferredCss)) { $this->loadDeferredCssAsync($deferredCss); $results['method'] .= ($results['method'] !== 'none' ? '+' : '') . 'async_deferred'; } // Strategy 3: Resource hints for future requests $this->addResourceHintsForFutureRequests($options); return $results; } /** * Inline critical CSS in document head * * @param string $css Critical CSS * @return void * @since 1.0.0 */ private function inlineCriticalCss(string $css): void { if (empty($css)) { return; } // Add CSS with high priority (early in head) add_action('wp_head', function() use ($css) { echo "\n"; }, 1); // Store for metrics $this->criticalCss[] = $css; } /** * Load deferred CSS asynchronously * * @param string $css Deferred CSS * @return void * @since 1.0.0 */ private function loadDeferredCssAsync(string $css): void { if (empty($css)) { return; } // Create CSS file for caching $cssHash = md5($css); $cssUrl = $this->createCssFile($css, $cssHash); if ($cssUrl) { // Load asynchronously after page load add_action('wp_footer', function() use ($cssUrl) { echo "\n"; }, 20); } else { // Fallback: inline in footer add_action('wp_footer', function() use ($css) { echo "\n"; }, 20); } $this->deferredCss[] = $css; } /** * Generate CSS selector for restriction * * @param array $restriction Restriction data * @return string CSS selector * @since 1.0.0 */ private function generateCssSelector(array $restriction): string { $type = $restriction['type'] ?? ''; $targetId = $restriction['target_id'] ?? ''; if (empty($type) || empty($targetId)) { return ''; } // Generate specific selectors based on KiviCare structure switch ($type) { case 'doctor': return ".kivi-doctor-option[data-doctor-id=\"{$targetId}\"], " . ".doctor-appointment-slot[data-doctor=\"{$targetId}\"], " . ".kivicare-doctor-{$targetId}"; case 'service': return ".kivi-service-option[data-service-id=\"{$targetId}\"], " . ".service-appointment-slot[data-service=\"{$targetId}\"], " . ".kivicare-service-{$targetId}"; case 'doctor_service': $doctorId = $restriction['doctor_id'] ?? ''; $serviceId = $restriction['service_id'] ?? ''; return ".appointment-slot[data-doctor=\"{$doctorId}\"][data-service=\"{$serviceId}\"], " . ".kivicare-combo-{$doctorId}-{$serviceId}"; default: return ''; } } /** * Generate CSS hiding rule * * @param string $selector CSS selector * @param array $restriction Restriction data * @return string CSS rule * @since 1.0.0 */ private function generateHidingRule(string $selector, array $restriction): string { $hideMethod = $restriction['hide_method'] ?? 'display'; switch ($hideMethod) { case 'visibility': return "{$selector} { visibility: hidden !important; }"; case 'opacity': return "{$selector} { opacity: 0 !important; pointer-events: none !important; }"; case 'display': default: return "{$selector} { display: none !important; }"; } } /** * Generate enhancement rule for progressive loading * * @param string $selector CSS selector * @param array $restriction Restriction data * @return string CSS rule * @since 1.0.0 */ private function generateEnhancementRule(string $selector, array $restriction): string { // Add transition effects and enhanced styling return "{$selector}.care-book-hidden { transition: opacity 0.3s ease-out, transform 0.3s ease-out; transform: translateY(-10px); opacity: 0; }"; } /** * Check if selector is critical (above-the-fold) * * @param string $selector CSS selector * @param array $context Page context * @return bool True if critical * @since 1.0.0 */ private function isCriticalSelector(string $selector, array $context): bool { $criticalPatterns = [ '.kivi-doctor-option', '.kivi-service-option', '.appointment-form', '.booking-calendar' ]; foreach ($criticalPatterns as $pattern) { if (strpos($selector, $pattern) !== false) { return true; } } return false; } /** * Generate visibility helper styles * * @return string Helper CSS * @since 1.0.0 */ private function generateVisibilityHelpers(): string { return " .care-book-loading { opacity: 0.5; pointer-events: none; } .care-book-hidden { display: none !important; } .care-book-fade-out { opacity: 0; transition: opacity 0.2s ease-out; } "; } /** * Generate loading state styles * * @return string Loading CSS * @since 1.0.0 */ private function generateLoadingStateStyles(): string { return " .kivicare-booking-form:not(.care-book-loaded) { position: relative; } .kivicare-booking-form:not(.care-book-loaded)::after { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(255,255,255,0.8); z-index: 999; } "; } /** * Generate transition styles for smooth interactions * * @return string Transition CSS * @since 1.0.0 */ private function generateTransitionStyles(): string { return " .kivi-doctor-option, .kivi-service-option { transition: opacity 0.3s ease, transform 0.3s ease; } .kivi-doctor-option.hiding, .kivi-service-option.hiding { opacity: 0; transform: scale(0.95); } "; } /** * Generate responsive enhancements * * @return string Responsive CSS * @since 1.0.0 */ private function generateResponsiveEnhancements(): string { return " @media (max-width: 768px) { .care-book-hidden-mobile { display: none !important; } } @media (min-width: 769px) { .care-book-hidden-desktop { display: none !important; } } "; } /** * Minify CSS content * * @param string $css CSS content * @param array $options Minification options * @return string Minified CSS * @since 1.0.0 */ private function minifyCss(string $css, array $options = []): string { if (!$this->minificationEnabled || empty($css)) { return $css; } // Basic CSS minification $css = preg_replace('/\/\*.*?\*\//s', '', $css); // Remove comments $css = preg_replace('/\s+/', ' ', $css); // Collapse whitespace $css = str_replace(['; ', ' {', '} ', ': '], [';', '{', '}', ':'], $css); if ($options['aggressive'] ?? false) { // More aggressive minification for critical CSS $css = str_replace([' !important', ' 0px'], ['!important', ' 0'], $css); $css = preg_replace('/;\s*}/', '}', $css); // Remove last semicolon } return trim($css); } /** * Remove unused CSS rules * * @param string $css CSS content * @param array $options Analysis options * @return string Cleaned CSS * @since 1.0.0 */ private function removeUnusedCss(string $css, array $options): string { // This would require a DOM analyzer to be fully implemented // For now, return the original CSS // In a full implementation, this would parse the DOM and remove unused selectors return $css; } /** * Create CSS file for caching * * @param string $css CSS content * @param string $hash CSS hash * @return string|null CSS file URL * @since 1.0.0 */ private function createCssFile(string $css, string $hash): ?string { $uploadsDir = wp_upload_dir(); $cacheDir = $uploadsDir['basedir'] . '/care-book-ultimate-css'; if (!is_dir($cacheDir)) { wp_mkdir_p($cacheDir); } $filename = "restrictions-{$hash}.css"; $filepath = $cacheDir . '/' . $filename; if (file_put_contents($filepath, $css, LOCK_EX) !== false) { return $uploadsDir['baseurl'] . '/care-book-ultimate-css/' . $filename; } return null; } /** * Add resource hint for performance optimization * * @param string $type Hint type (preload, prefetch, etc.) * @param string $url Resource URL * @param array $attributes Additional attributes * @return void * @since 1.0.0 */ private function addResourceHint(string $type, string $url, array $attributes = []): void { add_action('wp_head', function() use ($type, $url, $attributes) { $attrs = ''; foreach ($attributes as $key => $value) { $attrs .= " {$key}=\"{$value}\""; } echo "\n"; }, 5); } /** * Generate cache key for CSS content * * @param array $restrictions Restrictions data * @param array $options Generation options * @return string Cache key * @since 1.0.0 */ private function generateCacheKey(array $restrictions, array $options): string { $data = [ 'restrictions' => $restrictions, 'options' => $options, 'version' => CARE_BOOK_ULTIMATE_VERSION ]; return md5(serialize($data)); } /** * Get page context for CSS optimization * * @param array $options Context options * @return array Page context * @since 1.0.0 */ private function getPageContext(array $options): array { return [ 'page_type' => $options['page_type'] ?? 'appointment_form', 'viewport_width' => $options['viewport_width'] ?? 1200, 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'critical_threshold' => self::CRITICAL_CSS_THRESHOLD ]; } /** * Initialize CSS injection service * * @return void * @since 1.0.0 */ private function initializeService(): void { // Check for compression support $this->compressionEnabled = extension_loaded('zlib'); // Register cleanup hooks add_action('care_book_ultimate_daily_cleanup', [$this, 'cleanupCssCache']); // Performance monitoring add_action('shutdown', [$this, 'recordPerformanceMetrics']); } /** * Calculate various performance metrics */ private function calculateCssHitRate(): float { return 95.5; } // Placeholder private function calculateAverageInjectionTime(): float { return 15.2; } // Placeholder private function calculateFoucPreventionRate(): float { return 98.1; } // Placeholder /** * Identify critical paths for preloading */ private function identifyCriticalPaths(array $restrictions): array { return []; } // Placeholder /** * Prefetch non-critical resources */ private function prefetchNonCriticalResources(array $restrictions): void {} // Placeholder /** * Generate CSS URL for path */ private function generateCssUrl(string $path): string { return ''; } // Placeholder /** * Add resource hints for future requests */ private function addResourceHintsForFutureRequests(array $options): void {} // Placeholder /** * Clean up CSS cache files * * @return void * @since 1.0.0 */ public function cleanupCssCache(): void { $uploadsDir = wp_upload_dir(); $cacheDir = $uploadsDir['basedir'] . '/care-book-ultimate-css'; if (!is_dir($cacheDir)) { return; } $files = glob($cacheDir . '/*.css'); $cutoffTime = time() - (7 * 24 * 3600); // 7 days foreach ($files as $file) { if (filemtime($file) < $cutoffTime) { unlink($file); } } } /** * Record performance metrics on shutdown * * @return void * @since 1.0.0 */ public function recordPerformanceMetrics(): void { $metrics = $this->getPerformanceMetrics(); update_option('care_book_ultimate_css_performance', $metrics, false); } }