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 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 "
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 "Total logged queries:
Slow queries:
Average execution time: s
| Time | Duration | Query | Caller | URL |
|---|---|---|---|---|
| s |