📄 Viewing: class-debug-log.php

<?php

namespace DLM\Classes;

/**
 * Class related to the debug log file and entries
 *
 * @since 1.0.0
 */
class Debug_Log {

	// The wp_config object
	private $wp_config;

	/**
	 * Class constructor
	 */
	public function __construct() {

		$this->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() {

    	// If non-existent, create an empty index to prevent directory browsing and download of the debug log file
    	$uploads_path = wp_upload_dir()['basedir'] . '/debug-log-manager';
        if ( ! is_file( $uploads_path . '/index.php' ) ) {
	        file_put_contents( $uploads_path . '/index.php', '<?php // Nothing to show here' );    	
        }

		$value 		= get_option( 'debug_log_manager' );
		$status 	= $value['status'];

		if ( function_exists( 'wp_date' ) ) {
			$date_time 	= wp_date( 'M j, Y - H:i:s', strtotime( $value['on'] ) );
		} else {
			$date_time 	= date_i18n( 'M j, Y - H:i:s', strtotime( $value['on'] ) );
		}

		if ( 'enabled' == $status ) {

			return '<div id="debug-log-status" class="dlm-log-status"><strong>' . esc_html__( 'Error Logging', 'debug-log-manager' ) . '</strong>: ' . ucfirst( esc_html__( 'enabled', 'debug-log-manager' ) ) . ' ' . esc_html__( 'on', 'debug-log-manager' ) . ' ' . esc_html( $date_time ) . '</div>';

		} elseif ( 'disabled' == $status ) {

			return '<div id="debug-log-status" class="dlm-log-status"><strong>' . esc_html__( 'Error Logging', 'debug-log-manager' ) . '</strong>: ' . ucfirst( esc_html__( 'disabled', 'debug-log-manager' ) ) . ' ' . esc_html__( 'on', 'debug-log-manager' ) . ' ' . esc_html( $date_time ) . '</div>';

		} else {}

	}

	/**
	 * 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 );

		}

		if ( 'enabled' == $autorefresh_status ) {

			return '<div id="debug-autorefresh-status" class="dlm-autorefresh-status"><strong>Auto-Refresh</strong>: ' . ucfirst( esc_html__( 'enabled', 'debug-log-manager' ) ) . '</div>';

		} elseif ( 'disabled' == $autorefresh_status ) {

			return '<div id="debug-autorefresh-status" class="dlm-autorefresh-status"><strong>Auto-Refresh</strong>: ' . ucfirst( esc_html__( 'disabled', 'debug-log-manager' ) ) . '</div>';

		}

	}

	/**
	 * Enable / disable WP_DEBUG
	 *
	 * @since 1.0.0
	 */
	public function toggle_debugging() {

		if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {			
			if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
				
				$log_info 				= get_option( 'debug_log_manager' );
		        $dlm_debug_log_file_path 	= get_option( 'debug_log_manager_file_path' );

				if ( function_exists( 'wp_date' ) ) {
					$date_time 	= wp_date( 'M j, Y - H:i:s' ); // Localized according to WP timezone settings
				} else {
					$date_time 	= date_i18n( 'M j, Y - H:i:s' );

				}
				$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

					}

					// 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'       => true, // Display value in raw format without quotes.
						'normalize' => false, // Normalize config output using WP Coding Standards.
					);

					$this->wp_config->update( 'constant', 'SCRIPT_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 );

					$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', 'DISALLOW_FILE_EDIT', '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 ) );

					// 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 ) {

