0, 'slow_queries' => 0, 'total_time' => 0, 'peak_memory' => 0 ]; /** * Initialize monitoring */ public static function init() { // Only enable in development or when explicitly requested if (!self::should_monitor()) { return; } // Hook into WordPress query system add_filter('query', [__CLASS__, 'log_query'], 999); add_action('shutdown', [__CLASS__, 'analyze_queries']); // Admin hooks if (is_admin()) { add_action('admin_menu', [__CLASS__, 'add_admin_page']); add_action('wp_ajax_hvac_clear_query_log', [__CLASS__, 'ajax_clear_log']); } // WP-CLI integration (disabled - method not implemented) // if (defined('WP_CLI') && WP_CLI) { // WP_CLI::add_command('hvac query-monitor', [__CLASS__, 'wp_cli_commands']); // } } /** * Check if monitoring should be enabled * * @return bool */ private static function should_monitor() { // Enable if WP_DEBUG is on if (defined('WP_DEBUG') && WP_DEBUG) { return true; } // Enable if explicitly requested if (defined('HVAC_QUERY_MONITOR') && HVAC_QUERY_MONITOR) { return true; } // Enable for admin users with specific parameter if (is_admin() && current_user_can('manage_options') && isset($_GET['hvac_monitor'])) { return true; } return false; } /** * Log database query * * @param string $query SQL query * @return string Original query */ public static function log_query($query) { $start_time = microtime(true); // Get calling function information $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10); $caller_info = self::get_caller_info($backtrace); // Execute query timing logic by hooking into WordPress add_action('wp_footer', function() use ($query, $start_time, $caller_info) { $end_time = microtime(true); $execution_time = $end_time - $start_time; self::record_query($query, $execution_time, $caller_info); }, 999); return $query; } /** * Record query information * * @param string $query SQL query * @param float $execution_time Query execution time * @param array $caller_info Caller information */ private static function record_query($query, $execution_time, $caller_info) { // Skip if query is too common or not from our plugin if (!self::should_log_query($query, $caller_info)) { return; } $is_slow = $execution_time > self::SLOW_QUERY_THRESHOLD; $query_data = [ 'query' => self::sanitize_query($query), 'execution_time' => round($execution_time, 4), 'is_slow' => $is_slow, 'caller' => $caller_info, 'memory_usage' => memory_get_usage(true), 'timestamp' => time(), 'url' => $_SERVER['REQUEST_URI'] ?? 'unknown' ]; self::$queries[] = $query_data; // Update statistics self::$stats['total_queries']++; self::$stats['total_time'] += $execution_time; self::$stats['peak_memory'] = max(self::$stats['peak_memory'], $query_data['memory_usage']); if ($is_slow) { self::$stats['slow_queries']++; // Log slow queries immediately HVAC_Logger::warning( "Slow query detected ({$execution_time}s): " . substr($query, 0, 200), 'Query Monitor' ); } } /** * Get caller information from backtrace * * @param array $backtrace Debug backtrace * @return array Caller info */ private static function get_caller_info($backtrace) { foreach ($backtrace as $trace) { $file = $trace['file'] ?? ''; $function = $trace['function'] ?? ''; // Look for HVAC plugin functions if (strpos($file, 'hvac-community-events') !== false) { return [ 'file' => basename($file), 'line' => $trace['line'] ?? 0, 'function' => $function, 'class' => $trace['class'] ?? '' ]; } } // Fallback to first available trace $first = $backtrace[0] ?? []; return [ 'file' => basename($first['file'] ?? 'unknown'), 'line' => $first['line'] ?? 0, 'function' => $first['function'] ?? 'unknown', 'class' => $first['class'] ?? '' ]; } /** * Check if query should be logged * * @param string $query SQL query * @param array $caller_info Caller information * @return bool */ private static function should_log_query($query, $caller_info) { // Only log queries from our plugin $hvac_files = ['hvac-', 'HVAC_']; $is_hvac_query = false; foreach ($hvac_files as $prefix) { if (strpos($caller_info['file'], $prefix) !== false || strpos($caller_info['class'], $prefix) !== false) { $is_hvac_query = true; break; } } if (!$is_hvac_query) { return false; } // Skip common WordPress core queries $skip_patterns = [ 'SELECT option_value FROM', 'SELECT autoload FROM', 'SELECT meta_value FROM.*_usermeta WHERE meta_key = \'wp_', 'UPDATE.*_options SET option_value' ]; foreach ($skip_patterns as $pattern) { if (preg_match('/' . $pattern . '/i', $query)) { return false; } } return true; } /** * Sanitize query for logging * * @param string $query SQL query * @return string Sanitized query */ private static function sanitize_query($query) { // Remove potential sensitive data $query = preg_replace('/\'[^\']*\'/', "'[DATA]'", $query); $query = preg_replace('/\b\d+\b/', '[NUM]', $query); return trim($query); } /** * Analyze queries on shutdown */ public static function analyze_queries() { if (empty(self::$queries)) { return; } // Store queries in log self::store_query_log(); // Generate recommendations $recommendations = self::generate_recommendations(); if (!empty($recommendations)) { HVAC_Logger::info( 'Query optimization recommendations: ' . implode('; ', $recommendations), 'Query Monitor' ); } // Add debug output for admin users if (current_user_can('manage_options') && isset($_GET['hvac_debug_queries'])) { self::output_debug_info(); } } /** * Store query log */ private static function store_query_log() { $existing_log = get_option(self::LOG_OPTION, []); // Add new queries $new_log = array_merge($existing_log, self::$queries); // Keep only recent entries $new_log = array_slice($new_log, -self::MAX_LOG_ENTRIES); update_option(self::LOG_OPTION, $new_log); } /** * Generate optimization recommendations * * @return array Recommendations */ private static function generate_recommendations() { $recommendations = []; // Check for slow queries if (self::$stats['slow_queries'] > 0) { $recommendations[] = "Found " . self::$stats['slow_queries'] . " slow queries - consider adding indexes or caching"; } // Check total query time if (self::$stats['total_time'] > 1.0) { $recommendations[] = "Total query time is high (" . round(self::$stats['total_time'], 2) . "s) - consider query optimization"; } // Check for duplicate queries $query_counts = array_count_values(array_column(self::$queries, 'query')); $duplicates = array_filter($query_counts, function($count) { return $count > 1; }); if (!empty($duplicates)) { $recommendations[] = "Found " . count($duplicates) . " duplicate query patterns - consider caching"; } // Check memory usage if (self::$stats['peak_memory'] > 50 * 1024 * 1024) { // 50MB $recommendations[] = "High memory usage detected - consider result set optimization"; } return $recommendations; } /** * Output debug information */ private static function output_debug_info() { echo "\n\n"; echo "
"; echo "

HVAC Query Monitor

"; echo "

Total Queries: " . self::$stats['total_queries'] . " | "; echo "Slow Queries: " . self::$stats['slow_queries'] . " | "; echo "Total Time: " . round(self::$stats['total_time'], 3) . "s | "; echo "Peak Memory: " . size_format(self::$stats['peak_memory']) . "

"; if (!empty(self::$queries)) { echo "
Query Details"; foreach (array_slice(self::$queries, -10) as $query) { echo "
"; echo "" . $query['execution_time'] . "s - "; echo htmlspecialchars(substr($query['query'], 0, 100)) . "..."; echo "
Called from: " . $query['caller']['file'] . ":" . $query['caller']['line'] . ""; echo "
"; } echo "
"; } echo "
\n"; } /** * Add admin page */ public static function add_admin_page() { if (current_user_can('manage_options')) { add_management_page( 'HVAC Query Monitor', 'HVAC Query Monitor', 'manage_options', 'hvac-query-monitor', [__CLASS__, 'admin_page'] ); } } /** * Admin page content */ public static function admin_page() { $log = get_option(self::LOG_OPTION, []); $recent_log = array_slice($log, -20); ?>

HVAC Query Monitor

Query Statistics

Total logged queries:

Slow queries:

Average execution time: s

Clear Log Enable Debug Output

Recent Queries (Last 20)

>
Time Duration Query Caller URL
s
0, 'slow_queries' => 0, 'average_time' => 0, 'recommendations' => [] ]; } $slow_queries = array_filter($log, function($q) { return $q['is_slow']; }); $total_time = array_sum(array_column($log, 'execution_time')); return [ 'total_queries' => count($log), 'slow_queries' => count($slow_queries), 'average_time' => $total_time / count($log), 'total_time' => $total_time, 'recommendations' => self::generate_recommendations_from_log($log) ]; } /** * Generate recommendations from stored log * * @param array $log Query log * @return array Recommendations */ private static function generate_recommendations_from_log($log) { $recommendations = []; // Analyze patterns in stored log $query_patterns = []; foreach ($log as $query_data) { $pattern = preg_replace('/\[DATA\]|\[NUM\]/', 'X', $query_data['query']); $query_patterns[$pattern] = ($query_patterns[$pattern] ?? 0) + 1; } // Find frequently repeated queries $frequent_queries = array_filter($query_patterns, function($count) { return $count > 5; }); if (!empty($frequent_queries)) { $recommendations[] = "Consider caching for " . count($frequent_queries) . " frequently repeated queries"; } return $recommendations; } }