debug_log_file = WP_CONTENT_DIR . '/safari-debug.log'; $this->init_hooks(); } /** * Initialize hooks */ private function init_hooks() { // Hook very early to catch all requests add_action('init', [$this, 'debug_safari_request'], 1); // Log memory usage during page load add_action('wp_loaded', [$this, 'log_memory_usage']); // Monitor plugin loading for Safari requests add_action('plugins_loaded', [$this, 'log_plugins_loaded']); // Catch fatal errors register_shutdown_function([$this, 'catch_fatal_errors']); } /** * Check if current request is from Safari * * @return bool */ private function is_safari_request() { if (!isset($_SERVER['HTTP_USER_AGENT'])) { return false; } $user_agent = $_SERVER['HTTP_USER_AGENT']; // Check for Safari but not Chrome (Chrome also contains Safari in UA) return (strpos($user_agent, 'Safari') !== false && strpos($user_agent, 'Chrome') === false && strpos($user_agent, 'Chromium') === false); } /** * Debug Safari request details */ public function debug_safari_request() { if (!$this->is_safari_request()) { return; } $request_data = [ 'timestamp' => current_time('Y-m-d H:i:s'), 'url' => $_SERVER['REQUEST_URI'] ?? '', 'method' => $_SERVER['REQUEST_METHOD'] ?? '', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'accept' => $_SERVER['HTTP_ACCEPT'] ?? '', 'accept_encoding' => $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '', 'accept_language' => $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '', 'connection' => $_SERVER['HTTP_CONNECTION'] ?? '', 'host' => $_SERVER['HTTP_HOST'] ?? '', 'referer' => $_SERVER['HTTP_REFERER'] ?? '', 'remote_addr' => $_SERVER['REMOTE_ADDR'] ?? '', 'server_name' => $_SERVER['SERVER_NAME'] ?? '', 'memory_limit' => ini_get('memory_limit'), 'max_execution_time' => ini_get('max_execution_time'), 'memory_usage' => $this->format_bytes(memory_get_usage()), 'memory_peak' => $this->format_bytes(memory_get_peak_usage()), 'php_version' => PHP_VERSION ]; $this->log_debug('SAFARI REQUEST START', $request_data); // Check if this is the find-a-trainer page specifically if (strpos($_SERVER['REQUEST_URI'], 'find-a-trainer') !== false) { $this->log_debug('FIND-A-TRAINER PAGE REQUEST', [ 'page' => 'find-a-trainer', 'plugins_loaded' => did_action('plugins_loaded'), 'wp_loaded' => did_action('wp_loaded'), 'theme_setup' => did_action('after_setup_theme') ]); } } /** * Log memory usage at key points */ public function log_memory_usage() { if (!$this->is_safari_request()) { return; } $memory_data = [ 'current_usage' => $this->format_bytes(memory_get_usage()), 'peak_usage' => $this->format_bytes(memory_get_peak_usage()), 'real_usage' => $this->format_bytes(memory_get_usage(true)), 'real_peak' => $this->format_bytes(memory_get_peak_usage(true)), 'limit' => ini_get('memory_limit'), 'available' => $this->calculate_available_memory() ]; $this->log_debug('MEMORY USAGE AT WP_LOADED', $memory_data); } /** * Log plugins loaded status */ public function log_plugins_loaded() { if (!$this->is_safari_request()) { return; } // Get list of active plugins $active_plugins = get_option('active_plugins', []); $problematic_plugins = [ 'interactive-geo-maps/interactive-geo-maps.php', 'formidable/formidable.php', 'the-events-calendar/the-events-calendar.php', 'events-calendar-pro/events-calendar-pro.php' ]; $plugin_data = [ 'total_active_plugins' => count($active_plugins), 'problematic_plugins_active' => array_intersect($active_plugins, $problematic_plugins), 'hvac_plugin_active' => in_array('hvac-community-events/hvac-community-events.php', $active_plugins), 'memory_after_plugins' => $this->format_bytes(memory_get_usage()) ]; $this->log_debug('PLUGINS LOADED', $plugin_data); // Test specific plugin interactions $this->test_plugin_interactions(); } /** * Test specific plugin interactions that might cause Safari issues */ private function test_plugin_interactions() { try { // Test MapGeo plugin if active if (class_exists('Interactive_Geo_Maps')) { $this->log_debug('MAPGEO PLUGIN DETECTED', [ 'class_exists' => true, 'memory_usage' => $this->format_bytes(memory_get_usage()) ]); } // Test Events Calendar integration if (function_exists('tribe_get_events')) { $this->log_debug('EVENTS CALENDAR INTEGRATION', [ 'tribe_functions_available' => true, 'memory_usage' => $this->format_bytes(memory_get_usage()) ]); } // Test HVAC plugin classes if (class_exists('HVAC_Community_Events')) { $this->log_debug('HVAC PLUGIN CLASSES', [ 'main_class_loaded' => true, 'scripts_styles_class' => class_exists('HVAC_Scripts_Styles'), 'template_loader_class' => class_exists('HVAC_Template_Loader'), 'memory_usage' => $this->format_bytes(memory_get_usage()) ]); } } catch (Exception $e) { $this->log_debug('PLUGIN INTERACTION ERROR', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); } catch (Error $e) { $this->log_debug('PLUGIN INTERACTION FATAL', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); } } /** * Catch fatal errors and log them */ public function catch_fatal_errors() { if (!$this->is_safari_request()) { return; } $error = error_get_last(); if ($error && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) { $this->log_debug('FATAL ERROR DETECTED', [ 'type' => $error['type'], 'message' => $error['message'], 'file' => $error['file'], 'line' => $error['line'], 'memory_usage' => $this->format_bytes(memory_get_usage()), 'memory_peak' => $this->format_bytes(memory_get_peak_usage()) ]); } // Log successful completion if no fatal error if (!$error || !in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) { $this->log_debug('REQUEST COMPLETED SUCCESSFULLY', [ 'final_memory_usage' => $this->format_bytes(memory_get_usage()), 'final_memory_peak' => $this->format_bytes(memory_get_peak_usage()) ]); } } /** * Log debug information * * @param string $message * @param array $data */ private function log_debug($message, $data = []) { $log_entry = [ 'timestamp' => current_time('c'), 'message' => $message, 'data' => $data, 'request_id' => $this->get_request_id() ]; $log_line = '[' . date('Y-m-d H:i:s') . '] ' . $message . ' | ' . wp_json_encode($data) . PHP_EOL; // Write to custom log file if (is_writable(dirname($this->debug_log_file))) { error_log($log_line, 3, $this->debug_log_file); } // Also write to WordPress debug log if enabled if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) { error_log('[SAFARI-REQUEST-DEBUG] ' . $log_line); } } /** * Generate unique request ID * * @return string */ private function get_request_id() { static $request_id = null; if ($request_id === null) { $request_id = uniqid('safari_', true); } return $request_id; } /** * Format bytes for readable output * * @param int $bytes * @return string */ private function format_bytes($bytes) { $units = ['B', 'KB', 'MB', 'GB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= pow(1024, $pow); return round($bytes, 2) . ' ' . $units[$pow]; } /** * Calculate available memory * * @return string */ private function calculate_available_memory() { $limit = ini_get('memory_limit'); if ($limit === '-1') { return 'unlimited'; } $limit_bytes = $this->parse_memory_limit($limit); $used_bytes = memory_get_usage(); $available_bytes = $limit_bytes - $used_bytes; return $this->format_bytes($available_bytes); } /** * Parse memory limit string to bytes * * @param string $limit * @return int */ private function parse_memory_limit($limit) { $limit = trim($limit); $last = strtolower($limit[strlen($limit) - 1]); $limit = (int)$limit; switch ($last) { case 'g': $limit *= 1024; case 'm': $limit *= 1024; case 'k': $limit *= 1024; } return $limit; } } // Initialize only if this is a Safari request if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== false && strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') === false) { HVAC_Safari_Request_Debugger::instance(); }