wp_config = new WP_Config_Transformer; // already in the DLM\Classes namespace, so, no need to repeat } /** * Get status of WP_DEBUG * * @since 1.0.0 */ public function get_status() { $value = get_option( 'debug_log_manager' ); $status = $value['status']; $date_time = wp_date( 'M j, Y - H:i:s', strtotime( $value['on'] ) ); return '
Error Logging: '. esc_html( ucfirst( $status ) ) .' on '. esc_html( $date_time ) .'
'; } /** * Get log auto-refresh status * * @since 1.3.0 */ public function get_autorefresh_status() { if ( false !== get_option( 'debug_log_manager_autorefresh' ) ) { $autorefresh_status = get_option( 'debug_log_manager_autorefresh' ); } else { $autorefresh_status = 'disabled'; update_option( 'debug_log_manager_autorefresh', $autorefresh_status, false ); } return '
Auto-Refresh: ' . ucfirst( $autorefresh_status ) . '
'; } /** * Enable / disable WP_DEBUG * * @since 1.0.0 */ public function toggle_debugging() { if ( isset( $_REQUEST ) ) { $log_info = get_option( 'debug_log_manager' ); $dlm_debug_log_file_path = get_option( 'debug_log_manager_file_path' ); $date_time = wp_date( 'M j, Y - H:i:s' ); // Localized according to WP timezone settings $date_time_for_option = date( 'M j, Y H:i:s' ); // in UTC if ( 'disabled' == $log_info['status'] ) { $option_value = array( 'status' => 'enabled', 'on' => $date_time_for_option, ); update_option( 'debug_log_manager', $option_value, false ); // If WP_DEBUG_LOG is defined, copy content of existing debug.log file into the log file created by this plugin if ( $this->wp_config->exists( 'constant', 'WP_DEBUG_LOG' ) ) { $wp_debug_log_const = $this->wp_config->get_value( 'constant', 'WP_DEBUG_LOG' ); if ( in_array( $wp_debug_log_const, array( 'true', 'false' ), true ) ) { $wp_debug_log_const = (bool) $wp_debug_log_const; } if ( is_bool( $wp_debug_log_const ) ) { // WP_DEBUG_LOG is true or false. Log file is in default location of /wp-content/debug.log. if ( is_file( WP_CONTENT_DIR . '/debug.log' ) ) { // Copy existing debug log content to this plugin's debug log. $default_debug_log_content = file_get_contents( WP_CONTENT_DIR . '/debug.log' ); file_put_contents( $dlm_debug_log_file_path, $default_debug_log_content ); unlink( realpath( WP_CONTENT_DIR . '/debug.log' ) ); // delete existing debug log } } elseif ( is_string( $wp_debug_log_const ) ) { // WP_DEBUG_LOG is custom path to log file. Copy existing debug log content to this plugin's debug log. if ( is_file( $wp_debug_log_const ) && ( $wp_debug_log_const != $dlm_debug_log_file_path ) ) { $custom_debug_log_content = file_get_contents( $wp_debug_log_const ); file_put_contents( $dlm_debug_log_file_path, $custom_debug_log_content ); unlink( $wp_debug_log_const ); // delete existing debug log } } $copy = true; // existing debug.log file's entries are copied to this plugin's debug.log file } else { $copy = false; // existing debug.log file's entries are NOT copied to this plugin's debug.log file } // Prepare entries from the debug log for the data table $errors_master_list = json_decode( $this->get_processed_entries(), true ); $n = 1; $entries = array(); foreach ( $errors_master_list as $error ) { $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence $occurrence_count = count( $error['occurrences'] ); $entry = array( $n, $error['type'], $error['details'], $localized_timestamp . '
(' . $occurrence_count . ' occurrences logged)', ); $entries[] = $entry; $n++; } // Define Debug constants in wp-config.php $options = array( 'add' => true, // Add the config if missing. 'raw' => true, // Display value in raw format without quotes. 'normalize' => false, // Normalize config output using WP Coding Standards. ); $this->wp_config->update( 'constant', 'WP_DEBUG', 'true', $options ); $options = array( 'add' => true, // Add the config if missing. 'raw' => false, // Display value in raw format without quotes. 'normalize' => false, // Normalize config output using WP Coding Standards. ); $this->wp_config->update( 'constant', 'WP_DEBUG_LOG', get_option( 'debug_log_manager_file_path' ), $options ); $options = array( 'add' => true, // Add the config if missing. 'raw' => true, // Display value in raw format without quotes. 'normalize' => false, // Normalize config output using WP Coding Standards. ); $this->wp_config->update( 'constant', 'WP_DEBUG_DISPLAY', 'false', $options ); // Get the debug.log file size $log_file_path = get_option( 'debug_log_manager_file_path' ); $log_file_shortpath = str_replace( sanitize_text_field( $_SERVER['DOCUMENT_ROOT'] ), "", $log_file_path ); $file_size = size_format( (int) filesize( $log_file_path ) ); // Assemble data to return $data = array( 'status' => 'enabled', 'copy' => $copy, 'message' => 'Error Logging: Enabled on ' . esc_html( $date_time ), 'entries' => $entries, 'size' => $file_size, ); echo json_encode( $data ); } elseif ( 'enabled' == $log_info['status'] ) { $option_value = array( 'status' => 'disabled', 'on' => $date_time_for_option, ); update_option( 'debug_log_manager', $option_value, false ); // Remove Debug constants in wp-config.php $this->wp_config->remove( 'constant', 'WP_DEBUG' ); $this->wp_config->remove( 'constant', 'WP_DEBUG_LOG' ); $this->wp_config->remove( 'constant', 'WP_DEBUG_DISPLAY' ); // Assemble data to return $data = array( 'status' => 'disabled', 'copy' => false, 'message' => 'Error Logging: Disabled on ' . esc_html( $date_time ), 'entries' => '', 'size' => '', ); echo json_encode( $data ); } else {} } } /** * Toggle auto-refresh of entries table * * @since 1.3.0 */ public function toggle_autorefresh() { if ( isset( $_REQUEST ) ) { $autorefresh_status = get_option( 'debug_log_manager_autorefresh' ); if ( $autorefresh_status == 'disabled' ) { update_option( 'debug_log_manager_autorefresh', 'enabled', false ); $data = array( 'status' => 'enabled', 'message' => 'Auto-Refresh: Enabled', ); echo json_encode( $data ); } elseif ( $autorefresh_status == 'enabled' ) { update_option( 'debug_log_manager_autorefresh', 'disabled', false ); $data = array( 'status' => 'disabled', 'message' => 'Auto-Refresh: Disabled', ); echo json_encode( $data ); } else {} } } /** * Get the processed debug log data * * @return string $errors_master_list The processed error log entries * @since 1.2.0 */ public function get_processed_entries() { $debug_log_file_path = get_option( 'debug_log_manager_file_path' ); // Read the erros log file, reverse the order of the entries, prune to the latest 5000 entries $log = file_get_contents( $debug_log_file_path ); $log = str_replace( "[\\", "^\\", $log ); // certain error message contains the '[\' string, which will make the following split via explode() to split lines at places in the message it's not supposed to. So, we temporarily replace those with '^\' $log = str_replace( "[internal function]", "^internal function^", $log ); $lines = explode("[", $log); // Put back the missing '[' after explode operation $prepended_lines = array(); foreach ( $lines as $line ) { if ( !empty($line) ) { $line = str_replace( "UTC]", "UTC]@@@", $line ); // add '@@@' as marker/separator after time stamp $line = str_replace( "Stack trace:", "
Stack trace:", $line ); // add line break for stack trace section if ( strpos( $line, 'PHP Fatal' ) !== false ) { $line = str_replace( "#", "
#", $line ); // add line break on PHP Fatal error's stack trace lines } $line = str_replace( "Argument
#", "Argument #", $line ); // remove hr on certain error message $line = str_replace( "parameter
#", "parameter #", $line ); // remove hr on certain error message $line = str_replace( "the
#", "the #", $line ); // remove hr on certain error message $line = str_replace( "^\\", "[\\", $line ); // reverse the temporary replacement of '[\' with '^\' $line = str_replace( "^internal function^", "[internal function]", $line ); $prepended_line = '[' . $line; $prepended_lines[] = $prepended_line; } } $lines_newest_first = array_reverse( $prepended_lines ); $latest_lines = array_slice( $lines_newest_first, 0, 50000 ); // Will hold error details types $errors_master_list = array(); foreach( $latest_lines as $line ) { $line = explode("@@@ ", $line); // split the line using the '@@@' marker/separator defined earlier $timestamp = str_replace( [ "[", "]" ], "", $line[0] ); if ( array_key_exists('1', $line) ) { $error = $line[1]; } else { $error = 'No error message specified...'; } if ( strpos( $error, 'PHP Fatal' ) !== false ) { $error_type = 'PHP Fatal'; $error_details = str_replace( "PHP Fatal error: ", "", $error ); $error_details = str_replace( "PHP Fatal: ", "", $error_details ); } elseif ( strpos( $error, 'PHP Warning' ) !== false ) { $error_type = 'PHP Warning'; $error_details = str_replace( "PHP Warning: ", "", $error ); } elseif ( strpos( $error, 'PHP Notice' ) !== false ) { $error_type = 'PHP Notice'; $error_details = str_replace( "PHP Notice: ", "", $error ); } elseif ( strpos( $error, 'PHP Deprecated' ) !== false ) { $error_type = 'PHP Deprecated'; $error_details = str_replace( "PHP Deprecated: ", "", $error ); } elseif ( strpos( $error, 'PHP Parse' ) !== false ) { $error_type = 'PHP Parse'; $error_details = str_replace( "PHP Parse error: ", "", $error ); } elseif ( strpos( $error, 'WordPress database error' ) !== false ) { $error_type = 'Database'; $error_details = str_replace( "WordPress database error ", "", $error ); } elseif ( strpos( $error, 'JavaScript Error' ) !== false ) { $error_type = 'JavaScript'; $error_details = str_replace( "JavaScript Error: ", "", $error ); } else { $error_type = 'Other'; $error_details = $error; } // https://www.php.net/manual/en/function.array-search.php#120784 if ( array_search( trim( $error_details ), array_column( $errors_master_list, 'details' ) ) === false ) { $errors_master_list[] = array( 'type' => $error_type, 'details' => trim( $error_details ), 'occurrences' => array( $timestamp ), ); } else { $error_position = array_search( trim( $error_details ), array_column( $errors_master_list, 'details' ) ); // integer array_push( $errors_master_list[$error_position]['occurrences'], $timestamp ); } } return json_encode( $errors_master_list ); } /** * Get the latest entries for auto-refresh feature * * @since 1.0.0 */ public function get_latest_entries() { $errors_master_list = json_decode( $this->get_processed_entries(), true ); $n = 1; $entries = array(); foreach ( $errors_master_list as $error ) { $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence $occurrence_count = count( $error['occurrences'] ); $entry = array( $n, $error['type'], $error['details'], $localized_timestamp . '
(' . $occurrence_count . ' occurrences logged)', ); $entries[] = $entry; $n++; } $data = array( 'entries' => $entries, ); echo json_encode( $data ); } /** * Get debug log in data table format * * @since 1.0.0 */ public function get_entries_datatable() { ?>
get_processed_entries(), true ); $n = 1; foreach ( $errors_master_list as $error ) { $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence $occurrence_count = count( $error['occurrences'] ); ?>
# Error Type Details Last Occurrence

( occurrences logged)