						if ( function_exists( 'wp_date' ) ) {
							$localized_timestamp 	= wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
						} else {
							$localized_timestamp 	= date_i18n( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) );
						}

						$occurrence_count 		= count( $error['occurrences'] );

						$entry = array( 
								$n, 
								$error['type'], 
								$error['details'], 
								$localized_timestamp . '<br /><span class="dlm-faint">(' . sprintf( _n( '%s occurrence logged', '%s occurrences logged', $occurrence_count, 'debug-log-manager' ), number_format_i18n( $occurrence_count ) ) . ')<span>',
						);

						$entries[] = $entry;

						$n++;

					}

					// Assemble data to return

					$data = array(
						'status'	=> 'enabled',
						'copy'		=> $copy,
						'message' 	=> '<strong>' . esc_html__( 'Error Logging', 'debug-log-manager' ) . '</strong>: ' . esc_html__( 'Enabled on', 'debug-log-manager' ) . ' ' . 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', 'SCRIPT_DEBUG' );
					$this->wp_config->remove( 'constant', 'WP_DEBUG_LOG' );
					$this->wp_config->remove( 'constant', 'WP_DEBUG_DISPLAY' );
					$this->wp_config->remove( 'constant', 'DISALLOW_FILE_EDIT' );

					// Assemble data to return

					$data = array(
						'status'	=> 'disabled',
						'copy'		=> false,
						'message' 	=> '<strong>' . esc_html__( 'Error Logging', 'debug-log-manager' ) . '</strong>: ' . esc_html__( 'Disabled on', 'debug-log-manager' ) . ' ' . 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 ) && current_user_can( 'manage_options' ) ) {			
			if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
				
				$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' 	=> '<strong>' . esc_html__( 'Auto-Refresh', 'debug-log-manager' ) . '</strong>: ' . esc_html__( 'Enabled', 'debug-log-manager' ),
					);

					echo json_encode( $data );

				} elseif ( $autorefresh_status == 'enabled' ) {

			        update_option( 'debug_log_manager_autorefresh', 'disabled', false );

					$data = array(
						'status'	=> 'disabled',
						'message' 	=> '<strong>' . esc_html__( 'Auto-Refresh', 'debug-log-manager' ) . '</strong>: ' . esc_html__( 'Disabled', 'debug-log-manager' ),
					);

					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 errors log file 
        $log 	= file_get_contents( $debug_log_file_path );
        
        // Ignore words between square brackets, 
        // e.g. [DEBUG], [INFO], [WARNING], [ERROR] may come after timestamp [29-Nov-2023 01:30:03 UTC]
        // This regex will extract DEBUG, INFO, WARNING, ERROR
        // Prevents log entry with two bracket sets e.g. [timestamp] [someinfo] Details....
        // from being returned as "No error message specified"
		$log = preg_replace("/\[([a-zA-Z\s\-]+)\]/", "$1", $log);

        $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( "[\"", "^\"", $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 );

        // We are splitting the log file not using PHP_EOL to preserve the stack traces for PHP Fatal Errors among other things
        $lines 	= explode("[", $log);
        $prepended_lines = array();

        // Pluck out the last 100k entries, the newest entry is last
        // To pluck out the first 100000 entries, use array_slice( $lines, 0, 100000 )
        $lines 	= array_slice( $lines, -100000 );

        foreach ( $lines as $line ) {
        	if ( !empty($line) ) {
        		$timezone_strings_to_replace = array(
        			'UTC]',
					'Abidjan]',
					'Accra]',
					'Addis_Ababa]',
					'Algiers]',
					'Asmara]',
					'Bamako]',
					'Bangui]',
					'Banjul]',
					'Bissau]',
					'Blantyre]',
					'Brazzaville]',
					'Bujumbura]',
					'Cairo]',
					'Casablanca]',
					'Ceuta]',
					'Conakry]',
					'Dakar]',
					'Dar_es_Salaam]',
					'Djibouti]',
					'Douala]',
					'El_Aaiun]',
					'Freetown]',
					'Gaborone]',
					'Harare]',
					'Johannesburg]',
					'Juba]',
					'Kampala]',
					'Khartoum]',
					'Kigali]',
					'Kinshasa]',
					'Lagos]',
					'Libreville]',
					'Lome]',
					'Luanda]',
					'Lubumbashi]',
					'Lusaka]',
					'Malabo]',
					'Maputo]',
					'Maseru]',
					'Mbabane]',
					'Mogadishu]',
					'Monrovia]',
					'Nairobi]',
					'Ndjamena]',
					'Niamey]',
					'Nouakchott]',
					'Ouagadougou]',
					'Porto-Novo]',
					'Sao_Tome]',
					'Tripoli]',
					'Tunis]',
					'Windhoek]',
					'Adak]',
					'Anchorage]',
					'Anguilla]',
					'Antigua]',
					'Araguaina]',
					'Argentina/Buenos_Aires]',
					'Argentina/Catamarca]',
					'Argentina/Cordoba]',
					'Argentina/Jujuy]',
					'Argentina/La_Rioja]',
					'Argentina/Mendoza]',
					'Argentina/Rio_Gallegos]',
					'Argentina/Salta]',
					'Argentina/San_Juan]',
					'Argentina/San_Luis]',
					'Argentina/Tucuman]',
					'Argentina/Ushuaia]',
					'Aruba]',
					'Asuncion]',
					'Atikokan]',
					'Bahia]',
					'Bahia_Banderas]',
					'Barbados]',
					'Belem]',
					'Belize]',
					'Blanc-Sablon]',
					'Boa_Vista]',
					'Bogota]',
					'Boise]',
					'Cambridge_Bay]',
					'Campo_Grande]',
					'Cancun]',
					'Caracas]',
					'Cayenne]',
					'Cayman]',
					'Chicago]',
					'Chihuahua]',
					'Ciudad_Juarez]',
					'Costa_Rica]',
					'Coyhaique]',
					'Creston]',
					'Cuiaba]',
					'Curacao]',
					'Danmarkshavn]',
					'Dawson]',
					'Dawson_Creek]',
					'Denver]',
					'Detroit]',
					'Dominica]',
					'Edmonton]',
					'Eirunepe]',
					'El_Salvador]',
					'Fort_Nelson]',
					'Fortaleza]',
					'Glace_Bay]',
					'Goose_Bay]',
					'Grand_Turk]',
					'Grenada]',
					'Guadeloupe]',
					'Guatemala]',
					'Guayaquil]',
					'Guyana]',
					'Halifax]',
					'Havana]',
					'Hermosillo]',
					'Indiana/Indianapolis]',
					'Indiana/Knox]',
					'Indiana/Marengo]',
					'Indiana/Petersburg]',
					'Indiana/Tell_City]',
					'Indiana/Vevay]',
					'Indiana/Vincennes]',
					'Indiana/Winamac]',
					'Inuvik]',
					'Iqaluit]',
					'Jamaica]',
					'Juneau]',
					'Kentucky/Louisville]',
					'Kentucky/Monticello]',
					'Kralendijk]',
					'La_Paz]',
					'Lima]',
					'Los_Angeles]',
					'Lower_Princes]',
					'Maceio]',
					'Managua]',
					'Manaus]',
					'Marigot]',
					'Martinique]',
					'Matamoros]',
					'Mazatlan]',
					'Menominee]',
					'Merida]',
					'Metlakatla]',
					'Mexico_City]',
					'Miquelon]',
					'Moncton]',
					'Monterrey]',
					'Montevideo]',
					'Montserrat]',
					'Nassau]',
					'New_York]',
					'Nome]',
					'Noronha]',
					'North_Dakota/Beulah]',
					'North_Dakota/Center]',
					'North_Dakota/New_Salem]',
					'Nuuk]',
					'Ojinaga]',
					'Panama]',
					'Paramaribo]',
					'Phoenix]',
					'Port-au-Prince]',
					'Port_of_Spain]',
					'Porto_Velho]',
					'Puerto_Rico]',
					'Punta_Arenas]',
					'Rankin_Inlet]',
					'Recife]',
					'Regina]',
					'Resolute]',
					'Rio_Branco]',
					'Santarem]',
					'Santiago]',
					'Santo_Domingo]',
					'Sao_Paulo]',
					'Scoresbysund]',
					'Sitka]',
					'St_Barthelemy]',
					'St_Johns]',
					'St_Kitts]',
					'St_Lucia]',
					'St_Thomas]',
					'St_Vincent]',
					'Swift_Current]',
					'Tegucigalpa]',
					'Thule]',
					'Tijuana]',
					'Toronto]',
					'Tortola]',
					'Vancouver]',
					'Whitehorse]',
					'Winnipeg]',
					'Yakutat]',
					'Casey]',
					'Davis]',
					'DumontDUrville]',
					'Macquarie]',
					'Mawson]',
					'McMurdo]',
					'Palmer]',
					'Rothera]',
					'Syowa]',
					'Troll]',
					'Vostok]',
					'Longyearbyen]',
					'Aden]',
					'Almaty]',
					'Amman]',
					'Anadyr]',
					'Aqtau]',
					'Aqtobe]',
					'Ashgabat]',
					'Atyrau]',
					'Baghdad]',
					'Bahrain]',
					'Baku]',
					'Bangkok]',
					'Barnaul]',
					'Beirut]',
					'Bishkek]',
					'Brunei]',
					'Chita]',
					'Colombo]',
					'Damascus]',
					'Dhaka]',
					'Dili]',
					'Dubai]',
					'Dushanbe]',
					'Famagusta]',
					'Gaza]',
					'Hebron]',
					'Ho_Chi_Minh]',
					'Hong_Kong]',
					'Hovd]',
					'Irkutsk]',
					'Jakarta]',
					'Jayapura]',
					'Jerusalem]',
					'Kabul]',
					'Kamchatka]',
					'Karachi]',
					'Kathmandu]',
					'Khandyga]',
					'Kolkata]',
					'Krasnoyarsk]',
					'Kuala_Lumpur]',
					'Kuching]',
					'Kuwait]',
					'Macau]',
					'Magadan]',
					'Makassar]',
					'Manila]',
					'Muscat]',
					'Nicosia]',
					'Novokuznetsk]',
					'Novosibirsk]',
					'Omsk]',
					'Oral]',
					'Phnom_Penh]',
					'Pontianak]',
					'Pyongyang]',
					'Qatar]',
					'Qostanay]',
					'Qyzylorda]',
					'Riyadh]',
					'Sakhalin]',
					'Samarkand]',
					'Seoul]',
					'Shanghai]',
					'Singapore]',
					'Srednekolymsk]',
					'Taipei]',
					'Tashkent]',
					'Tbilisi]',
					'Tehran]',
					'Thimphu]',
					'Tokyo]',
					'Tomsk]',
					'Ulaanbaatar]',
					'Urumqi]',
					'Ust-Nera]',
					'Vientiane]',
					'Vladivostok]',
					'Yakutsk]',
					'Yangon]',
					'Yekaterinburg]',
					'Yerevan]',
					'Atlantic/Azores]',
					'Atlantic/Bermuda]',
					'Atlantic/Canary]',
					'Atlantic/Cape_Verde]',
					'Atlantic/Faroe]',
					'Atlantic/Madeira]',
					'Atlantic/Reykjavik]',
					'Atlantic/South_Georgia]',
					'Atlantic/St_Helena]',
					'Atlantic/Stanley]',
					'Australia/Adelaide]',
					'Australia/Brisbane]',
					'Australia/Broken_Hill]',
					'Australia/Darwin]',
					'Australia/Eucla]',
					'Australia/Hobart]',
					'Australia/Lindeman]',
					'Australia/Lord_Howe]',
					'Australia/Melbourne]',
					'Australia/Perth]',
					'Australia/Sydney]',
					'Amsterdam]',
					'Andorra]',
					'Astrakhan]',
					'Athens]',
					'Belgrade]',
					'Berlin]',
					'Bratislava]',
					'Brussels]',
					'Bucharest]',
					'Budapest]',
					'Busingen]',
					'Chisinau]',
					'Copenhagen]',
					'Dublin]',
					'Gibraltar]',
					'Guernsey]',
					'Helsinki]',
					'Isle_of_Man]',
					'Istanbul]',
					'Jersey]',
					'Kaliningrad]',
					'Kirov]',
					'Kyiv]',
					'Lisbon]',
					'Ljubljana]',
					'London]',
					'Luxembourg]',
					'Madrid]',
					'Malta]',
					'Mariehamn]',
					'Minsk]',
					'Monaco]',
					'Moscow]',
					'Oslo]',
					'Paris]',
					'Podgorica]',
					'Prague]',
					'Riga]',
					'Rome]',
					'Samara]',
					'San_Marino]',
					'Sarajevo]',
					'Saratov]',
					'Simferopol]',
					'Skopje]',
					'Sofia]',
					'Stockholm]',
					'Tallinn]',
					'Tirane]',
					'Ulyanovsk]',
					'Vaduz]',
					'Vatican]',
					'Vienna]',
					'Vilnius]',
					'Volgograd]',
					'Warsaw]',
					'Zagreb]',
					'Zurich]',
					'Antananarivo]',
					'Chagos]',
					'Christmas]',
					'Cocos]',
					'Comoro]',
					'Kerguelen]',
					'Mahe]',
					'Maldives]',
					'Mauritius]',
					'Mayotte]',
					'Reunion]',
					'Apia]',
					'Auckland]',
					'Bougainville]',
					'Chatham]',
					'Chuuk]',
					'Easter]',
					'Efate]',
					'Fakaofo]',
					'Fiji]',
					'Funafuti]',
					'Galapagos]',
					'Gambier]',
					'Guadalcanal]',
					'Guam]',
					'Honolulu]',
					'Kanton]',
					'Kiritimati]',
					'Kosrae]',
					'Kwajalein]',
					'Majuro]',
					'Marquesas]',
					'Midway]',
					'Nauru]',
					'Niue]',
					'Norfolk]',
					'Noumea]',
					'Pago_Pago]',
					'Palau]',
					'Pitcairn]',
					'Pohnpei]',
					'Port_Moresby]',
					'Rarotonga]',
					'Saipan]',
					'Tahiti]',
					'Tarawa]',
					'Tongatapu]',
					'Wake]',
					'Wallis]',
        		);
        		$timezone_replacement_strings = array(
        			'UTC]@@@',
					'Abidjan]@@@',
					'Accra]@@@',
					'Addis_Ababa]@@@',
					'Algiers]@@@',
					'Asmara]@@@',
					'Bamako]@@@',
					'Bangui]@@@',
					'Banjul]@@@',
					'Bissau]@@@',
					'Blantyre]@@@',
					'Brazzaville]@@@',
					'Bujumbura]@@@',
					'Cairo]@@@',
					'Casablanca]@@@',
					'Ceuta]@@@',
					'Conakry]@@@',
					'Dakar]@@@',
					'Dar_es_Salaam]@@@',
					'Djibouti]@@@',
					'Douala]@@@',
					'El_Aaiun]@@@',
					'Freetown]@@@',
					'Gaborone]@@@',
					'Harare]@@@',
					'Johannesburg]@@@',
					'Juba]@@@',
					'Kampala]@@@',
					'Khartoum]@@@',
					'Kigali]@@@',
					'Kinshasa]@@@',
					'Lagos]@@@',
					'Libreville]@@@',
					'Lome]@@@',
					'Luanda]@@@',
					'Lubumbashi]@@@',
					'Lusaka]@@@',
					'Malabo]@@@',
					'Maputo]@@@',
					'Maseru]@@@',
					'Mbabane]@@@',
					'Mogadishu]@@@',
					'Monrovia]@@@',
					'Nairobi]@@@',
					'Ndjamena]@@@',
					'Niamey]@@@',
					'Nouakchott]@@@',
					'Ouagadougou]@@@',
					'Porto-Novo]@@@',
					'Sao_Tome]@@@',
					'Tripoli]@@@',
					'Tunis]@@@',
					'Windhoek]@@@',
					'Adak]@@@',
					'Anchorage]@@@',
					'Anguilla]@@@',
					'Antigua]@@@',
					'Araguaina]@@@',
					'Argentina/Buenos_Aires]@@@',
					'Argentina/Catamarca]@@@',
					'Argentina/Cordoba]@@@',
					'Argentina/Jujuy]@@@',
					'Argentina/La_Rioja]@@@',
					'Argentina/Mendoza]@@@',
					'Argentina/Rio_Gallegos]@@@',
					'Argentina/Salta]@@@',
					'Argentina/San_Juan]@@@',
					'Argentina/San_Luis]@@@',
					'Argentina/Tucuman]@@@',
					'Argentina/Ushuaia]@@@',
					'Aruba]@@@',
					'Asuncion]@@@',
					'Atikokan]@@@',
					'Bahia]@@@',
					'Bahia_Banderas]@@@',
					'Barbados]@@@',
					'Belem]@@@',
					'Belize]@@@',
					'Blanc-Sablon]@@@',
					'Boa_Vista]@@@',
					'Bogota]@@@',
					'Boise]@@@',
					'Cambridge_Bay]@@@',
					'Campo_Grande]@@@',
					'Cancun]@@@',
					'Caracas]@@@',
					'Cayenne]@@@',
					'Cayman]@@@',
					'Chicago]@@@',
					'Chihuahua]@@@',
					'Ciudad_Juarez]@@@',
					'Costa_Rica]@@@',
					'Coyhaique]@@@',
					'Creston]@@@',
					'Cuiaba]@@@',
					'Curacao]@@@',
					'Danmarkshavn]@@@',
					'Dawson]@@@',
					'Dawson_Creek]@@@',
					'Denver]@@@',
					'Detroit]@@@',
					'Dominica]@@@',
					'Edmonton]@@@',
					'Eirunepe]@@@',
					'El_Salvador]@@@',
					'Fort_Nelson]@@@',
					'Fortaleza]@@@',
					'Glace_Bay]@@@',
					'Goose_Bay]@@@',
					'Grand_Turk]@@@',
					'Grenada]@@@',
					'Guadeloupe]@@@',
					'Guatemala]@@@',
					'Guayaquil]@@@',
					'Guyana]@@@',
					'Halifax]@@@',
					'Havana]@@@',
					'Hermosillo]@@@',
					'Indiana/Indianapolis]@@@',
					'Indiana/Knox]@@@',
					'Indiana/Marengo]@@@',
					'Indiana/Petersburg]@@@',
					'Indiana/Tell_City]@@@',
					'Indiana/Vevay]@@@',
					'Indiana/Vincennes]@@@',
					'Indiana/Winamac]@@@',
					'Inuvik]@@@',
					'Iqaluit]@@@',
					'Jamaica]@@@',
					'Juneau]@@@',
					'Kentucky/Louisville]@@@',
					'Kentucky/Monticello]@@@',
					'Kralendijk]@@@',
					'La_Paz]@@@',
					'Lima]@@@',
					'Los_Angeles]@@@',
					'Lower_Princes]@@@',
					'Maceio]@@@',
					'Managua]@@@',
					'Manaus]@@@',
					'Marigot]@@@',
					'Martinique]@@@',
					'Matamoros]@@@',
					'Mazatlan]@@@',
					'Menominee]@@@',
					'Merida]@@@',
					'Metlakatla]@@@',
					'Mexico_City]@@@',
					'Miquelon]@@@',
					'Moncton]@@@',
					'Monterrey]@@@',
					'Montevideo]@@@',
					'Montserrat]@@@',
					'Nassau]@@@',
					'New_York]@@@',
					'Nome]@@@',
					'Noronha]@@@',
					'North_Dakota/Beulah]@@@',
					'North_Dakota/Center]@@@',
					'North_Dakota/New_Salem]@@@',
					'Nuuk]@@@',
					'Ojinaga]@@@',
					'Panama]@@@',
					'Paramaribo]@@@',
					'Phoenix]@@@',
					'Port-au-Prince]@@@',
					'Port_of_Spain]@@@',
					'Porto_Velho]@@@',
					'Puerto_Rico]@@@',
					'Punta_Arenas]@@@',
					'Rankin_Inlet]@@@',
					'Recife]@@@',
					'Regina]@@@',
					'Resolute]@@@',
					'Rio_Branco]@@@',
					'Santarem]@@@',
					'Santiago]@@@',
					'Santo_Domingo]@@@',
					'Sao_Paulo]@@@',
					'Scoresbysund]@@@',
					'Sitka]@@@',
					'St_Barthelemy]@@@',
					'St_Johns]@@@',
					'St_Kitts]@@@',
					'St_Lucia]@@@',
					'St_Thomas]@@@',
					'St_Vincent]@@@',
					'Swift_Current]@@@',
					'Tegucigalpa]@@@',
					'Thule]@@@',
					'Tijuana]@@@',
					'Toronto]@@@',
					'Tortola]@@@',
					'Vancouver]@@@',
					'Whitehorse]@@@',
					'Winnipeg]@@@',
					'Yakutat]@@@',
					'Casey]@@@',
					'Davis]@@@',
					'DumontDUrville]@@@',
					'Macquarie]@@@',
					'Mawson]@@@',
					'McMurdo]@@@',
					'Palmer]@@@',
					'Rothera]@@@',
					'Syowa]@@@',
					'Troll]@@@',
					'Vostok]@@@',
					'Longyearbyen]@@@',
					'Aden]@@@',
					'Almaty]@@@',
					'Amman]@@@',
					'Anadyr]@@@',
					'Aqtau]@@@',
					'Aqtobe]@@@',
					'Ashgabat]@@@',
					'Atyrau]@@@',
					'Baghdad]@@@',
					'Bahrain]@@@',
					'Baku]@@@',
					'Bangkok]@@@',
					'Barnaul]@@@',
					'Beirut]@@@',
					'Bishkek]@@@',
					'Brunei]@@@',
					'Chita]@@@',
					'Colombo]@@@',
					'Damascus]@@@',
					'Dhaka]@@@',
					'Dili]@@@',
					'Dubai]@@@',
					'Dushanbe]@@@',
					'Famagusta]@@@',
					'Gaza]@@@',
					'Hebron]@@@',
					'Ho_Chi_Minh]@@@',
					'Hong_Kong]@@@',
					'Hovd]@@@',
					'Irkutsk]@@@',
					'Jakarta]@@@',
					'Jayapura]@@@',
					'Jerusalem]@@@',
					'Kabul]@@@',
					'Kamchatka]@@@',
					'Karachi]@@@',
					'Kathmandu]@@@',
					'Khandyga]@@@',
					'Kolkata]@@@',
					'Krasnoyarsk]@@@',
					'Kuala_Lumpur]@@@',
					'Kuching]@@@',
					'Kuwait]@@@',
					'Macau]@@@',
					'Magadan]@@@',
					'Makassar]@@@',
					'Manila]@@@',
					'Muscat]@@@',
					'Nicosia]@@@',
					'Novokuznetsk]@@@',
					'Novosibirsk]@@@',
					'Omsk]@@@',
					'Oral]@@@',
					'Phnom_Penh]@@@',
					'Pontianak]@@@',
					'Pyongyang]@@@',
					'Qatar]@@@',
					'Qostanay]@@@',
					'Qyzylorda]@@@',
					'Riyadh]@@@',
					'Sakhalin]@@@',
					'Samarkand]@@@',
					'Seoul]@@@',
					'Shanghai]@@@',
					'Singapore]@@@',
					'Srednekolymsk]@@@',
					'Taipei]@@@',
					'Tashkent]@@@',
					'Tbilisi]@@@',
					'Tehran]@@@',
					'Thimphu]@@@',
					'Tokyo]@@@',
					'Tomsk]@@@',
					'Ulaanbaatar]@@@',
					'Urumqi]@@@',
					'Ust-Nera]@@@',
					'Vientiane]@@@',
					'Vladivostok]@@@',
					'Yakutsk]@@@',
					'Yangon]@@@',
					'Yekaterinburg]@@@',
					'Yerevan]@@@',
					'Atlantic/Azores]@@@',
					'Atlantic/Bermuda]@@@',
					'Atlantic/Canary]@@@',
					'Atlantic/Cape_Verde]@@@',
					'Atlantic/Faroe]@@@',
					'Atlantic/Madeira]@@@',
					'Atlantic/Reykjavik]@@@',
					'Atlantic/South_Georgia]@@@',
					'Atlantic/St_Helena]@@@',
					'Atlantic/Stanley]@@@',
					'Australia/Adelaide]@@@',
					'Australia/Brisbane]@@@',
					'Australia/Broken_Hill]@@@',
					'Australia/Darwin]@@@',
					'Australia/Eucla]@@@',
					'Australia/Hobart]@@@',
					'Australia/Lindeman]@@@',
					'Australia/Lord_Howe]@@@',
					'Australia/Melbourne]@@@',
					'Australia/Perth]@@@',
					'Australia/Sydney]@@@',
					'Amsterdam]@@@',
					'Andorra]@@@',
					'Astrakhan]@@@',
					'Athens]@@@',
					'Belgrade]@@@',
					'Berlin]@@@',
					'Bratislava]@@@',
					'Brussels]@@@',
					'Bucharest]@@@',
					'Budapest]@@@',
					'Busingen]@@@',
					'Chisinau]@@@',
					'Copenhagen]@@@',
					'Dublin]@@@',
					'Gibraltar]@@@',
					'Guernsey]@@@',
					'Helsinki]@@@',
					'Isle_of_Man]@@@',
					'Istanbul]@@@',
					'Jersey]@@@',
					'Kaliningrad]@@@',
					'Kirov]@@@',
					'Kyiv]@@@',
					'Lisbon]@@@',
					'Ljubljana]@@@',
					'London]@@@',
					'Luxembourg]@@@',
					'Madrid]@@@',
					'Malta]@@@',
					'Mariehamn]@@@',
					'Minsk]@@@',
					'Monaco]@@@',
					'Moscow]@@@',
					'Oslo]@@@',
					'Paris]@@@',
					'Podgorica]@@@',
					'Prague]@@@',
					'Riga]@@@',
					'Rome]@@@',
					'Samara]@@@',
					'San_Marino]@@@',
					'Sarajevo]@@@',
					'Saratov]@@@',
					'Simferopol]@@@',
					'Skopje]@@@',
					'Sofia]@@@',
					'Stockholm]@@@',
					'Tallinn]@@@',
					'Tirane]@@@',
					'Ulyanovsk]@@@',
					'Vaduz]@@@',
					'Vatican]@@@',
					'Vienna]@@@',
					'Vilnius]@@@',
					'Volgograd]@@@',
					'Warsaw]@@@',
					'Zagreb]@@@',
					'Zurich]@@@',
					'Antananarivo]@@@',
					'Chagos]@@@',
					'Christmas]@@@',
					'Cocos]@@@',
					'Comoro]@@@',
					'Kerguelen]@@@',
					'Mahe]@@@',
					'Maldives]@@@',
					'Mauritius]@@@',
					'Mayotte]@@@',
					'Reunion]@@@',
					'Apia]@@@',
					'Auckland]@@@',
					'Bougainville]@@@',
					'Chatham]@@@',
					'Chuuk]@@@',
					'Easter]@@@',
					'Efate]@@@',
					'Fakaofo]@@@',
					'Fiji]@@@',
					'Funafuti]@@@',
					'Galapagos]@@@',
					'Gambier]@@@',
					'Guadalcanal]@@@',
					'Guam]@@@',
					'Honolulu]@@@',
					'Kanton]@@@',
					'Kiritimati]@@@',
					'Kosrae]@@@',
					'Kwajalein]@@@',
					'Majuro]@@@',
					'Marquesas]@@@',
					'Midway]@@@',
					'Nauru]@@@',
					'Niue]@@@',
					'Norfolk]@@@',
					'Noumea]@@@',
					'Pago_Pago]@@@',
					'Palau]@@@',
					'Pitcairn]@@@',
					'Pohnpei]@@@',
					'Port_Moresby]@@@',
					'Rarotonga]@@@',
					'Saipan]@@@',
					'Tahiti]@@@',
					'Tarawa]@@@',
					'Tongatapu]@@@',
					'Wake]@@@',
					'Wallis]@@@',
        		);
        		$line 			= str_replace( $timezone_strings_to_replace, $timezone_replacement_strings, $line ); // add '@@@' as marker/separator after time stamp
        		$line 			= str_replace( "Stack trace:", "<hr />Stack trace:", $line ); // add line break for stack trace section
				if ( strpos( $line, 'PHP Fatal' ) !== false ) {
	        		$line 		= str_replace( "#", "<hr />#", $line ); // add line break on PHP Fatal error's stack trace lines
	        	}
        		$line 			= str_replace( "Argument <hr />#", "Argument #", $line ); // remove hr on certain error message
        		$line 			= str_replace( "parameter <hr />#", "parameter #", $line ); // remove hr on certain error message
        		$line 			= str_replace( "the <hr />#", "the #", $line ); // remove hr on certain error message
        		$line 			= str_replace( "^\\", "[\\", $line ); // reverse the temporary replacement of '[\' with '^\'
        		$line 			= str_replace( "^\"", "[\"", $line ); // reverse the temporary replacement of '["' with '^"'
        		$line 			= str_replace( "^internal function^", "[internal function]", $line );
	        	$prepended_line 	= '[' . $line; // Put back the missing '[' after explode operation
	        	$prepended_lines[] 	= $prepended_line;
        	}
        }

        // Reverse the order of the entries, so the newest entry is first
        $latest_lines 	= array_reverse( $prepended_lines );

        // Will hold error details types
        $errors_master_list = array();

		foreach( $latest_lines as $line ) {
			$line = wp_kses_post( $line );

			$line = explode("@@@ ", trim( $line ) ); // split the line using the '@@@' marker/separator defined earlier. '@@@' will be deleted by explode().

			$timestamp = str_replace( [ "[", "]" ], "", $line[0] );

			// Initialize error-related variables
			$error = '';
			$error_source = '';	
			$error_file = '';
			$error_file_path = '';
			$error_file_line = '';

			if ( array_key_exists('1', $line) ) {
				$error = $line[1];

				// Check if there is a file path to pluck out of the error line
				if ( false !== strpos( $error, ABSPATH ) ) {

					// Separata file path and error line from error message

					// Handling for PHP Fatal errors with stack trace included
					if ( false !== strpos( $error, 'Stack trace:' ) ) {
						$error_parts = explode( 'Stack trace:', $error );
						$error_message = str_replace( '<hr />', '', $error_parts[0] );
						if ( isset( $error_parts[1] ) ) {
							$error_stack_trace = ' ' . $error_parts[1];
						}

						$error_message_parts = explode( ' in /', $error_message );

						// Reconstruct the error details without error file path and line info
						$error = $error_message_parts[0] . '<hr />Stack trace:' . $error_stack_trace;
						// Shorten the file path in the error details
						$error = str_replace( ABSPATH, '/', $error );
						if ( isset( $error_message_parts[1] ) ) {
							$error_file = '/' . $error_message_parts[1];
							$error_file_info = explode ( ':', $error_file );
							$error_file_path = $error_file_info[0];
							if ( array_key_exists('1', $error_file_info) ) {
								$error_file_line = $error_file_info[1];
							}
						}
					} else {
						$error_message_parts = explode( ' in /', $error );

						$error = $error_message_parts[0];
						if ( isset( $error_message_parts[1] ) ) {
							$error_file = '/' . $error_message_parts[1];

							$error_file_info = explode ( ' on line ', $error_file );
							$error_file_path = $error_file_info[0];
							if ( array_key_exists('1', $error_file_info) ) {
								$error_file_line = $error_file_info[1];
							}
						}
					}

					// Shorten the file path where the error occurred
					$error_file_path = str_replace( ABSPATH, '/', $error_file_path );

					// Define whether source of error is WP Core, Theme, Plugin or Other

					if ( ( false !== strpos( $error_file, '/wp-admin/' ) ) || 
						   ( false !== strpos( $error_file, '/wp-includes/' ) ) ) {
						$error_source = __( 'WordPress core', 'debug-log-manager' );
					} elseif ( ( false !== strpos( $error_file, '/wp-content/themes/' ) ) ) {
						$error_source = __( 'Theme', 'debug-log-manager' );
					} elseif ( ( false !== strpos( $error_file, '/wp-content/plugins/' ) ) ) {
						$error_source = __( 'Plugin', 'debug-log-manager' );
					} else {
						$error_source = '';	
					}

					// Get plugin/theme directory name of error file when error source is plugin or theme

					if ( ( 'Plugin' == $error_source ) || ( 'Theme' == $error_source ) ) {
						$error_file_path_parts = explode( '/', $error_file_path );
						$error_file_directory = $error_file_path_parts[3];
					}

					// Get plugin name

					$plugins = get_plugins();

					if ( 'Plugin' == $error_source ) {
						foreach ( $plugins as $plugin_path_file => $plugin_info ) {
							if ( false !== strpos( $plugin_path_file, $error_file_directory ) ) {
								$error_source_plugin_path_file = $plugin_path_file;
								$error_source_plugin_name = $plugin_info['Name'];
								$error_source_plugin_uri = $plugin_info['PluginURI'];
								// $error_source_plugin_version = $plugin_info['Version'];
							}
						}
					}

					// Get theme name

					if ( 'Theme' == $error_source ) {
						$theme = wp_get_theme( $error_file_directory );
						if ( $theme->exists() ) {
							$error_source_theme_dir = $error_file_directory;
							$error_source_theme_name = $theme->get( 'Name' );
							$error_source_theme_uri = $theme->get( 'ThemeURI' );
							// $error_source_theme_version = $theme->get( 'Version' );
						} else {
							$error_source_theme_name = $error_file_directory;
						}
					}

				}

			} else {

				$error = __( 'No error message specified...', 'debug-log-manager' );
	
			}
			
			if ( ( false !== strpos( $error, 'PHP Fatal' )) || ( false !== strpos( $error, 'FATAL' ) ) || ( false !== strpos( $error, 'E_ERROR' ) ) ) {
				$error_type 	= __( 'PHP Fatal', 'debug-log-manager' );
				$error_details 	= str_replace( "PHP Fatal error: ", "", $error );
				$error_details 	= str_replace( "PHP Fatal: ", "", $error_details );
				$error_details 	= str_replace( "FATAL ", "", $error_details );
				$error_details 	= str_replace( "E_ERROR: ", "", $error_details );
			} elseif ( ( false !== strpos( $error, 'PHP Warning' ) ) || (  false !== strpos( $error, 'E_WARNING' ) ) ) {
				$error_type 	= __( 'PHP Warning', 'debug-log-manager' );
				$error_details 	= str_replace( "PHP Warning: ", "", $error );
				$error_details 	= str_replace( "E_WARNING: ", "", $error_details );
			} elseif ( ( false !== strpos( $error, 'PHP Notice' ) ) || ( false !== strpos( $error, 'E_NOTICE' ) ) ) {
				$error_type 	= __( 'PHP Notice', 'debug-log-manager' );
				$error_details 	= str_replace( "PHP Notice: ", "", $error );
				$error_details 	= str_replace( "E_NOTICE: ", "", $error_details );
			} elseif ( false !== strpos( $error, 'PHP Deprecated' ) ) {
				$error_type 	= __( 'PHP Deprecated', 'debug-log-manager' );
				$error_details 	= str_replace( "PHP Deprecated: ", "", $error );
			} elseif ( ( false !== strpos( $error, 'PHP Parse' ) ) || ( false !== strpos( $error, 'E_PARSE' ) ) ) {
				$error_type 	= __( 'PHP Parse', 'debug-log-manager' );
				$error_details 	= str_replace( "PHP Parse error: ", "", $error );
				$error_details 	= str_replace( "E_PARSE: ", "", $error_details );
			} elseif ( false !== strpos( $error, 'EXCEPTION:' ) ) {
				$error_type 	= __( 'PHP Exception', 'debug-log-manager' );
				$error_details 	= str_replace( "EXCEPTION: ", "", $error );
			} elseif ( false !== strpos( $error, 'WordPress database error' ) ) {
				$error_type 	= __( 'Database', 'debug-log-manager' );
				$error_details 	= str_replace( "WordPress database error ", "", $error );
			} elseif ( false !== strpos( $error, 'JavaScript Error' ) ) {
				$error_type 	= __( 'JavaScript', 'debug-log-manager' );
				$error_details 	= str_replace( "JavaScript Error: ", "", $error );
			} else {
				$error_type 	= __( 'Other', 'debug-log-manager' );
				$error_details 	= $error;
				if ( $this->is_json( $error_details ) ) {
					// For JSON string in error message, originally added via error_log( json_encode( $variable ) )
					// This will output said JSON string as well-formated array in the log entries table
					$error_details = '<pre>' . print_r( json_decode( $error_details, true ), true ) . '</pre>';
				}
			}

			// Append error source, file path and line number info to error details. If core plugin/theme editor is not disabled, link file path to the editor view.

			if ( ! empty( $error_source ) ) {
				if ( 'WordPress core' == $error_source ) {
					$wp_version = get_bloginfo( 'version' );
					$file_viewer_url = 'https://github.com/WordPress/wordpress-develop/blob/' . $wp_version . '/src' . $error_file_path;
					$error_details = '<span class="error-details">' . $error_details . '</span><hr />' . $error_source . '<br />' . __( 'File', 'debug-log-manager' ) . ': <a href="' . $file_viewer_url . '" target="_blank" class="error-source-link">' . $error_file_path . '<span class="dashicons dashicons-visibility offset-down"></span></a><br />' . __( 'Line', 'debug-log-manager' ) . ': ' . $error_file_line;
				} elseif ( 'Theme' == $error_source ) {
					if ( ! defined( 'DISALLOW_FILE_EDIT' ) || ( false === constant( 'DISALLOW_FILE_EDIT' ) ) ) {
						$file_viewer_url = get_admin_url() . 'theme-editor.php?file=' . urlencode( str_replace( '/wp-content/themes/', '', $error_file_path ) ) . '&theme=' . $error_source_theme_dir;
						$error_details = '<span class="error-details">' . $error_details . '</span><hr />' . $error_source . ': <a href="' . $error_source_theme_uri . '" target="_blank" class="error-source-link">' . $error_source_theme_name . '<span class="dashicons dashicons-external offset-up"></span></a><br />' . __( 'File', 'debug-log-manager' ) . ': <a href="' . $file_viewer_url . '" target="_blank" class="error-source-link">' . $error_file_path . '<span class="dashicons dashicons-visibility offset-down"></span></a><br />' . __( 'Line', 'debug-log-manager' ) . ': ' . $error_file_line;
					} 
					if ( defined( 'DISALLOW_FILE_EDIT' ) && ( true === constant( 'DISALLOW_FILE_EDIT' ) ) ) {
						$error_details = '<span class="error-details">' . $error_details . '</span><hr />' . $error_source . ': <a href="' . $error_source_theme_uri . '" target="_blank" class="error-source-link">' . $error_source_theme_name . '<span class="dashicons dashicons-external offset-up"></span></a><br />' . __( 'File', 'debug-log-manager' ) . ': ' . $error_file_path . '<br />' . __( 'Line', 'debug-log-manager' ) . ': ' . $error_file_line;
					}
				} elseif ( 'Plugin' == $error_source ) {
					if ( ! defined( 'DISALLOW_FILE_EDIT' ) || ( false === constant( 'DISALLOW_FILE_EDIT' ) ) ) {
						$file_viewer_url = get_admin_url() . 'plugin-editor.php?file=' . urlencode( str_replace( '/wp-content/plugins/', '', $error_file_path ) ) . '&plugin=' . urlencode( $error_source_plugin_path_file );
						$error_details = '<span class="error-details">' . $error_details . '</span><hr />' . $error_source . ': <a href="' . $error_source_plugin_uri . '" target="_blank" class="error-source-link">' . $error_source_plugin_name . '<span class="dashicons dashicons-external offset-up"></span></a><br />' . __( 'File', 'debug-log-manager' ) . ': <a href="' . $file_viewer_url . '" target="_blank" class="error-source-link">' . $error_file_path . '<span class="dashicons dashicons-visibility offset-down"></span></a><br />' . __( 'Line', 'debug-log-manager' ) . ': ' . $error_file_line;
					} 
					if ( defined( 'DISALLOW_FILE_EDIT' ) && ( true === constant( 'DISALLOW_FILE_EDIT' ) ) ) {
						$error_details = '<span class="error-details">' . $error_details . '</span><hr />' . $error_source . ': <a href="' . $error_source_plugin_uri . '" target="_blank" class="error-source-link">' . $error_source_plugin_name . '<span class="dashicons dashicons-external offset-up"></span></a><br />' . __( 'File', 'debug-log-manager' ) . ': ' . $error_file_path . '<br />' . __( 'Line', 'debug-log-manager' ) . ': ' . $error_file_line;
					}
				}
			}

			// 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() {

		if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {

			if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
				
				$errors_master_list = json_decode( $this->get_processed_entries(), true );

				$n = 1;
				$entries = array();

				foreach ( $errors_master_list as $error ) {

					if ( function_exists( 'wp_date' ) ) {
						$localized_timestamp 	= wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
					} else {
						$localized_timestamp 	= date_i18n( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) );
					}

					$occurrence_count 		= count( $error['occurrences'] );

					$entry = array( 
							$n, 
							$error['type'], 
							$error['details'], 
							$localized_timestamp . '<br /><span class="dlm-faint">(' . sprintf( _n( '%s occurrence logged', '%s occurrences logged', $occurrence_count, 'debug-log-manager' ), number_format_i18n( $occurrence_count ) ) . ')<span>',
					);

					$entries[] = $entry;

					$n++;

				}

				$data = array(
					'entries'	=> $entries,
				);

			}

		} else {
			
			$data = array();
			
		}

		echo json_encode( $data );

	}

	/**
	 * Get debug log in data table format
	 *
	 * @since 1.0.0
	 */
	public function get_entries_datatable() {

		?>
		<div>
			<select id="errorTypeFilter" class="dlm-error-type-filter">
				<option value=""><?php esc_html_e( 'All Error Types', 'debug-log-manager' ); ?></option>
				<option value="<?php esc_attr_e( 'PHP Fatal', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Fatal', 'debug-log-manager' ); ?></option>
				<option value="<?php esc_attr_e( 'PHP Warning', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Warning', 'debug-log-manager' ); ?></option>
				<option value="<?php esc_attr_e( 'PHP Notice', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Notice', 'debug-log-manager' ); ?></option>
				<option value="<?php esc_attr_e( 'PHP Deprecated', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Deprecated', 'debug-log-manager' ); ?></option>
				<option value="<?php esc_attr_e( 'PHP Parse', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Parse', 'debug-log-manager' ); ?></option>
				<option value="<?php esc_attr_e( 'PHP Exception', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Exception', 'debug-log-manager' ); ?></option>
				<option value="<?php esc_attr_e( 'Database', 'debug-log-manager' ); ?>"><?php esc_html_e( 'Database', 'debug-log-manager' ); ?></option>
				<option value="<?php esc_attr_e( 'JavaScript', 'debug-log-manager' ); ?>"><?php esc_html_e( 'JavaScript', 'debug-log-manager' ); ?></option>
				<option value="<?php esc_attr_e( 'Other', 'debug-log-manager' ); ?>"><?php esc_html_e( 'Other', 'debug-log-manager' ); ?></option>				
			</select>
		</div>
		<table id="debug-log" class="wp-list-table widefat striped">
			<thead>
				<tr>
					<th class="dlm-entry-no">#</th>
					<th class="dlm-entry-type"><?php esc_html_e( 'Error Type', 'debug-log-manager' ); ?></th>
					<th class="dlm-entry-details"><?php esc_html_e( 'Details', 'debug-log-manager' ); ?></th>
					<th class="dlm-entry-datetime"><?php esc_html_e( 'Last Occurrence', 'debug-log-manager' ); ?></th>
				</tr>
			</thead>
			<tbody>
		<?php

		$errors_master_list = json_decode( $this->get_processed_entries(), true );

		$n = 1;

		foreach ( $errors_master_list as $error ) {

			if ( function_exists( 'wp_date' ) ) {
				$localized_timestamp 	= wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
			} else {
				$localized_timestamp 	= date_i18n( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) );
			}

			$occurrence_count 		= count( $error['occurrences'] );
			?>

			<tr>
				<td class="dlm-entry-no"><?php echo esc_html( $n ); ?></td>
				<td class="dlm-entry-type"><?php echo esc_html( $error['type'] ); ?></td>
				<td class="dlm-entry-details"><?php echo wp_kses( $error['details'], 'post' ); ?></td>
				<td class="dlm-entry-datetime"><?php echo esc_html( $localized_timestamp ); ?><br /><span class="dlm-faint">(<?php printf( esc_html( _n( '%s occurrence logged', '%s occurrences logged', $occurrence_count, 'debug-log-manager' ) ), esc_html( number_format_i18n( $occurrence_count ) ) ); ?>)<span></td>
			</tr>

			<?php
			$n++;

		}

		?>
			</tbody>
		</table>
		<?php

	}

	/**
	 * Get entries for dashboard widget
	 *
	 * @since 1.8.0
	 */
	public function get_dashboard_widget_entries() {

		$errors_master_list = json_decode( $this->get_processed_entries(), true );

		$n = 1;
		$entries_to_show = 5;

		?>
		<style>

			#debug_log_manager_widget.postbox .inside {
				margin: 0;
				padding: 0;
			}

			.dlm-dashboard-widget-entry {
				padding: 12px;
				border-bottom: 1px solid #e6e7e7;
			    word-wrap:  break-word; /* All browsers since IE 5.5+ */
			    overflow-wrap: break-word; /* Renamed property in CSS3 draft spec */
			}

			.dlm-dashboard-widget-entry:nth-child(odd) {
				background-color: #f6f7f7;
			}

			.dlm-dashboard-widget-entry-message a.error-source-link {
			    color: #50575e;
			    text-decoration: none;
			}

			.dlm-dashboard-widget-entry-message a.error-source-link span {
			    position: relative;
			    margin-left: 2px;
			    color: #777;
			    font-size: 18px;
			    width: 18px;
			    height: 18px;
			    transition: .25s;
			    text-decoration: none;
			}

			.dlm-dashboard-widget-entry-message a.error-source-link span.offset-up {
			    top: -1px;
			}

			.dlm-dashboard-widget-entry-message a.error-source-link span.offset-down {
			    top: 1px;
			}

			.dlm-dashboard-widget-entry-message a.error-source-link:hover {
			    color: #2271b1;
			    text-decoration: underline;
			}

			#debug-log .dlm-entry-details a.error-source-link:hover span {
			    color: #2271b1;
			    text-decoration: none;
			}

			.dlm-dashboard-widget-entry-meta {
				display: flex;
			}

			.dlm-dashboard-widget-entry-type {
				margin-right: 4px;
				font-weight: 600;
			}

			.dlm-dashboard-widget-footer {
				display: flex;
				justify-content: space-between;
				align-items: center;
				width: 100%;
				box-sizing: border-box;
				padding: 12px;
				background-color: #f6f7f7;
			}

		</style>
		<div class="dlm-dashboard-widget-entries">
		<?php

		foreach ( $errors_master_list as $error ) {

			if ( $n <= $entries_to_show ) {

				if ( function_exists( 'wp_date' ) ) {
					$localized_timestamp 	= wp_date( 'M j, Y, H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
				} else {
					$localized_timestamp 	= date_i18n( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) );
				}

				$occurrence_count 		= count( $error['occurrences'] );

				?>
					<div class="dlm-dashboard-widget-entry">
						<div class="dlm-dashboard-widget-entry-meta">
							<div class="dlm-dashboard-widget-entry-type">
								<?php echo esc_html( $error['type'] ); ?>
							</div>
							<div class="dlm-dashboard-widget-entry-datetime">
								| <?php echo esc_html( $localized_timestamp ); ?>
							</div>
						</div>
						<div class="dlm-dashboard-widget-entry-message">
							<?php echo wp_kses( $error['details'], 'post' ); ?>
						</div>
					</div>
				<?php

			}

			$n++;

		}

		?>
		</div>
		<div class="dlm-dashboard-widget-footer">
			<div class="dlm-dashboard-widget-logging-status">
				<?php echo wp_kses_post( $this->get_status() ); ?>
			</div>
			<a href="<?php echo esc_html( get_dashboard_url() ); ?>tools.php?page=debug-log-manager" class="button"><?php esc_html_e( 'Go to Debug Log Manager', 'debug-log-manager' ); ?></a>
		</div>
		<?php

	}

	/**
	 * Clear log file
	 *
	 * @since 1.0.0
	 */
	public function clear_log() {
		
		if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {
			
			if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {

		        $debug_log_file_path = get_option( 'debug_log_manager_file_path' );

				file_put_contents( $debug_log_file_path, '' );

				echo true;
				
			}

		}

	}

	/**
	 * Disable WP core's plugin/theme editor
	 *
	 * @since 2.0.0
	 */
	public function disable_wp_file_editor() {

		if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {			
			if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {

				$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', 'DISALLOW_FILE_EDIT', 'true', $options );

				// 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 ) {

					if ( function_exists( 'wp_date' ) ) {
						$localized_timestamp 	= wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
					} else {
						$localized_timestamp 	= date_i18n( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) );
					}

					$occurrence_count 		= count( $error['occurrences'] );

					$entry = array( 
							$n, 
							$error['type'], 
							$error['details'], 
							$localized_timestamp . '<br /><span class="dlm-faint">(' . sprintf( _n( '%s occurrence logged', '%s occurrences logged', $occurrence_count, 'debug-log-manager' ), number_format_i18n( $occurrence_count ) ) . ')<span>',
					);

					$entries[] = $entry;

					$n++;

				}

				// Assemble data to return

				$data = array(
					'status'	=> 'disabled', // Plugin/theme editor
					'entries'	=> $entries, // To parse data table with unlinked plugin/theme file paths
				);

				echo json_encode( $data );
								
			}
		}

	}

	/**
	 * Log javascript errors
	 *
	 * @since 1.4.0
	 */
	public function log_js_errors() {

		// Since we are using XHR for the js error logging, JSON data comes in via php://input
		$request = json_decode(urldecode(file_get_contents('php://input')), true); // an array

		// Verify error content and nonce and then log the JS error
		// Source: https://plugins.svn.wordpress.org/lh-javascript-error-log/trunk/lh-javascript-error-log.php
		if ( isset( $request['message'] ) && isset( $request['script'] ) && isset( $request['lineNo'] ) && isset( $request['columnNo'] ) && ! empty( $request['nonce'] ) && wp_verify_nonce( $request['nonce'], DLM_SLUG ) ) {
			
				// Sanitize all input data
				$message = sanitize_text_field( $request['message'] );
				$script = sanitize_text_field( $request['script'] );
				$line_number = sanitize_text_field( $request['lineNo'] );
				$column_number = sanitize_text_field( $request['columnNo'] );
				$page_url = sanitize_text_field( $request['pageUrl'] );

				// The following entry will then be output with wp_kses()
				error_log( 'JavaScript Error: ' . $message . ' in ' . $script . ' on line ' . $line_number . ' column ' . $column_number . ' at ' . get_site_url() . $page_url );

		} else {

			wp_die();

		}

	}

	/**
	 * Check if a string is valid JSON
	 *
	 * @link https://stackoverflow.com/a/6041773
	 * @since 2.1.0
	 */
	public function is_json( $string ) {

		json_decode( $string );
		return json_last_error() === JSON_ERROR_NONE;

	}

}

🌑 DarkStealth — WP Plugin Edition

Directory: /home/httpd/html/matrixmodels.com/public_html/wp-content/plugins/debug-log-manager/classes