/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ 95% cache hit rate */ const TARGET_CACHE_HIT_RATE = 95.0; /** * Initialize performance monitoring */ public static function init() { // Hook into WordPress performance points add_action('init', [__CLASS__, 'start_performance_tracking'], 1); add_action('wp_footer', [__CLASS__, 'end_performance_tracking'], 999); // AJAX performance tracking add_action('wp_ajax_care_booking_get_entities', [__CLASS__, 'track_ajax_start'], 1); add_action('wp_ajax_nopriv_care_booking_get_entities', [__CLASS__, 'track_ajax_start'], 1); // Database query performance add_filter('query', [__CLASS__, 'track_database_queries'], 10, 1); // Cache performance tracking add_action('care_booking_cache_hit', [__CLASS__, 'track_cache_hit']); add_action('care_booking_cache_miss', [__CLASS__, 'track_cache_miss']); // Memory usage tracking add_action('shutdown', [__CLASS__, 'track_memory_usage'], 1); } /** * Start performance tracking for page loads */ public static function start_performance_tracking() { if (!self::should_track_performance()) { return; } // Store start time and memory if (!defined('CARE_BOOKING_START_TIME')) { define('CARE_BOOKING_START_TIME', microtime(true)); define('CARE_BOOKING_START_MEMORY', memory_get_usage()); } } /** * End performance tracking and calculate metrics */ public static function end_performance_tracking() { if (!defined('CARE_BOOKING_START_TIME')) { return; } $end_time = microtime(true); $end_memory = memory_get_usage(); $execution_time = ($end_time - CARE_BOOKING_START_TIME) * 1000; // Convert to ms $memory_usage = $end_memory - CARE_BOOKING_START_MEMORY; // Calculate overhead percentage (plugin time vs total page time) $total_page_time = (microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000; $overhead_percent = ($execution_time / $total_page_time) * 100; $metrics = [ 'execution_time_ms' => round($execution_time, 2), 'memory_usage_bytes' => $memory_usage, 'overhead_percent' => round($overhead_percent, 2), 'timestamp' => time(), 'url' => $_SERVER['REQUEST_URI'] ?? '', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '' ]; self::store_performance_metrics($metrics); self::check_performance_targets($metrics); // Output debug info if enabled if (defined('WP_DEBUG') && WP_DEBUG && current_user_can('manage_options')) { self::output_debug_info($metrics); } } /** * Track AJAX request start time */ public static function track_ajax_start() { if (!defined('CARE_BOOKING_AJAX_START')) { define('CARE_BOOKING_AJAX_START', microtime(true)); } } /** * Track AJAX response completion * * @param mixed $response AJAX response data * @return mixed Original response */ public static function track_ajax_complete($response) { if (!defined('CARE_BOOKING_AJAX_START')) { return $response; } $response_time = (microtime(true) - CARE_BOOKING_AJAX_START) * 1000; $metrics = [ 'ajax_response_time_ms' => round($response_time, 2), 'ajax_action' => $_POST['action'] ?? '', 'timestamp' => time() ]; self::store_ajax_metrics($metrics); // Check if we're meeting AJAX performance targets if ($response_time > self::TARGET_AJAX_RESPONSE_MS) { self::log_performance_warning("AJAX response exceeded target: {$response_time}ms > " . self::TARGET_AJAX_RESPONSE_MS . "ms"); } return $response; } /** * Track database queries performance * * @param string $query SQL query * @return string Original query */ public static function track_database_queries($query) { // Only track Care Booking related queries if (strpos($query, 'care_booking_restrictions') === false) { return $query; } $start_time = microtime(true); // Use a filter to track completion add_filter('query_result', function($result) use ($start_time, $query) { $execution_time = (microtime(true) - $start_time) * 1000; if ($execution_time > 50) { // Log slow queries > 50ms self::log_performance_warning("Slow query detected: {$execution_time}ms - " . substr($query, 0, 100)); } return $result; }, 10, 1); return $query; } /** * Track cache hit * * @param string $cache_key Cache key that was hit */ public static function track_cache_hit($cache_key = '') { $stats = get_transient('care_booking_cache_stats') ?: ['hits' => 0, 'misses' => 0]; $stats['hits']++; $stats['last_hit'] = time(); set_transient('care_booking_cache_stats', $stats, HOUR_IN_SECONDS); } /** * Track cache miss * * @param string $cache_key Cache key that was missed */ public static function track_cache_miss($cache_key = '') { $stats = get_transient('care_booking_cache_stats') ?: ['hits' => 0, 'misses' => 0]; $stats['misses']++; $stats['last_miss'] = time(); set_transient('care_booking_cache_stats', $stats, HOUR_IN_SECONDS); // Log excessive cache misses $total = $stats['hits'] + $stats['misses']; if ($total > 10 && (($stats['hits'] / $total) * 100) < self::TARGET_CACHE_HIT_RATE) { self::log_performance_warning("Cache hit rate below target: " . round(($stats['hits'] / $total) * 100, 1) . "%"); } } /** * Track memory usage */ public static function track_memory_usage() { $current_memory = memory_get_usage(); $peak_memory = memory_get_peak_usage(); // Target: <10MB footprint $target_memory = 10 * 1024 * 1024; // 10MB in bytes if (defined('CARE_BOOKING_START_MEMORY')) { $plugin_memory = $current_memory - CARE_BOOKING_START_MEMORY; if ($plugin_memory > $target_memory) { self::log_performance_warning("Memory usage exceeded target: " . size_format($plugin_memory) . " > 10MB"); } } } /** * Store performance metrics * * @param array $metrics Performance metrics */ private static function store_performance_metrics($metrics) { $stored_metrics = get_transient(self::METRICS_CACHE_KEY) ?: []; // Keep only last 100 measurements for performance if (count($stored_metrics) >= 100) { $stored_metrics = array_slice($stored_metrics, -99); } $stored_metrics[] = $metrics; set_transient(self::METRICS_CACHE_KEY, $stored_metrics, DAY_IN_SECONDS); } /** * Store AJAX performance metrics * * @param array $metrics AJAX metrics */ private static function store_ajax_metrics($metrics) { $ajax_metrics = get_transient('care_booking_ajax_metrics') ?: []; if (count($ajax_metrics) >= 50) { $ajax_metrics = array_slice($ajax_metrics, -49); } $ajax_metrics[] = $metrics; set_transient('care_booking_ajax_metrics', $ajax_metrics, DAY_IN_SECONDS); } /** * Check if performance targets are being met * * @param array $metrics Current performance metrics */ private static function check_performance_targets($metrics) { $warnings = []; // Check overhead target (<2%) if ($metrics['overhead_percent'] > self::TARGET_OVERHEAD_PERCENT) { $warnings[] = "Page overhead exceeded target: {$metrics['overhead_percent']}% > " . self::TARGET_OVERHEAD_PERCENT . "%"; } // Check execution time target (<50ms for plugin operations) if ($metrics['execution_time_ms'] > 50) { $warnings[] = "Plugin execution time high: {$metrics['execution_time_ms']}ms"; } // Check memory usage target (<10MB) $memory_mb = $metrics['memory_usage_bytes'] / (1024 * 1024); if ($memory_mb > 10) { $warnings[] = "Memory usage exceeded target: " . round($memory_mb, 2) . "MB > 10MB"; } foreach ($warnings as $warning) { self::log_performance_warning($warning); } } /** * Log performance warning * * @param string $message Warning message */ private static function log_performance_warning($message) { if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) { error_log("Care Booking Performance Warning: " . $message); } // Store in admin notices if user is admin if (current_user_can('manage_options')) { $notices = get_transient('care_booking_performance_notices') ?: []; $notices[] = [ 'message' => $message, 'timestamp' => time(), 'severity' => 'warning' ]; // Keep only last 10 notices if (count($notices) > 10) { $notices = array_slice($notices, -10); } set_transient('care_booking_performance_notices', $notices, HOUR_IN_SECONDS); } } /** * Get comprehensive performance report * * @return array Performance report */ public static function get_performance_report() { $metrics = get_transient(self::METRICS_CACHE_KEY) ?: []; $ajax_metrics = get_transient('care_booking_ajax_metrics') ?: []; $cache_stats = get_transient('care_booking_cache_stats') ?: ['hits' => 0, 'misses' => 0]; if (empty($metrics)) { return ['status' => 'no_data']; } // Calculate averages $avg_overhead = array_sum(array_column($metrics, 'overhead_percent')) / count($metrics); $avg_execution = array_sum(array_column($metrics, 'execution_time_ms')) / count($metrics); $avg_memory = array_sum(array_column($metrics, 'memory_usage_bytes')) / count($metrics); // Calculate cache hit rate $total_cache_requests = $cache_stats['hits'] + $cache_stats['misses']; $cache_hit_rate = $total_cache_requests > 0 ? ($cache_stats['hits'] / $total_cache_requests) * 100 : 0; // Calculate AJAX averages $avg_ajax_response = !empty($ajax_metrics) ? array_sum(array_column($ajax_metrics, 'ajax_response_time_ms')) / count($ajax_metrics) : 0; return [ 'status' => 'active', 'targets' => [ 'overhead_percent' => self::TARGET_OVERHEAD_PERCENT, 'ajax_response_ms' => self::TARGET_AJAX_RESPONSE_MS, 'cache_hit_rate' => self::TARGET_CACHE_HIT_RATE ], 'current' => [ 'avg_overhead_percent' => round($avg_overhead, 2), 'avg_execution_time_ms' => round($avg_execution, 2), 'avg_memory_usage_mb' => round($avg_memory / (1024 * 1024), 2), 'cache_hit_rate_percent' => round($cache_hit_rate, 2), 'avg_ajax_response_ms' => round($avg_ajax_response, 2) ], 'performance_score' => self::calculate_performance_score($avg_overhead, $avg_ajax_response, $cache_hit_rate), 'measurements_count' => count($metrics), 'last_measurement' => max(array_column($metrics, 'timestamp')) ]; } /** * Calculate overall performance score (0-100) * * @param float $overhead_percent Current overhead percentage * @param float $ajax_response_ms Current AJAX response time * @param float $cache_hit_rate Current cache hit rate * @return int Performance score */ private static function calculate_performance_score($overhead_percent, $ajax_response_ms, $cache_hit_rate) { $score = 100; // Deduct points for overhead (target <2%) if ($overhead_percent > self::TARGET_OVERHEAD_PERCENT) { $score -= min(30, ($overhead_percent - self::TARGET_OVERHEAD_PERCENT) * 10); } // Deduct points for AJAX response time (target <100ms) if ($ajax_response_ms > self::TARGET_AJAX_RESPONSE_MS) { $score -= min(30, ($ajax_response_ms - self::TARGET_AJAX_RESPONSE_MS) / 10); } // Deduct points for cache hit rate (target >95%) if ($cache_hit_rate < self::TARGET_CACHE_HIT_RATE) { $score -= min(25, (self::TARGET_CACHE_HIT_RATE - $cache_hit_rate)); } return max(0, (int) $score); } /** * Should track performance based on current context * * @return bool True if should track */ private static function should_track_performance() { // Don't track in admin area unless specifically enabled if (is_admin() && !defined('CARE_BOOKING_TRACK_ADMIN_PERFORMANCE')) { return false; } // Don't track for bots and crawlers $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? ''; if (preg_match('/bot|crawler|spider|robot/i', $user_agent)) { return false; } return true; } /** * Output debug information * * @param array $metrics Performance metrics */ private static function output_debug_info($metrics) { echo "\n\n"; echo "\n"; echo "\n"; echo "\n"; echo "\n"; $status = $metrics['overhead_percent'] <= self::TARGET_OVERHEAD_PERCENT ? 'MEETING TARGET' : 'EXCEEDING TARGET'; echo "\n"; echo "\n"; } /** * Get performance notices for admin display * * @return array Performance notices */ public static function get_performance_notices() { return get_transient('care_booking_performance_notices') ?: []; } /** * Clear performance notices */ public static function clear_performance_notices() { delete_transient('care_booking_performance_notices'); } /** * Get asset optimization statistics * * @return array Asset optimization stats */ public static function get_asset_stats() { $asset_files = [ 'admin_css' => [ 'original' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/css/admin-style.css', 'minified' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/css/admin-style.min.css' ], 'admin_js' => [ 'original' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/js/admin-script.js', 'minified' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/js/admin-script.min.js' ], 'frontend_css' => [ 'original' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/css/frontend.css', 'minified' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/css/frontend.min.css' ], 'frontend_js' => [ 'original' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/js/frontend.js', 'minified' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/js/frontend.min.js' ] ]; $stats = []; $total_original = 0; $total_minified = 0; foreach ($asset_files as $key => $files) { $original_size = file_exists($files['original']) ? filesize($files['original']) : 0; $minified_size = file_exists($files['minified']) ? filesize($files['minified']) : 0; $savings_bytes = $original_size - $minified_size; $savings_percent = $original_size > 0 ? ($savings_bytes / $original_size) * 100 : 0; $stats[$key] = [ 'original_size' => $original_size, 'minified_size' => $minified_size, 'savings_bytes' => $savings_bytes, 'savings_percent' => round($savings_percent, 1) ]; $total_original += $original_size; $total_minified += $minified_size; } $total_savings = $total_original - $total_minified; $total_savings_percent = $total_original > 0 ? ($total_savings / $total_original) * 100 : 0; $stats['total'] = [ 'original_size' => $total_original, 'minified_size' => $total_minified, 'savings_bytes' => $total_savings, 'savings_percent' => round($total_savings_percent, 1) ]; return $stats; } } // Initialize performance monitoring add_action('plugins_loaded', [Care_Booking_Performance_Monitor::class, 'init'], 5);