Created
September 11, 2024 07:08
-
-
Save shaunpalmer/b5cef2e784fa570905467c11a284980c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Configuration class to manage settings | |
| */ | |
| class WP_Error_Handler_Config { | |
| private $config; | |
| public function __construct(array $config = []) { | |
| $this->config = array_merge([ | |
| 'max_views' => 15, | |
| 'error_timeout' => 300, | |
| 'log_file' => WP_CONTENT_DIR . '/debug.log', | |
| 'log_rotation_size' => 5 * 1024 * 1024, // 5MB | |
| ], $config); | |
| } | |
| public function get($key) { | |
| return $this->config[$key] ?? null; | |
| } | |
| } | |
| /** | |
| * Logging class for error logging | |
| */ | |
| class WP_Error_Logger { | |
| private $config; | |
| public function __construct(WP_Error_Handler_Config $config) { | |
| $this->config = $config; | |
| } | |
| public function log_error($error) { | |
| $backtrace = function_exists('wp_debug_backtrace_summary') ? wp_debug_backtrace_summary() : 'Backtrace not available'; | |
| $log_message = sprintf( | |
| "[%s] %s: %s in %s on line %d\nBacktrace: %s", | |
| $error['timestamp'], | |
| $this->get_error_type_string($error['type']), | |
| $error['message'], | |
| $error['file'], | |
| $error['line'], | |
| $backtrace | |
| ); | |
| $this->write_to_log($log_message); | |
| } | |
| private function write_to_log($message) { | |
| $log_file = $this->config->get('log_file'); | |
| if (file_exists($log_file) && filesize($log_file) > $this->config->get('log_rotation_size')) { | |
| rename($log_file, $log_file . '.' . date('Y-m-d-H-i-s')); | |
| } | |
| error_log($message . PHP_EOL, 3, $log_file); | |
| } | |
| private function get_error_type_string($type) { | |
| $error_types = [ | |
| E_ERROR => 'E_ERROR', | |
| E_WARNING => 'E_WARNING', | |
| E_PARSE => 'E_PARSE', | |
| E_NOTICE => 'E_NOTICE', | |
| E_CORE_ERROR => 'E_CORE_ERROR', | |
| E_CORE_WARNING => 'E_CORE_WARNING', | |
| E_COMPILE_ERROR => 'E_COMPILE_ERROR', | |
| E_COMPILE_WARNING => 'E_COMPILE_WARNING', | |
| E_USER_ERROR => 'E_USER_ERROR', | |
| E_USER_WARNING => 'E_USER_WARNING', | |
| E_USER_NOTICE => 'E_USER_NOTICE', | |
| E_STRICT => 'E_STRICT', | |
| E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', | |
| E_DEPRECATED => 'E_DEPRECATED', | |
| E_USER_DEPRECATED => 'E_USER_DEPRECATED', | |
| ]; | |
| return isset($error_types[$type]) ? $error_types[$type] : 'UNKNOWN'; | |
| } | |
| } | |
| /** | |
| * AdminNotices class for handling admin notices | |
| */ | |
| class WP_Error_Admin_Notices { | |
| private $notices = []; | |
| private $config; | |
| public function __construct(WP_Error_Handler_Config $config) { | |
| $this->config = $config; | |
| add_action('admin_notices', [$this, 'display_admin_notices']); | |
| } | |
| public function queue_admin_notice($error, $type = 'warning') { | |
| $notice = sprintf( | |
| '<div class="notice notice-%s is-dismissible"><p>[%s] Error: %s in file %s on line %d</p></div>', | |
| esc_attr($type), | |
| esc_html($error['timestamp']), | |
| esc_html($error['message']), | |
| esc_html($error['file']), | |
| esc_html($error['line']) | |
| ); | |
| $this->notices[] = $notice; | |
| } | |
| public function display_admin_notices() { | |
| foreach ($this->notices as $notice) { | |
| echo $notice; | |
| } | |
| } | |
| } | |
| /** | |
| * Main ErrorManagement class | |
| */ | |
| class WP_Error_Handler { | |
| private static $instance = null; | |
| private $config; | |
| private $logger; | |
| private $admin_notices; | |
| private $error_history = []; | |
| private $error_views = []; | |
| private function __construct(WP_Error_Handler_Config $config) { | |
| $this->config = $config; | |
| $this->logger = new WP_Error_Logger($config); | |
| $this->admin_notices = new WP_Error_Admin_Notices($config); | |
| $this->set_error_reporting(); | |
| } | |
| public static function get_instance(WP_Error_Handler_Config $config = null) { | |
| if (self::$instance === null) { | |
| self::$instance = new self($config ?? new WP_Error_Handler_Config()); | |
| } | |
| return self::$instance; | |
| } | |
| private function set_error_reporting() { | |
| if (defined('WP_DEBUG') && WP_DEBUG) { | |
| error_reporting(E_ALL); | |
| ini_set('display_errors', 1); | |
| } else { | |
| error_reporting(0); | |
| ini_set('display_errors', 0); | |
| } | |
| } | |
| public function handle_error($errno, $errstr, $errfile, $errline) { | |
| $error = [ | |
| 'type' => $errno, | |
| 'message' => $errstr, | |
| 'file' => $errfile, | |
| 'line' => $errline, | |
| 'timestamp' => $this->get_current_timestamp() | |
| ]; | |
| $error_key = $this->generate_error_key($error); | |
| if (!isset($this->error_views[$error_key])) { | |
| $this->error_views[$error_key] = 0; | |
| } | |
| $this->error_views[$error_key]++; | |
| if ($this->error_views[$error_key] <= $this->config->get('max_views') && !$this->is_duplicate_error($error_key)) { | |
| $this->logger->log_error($error); | |
| if ($this->is_resolving($error)) { | |
| $this->admin_notices->queue_admin_notice($error); | |
| } else { | |
| $this->not_resolving($error); | |
| } | |
| $this->error_history[$error_key] = time(); | |
| } | |
| return true; | |
| } | |
| private function is_duplicate_error($error_key) { | |
| if (isset($this->error_history[$error_key])) { | |
| if (time() - $this->error_history[$error_key] < $this->config->get('error_timeout')) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| private function generate_error_key($error) { | |
| return md5($error['type'] . $error['message'] . $error['file'] . $error['line']); | |
| } | |
| private function get_current_timestamp() { | |
| return date('Y-m-d H:i:s'); | |
| } | |
| private function is_resolving($error) { | |
| return !in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]); | |
| } | |
| private function not_resolving($error) { | |
| $this->admin_notices->queue_admin_notice($error, 'error'); | |
| $this->trigger_recovery_mode(); | |
| } | |
| private function trigger_recovery_mode() { | |
| if (function_exists('wp_fatal_error_handler_enabled') && wp_fatal_error_handler_enabled()) { | |
| wp_die( | |
| __('There has been a critical error on this website. Please check the site admin email inbox for instructions.'), | |
| __('WordPress Fatal Error'), | |
| ['response' => 500] | |
| ); | |
| } | |
| } | |
| } | |
| /* Usage example | |
| $config = new WP_Error_Handler_Config([ | |
| 'max_views' => 20, | |
| 'error_timeout' => 600, | |
| ]); | |
| $error_handler = WP_Error_Handler::get_instance($config); | |
| set_error_handler([$error_handler, 'handle_error']); | |
| function divide_numbers($a, $b) { | |
| if ($b == 0) { | |
| throw new Exception("Division by zero!"); | |
| } | |
| return $a / $b; | |
| } | |
| function some_functionality() { | |
| try { | |
| $result = divide_numbers(10, 0); | |
| echo "Result: " . $result; | |
| } catch (Exception $e) { | |
| trigger_error($e->getMessage(), E_USER_ERROR); | |
| } | |
| } | |
| /# Call the function to demonstrate error handling | |
| some_functionality(); | |
| ?> | |
| /* Usage example remains the same as before | |
| function divide_numbers($a, $b) { | |
| if ($b == 0) { | |
| throw new Exception("Division by zero!"); | |
| } | |
| return $a / $b; | |
| } | |
| function some_functionality() { | |
| $error_handler = WP_Error_Handler::get_instance(); | |
| set_error_handler([$error_handler, 'handle_error']); | |
| try { | |
| $result = divide_numbers(10, 0); | |
| echo "Result: " . $result; | |
| } catch (Exception $e) { | |
| trigger_error($e->getMessage(), E_USER_ERROR); | |
| } finally { | |
| restore_error_handler(); | |
| } | |
| } | |
| # Call the function to demonstrate error handling | |
| some_functionality(); | |
| */ | |
| ?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment