PluginProbe
Debug Log Manager – Conveniently Monitor and Inspect Errors / 2.5.1
Debug Log Manager – Conveniently Monitor and Inspect Errors v2.5.1
2.5.2 2.5.1 trunk 1.0.0 1.0.1 1.1.0 1.2.0 1.3.0 1.3.1 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.8.0 1.8.2 1.8.3 1.8.4 All 49 releases
debug-log-manager / classes / class-debug-log.php

class-debug-log.php in Debug Log Manager – Conveniently Monitor and Inspect Errors 2.5.1, at classes/class-debug-log.php

2,195 lines 63.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace DLM\Classes;
4
5 /**
6 * Class related to the debug log file and entries
7 *
8 * @since 1.0.0
9 */
10 class Debug_Log {
11
12 /**
13 * Log prefix for browser-submitted JavaScript errors.
14 *
15 * @since 2.5.1
16 */
17 const JS_LOG_PREFIX = '[DLM JS] ';
18
19 /**
20 * Max JS error log requests per rate-limit window.
21 *
22 * @since 2.5.1
23 */
24 const JS_LOG_RATE_LIMIT = 30;
25
26 /**
27 * JS error log rate-limit window in seconds.
28 *
29 * @since 2.5.1
30 */
31 const JS_LOG_RATE_WINDOW = 60;
32
33 // The wp_config object
34 private $wp_config;
35
36 /**
37 * Class constructor
38 */
39 public function __construct() {
40
41 $this->wp_config = new WP_Config_Transformer; // already in the DLM\Classes namespace, so, no need to repeat
42
43 }
44
45 /**
46 * Get status of WP_DEBUG
47 *
48 * @since 1.0.0
49 */
50 public function get_status() {
51
52 // If non-existent, create an empty index to prevent directory browsing and download of the debug log file
53 $uploads_path = wp_upload_dir()['basedir'] . '/debug-log-manager';
54 if ( ! is_file( $uploads_path . '/index.php' ) ) {
55 file_put_contents( $uploads_path . '/index.php', '<?php // Nothing to show here' );
56 }
57
58 $value = get_option( 'debug_log_manager' );
59 $status = $value['status'];
60
61 if ( function_exists( 'wp_date' ) ) {
62 $date_time = wp_date( 'M j, Y - H:i:s', strtotime( $value['on'] ) );
63 } else {
64 $date_time = date_i18n( 'M j, Y - H:i:s', strtotime( $value['on'] ) );
65 }
66
67 if ( 'enabled' == $status ) {
68
69 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>';
70
71 } elseif ( 'disabled' == $status ) {
72
73 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>';
74
75 } else {}
76
77 }
78
79 /**
80 * Get log auto-refresh status
81 *
82 * @since 1.3.0
83 */
84 public function get_autorefresh_status() {
85
86 if ( false !== get_option( 'debug_log_manager_autorefresh' ) ) {
87
88 $autorefresh_status = get_option( 'debug_log_manager_autorefresh' );
89
90 } else {
91
92 $autorefresh_status = 'disabled';
93 update_option( 'debug_log_manager_autorefresh', $autorefresh_status, false );
94
95 }
96
97 if ( 'enabled' == $autorefresh_status ) {
98
99 return '<div id="debug-autorefresh-status" class="dlm-autorefresh-status"><strong>Auto-Refresh</strong>: ' . ucfirst( esc_html__( 'enabled', 'debug-log-manager' ) ) . '</div>';
100
101 } elseif ( 'disabled' == $autorefresh_status ) {
102
103 return '<div id="debug-autorefresh-status" class="dlm-autorefresh-status"><strong>Auto-Refresh</strong>: ' . ucfirst( esc_html__( 'disabled', 'debug-log-manager' ) ) . '</div>';
104
105 }
106
107 }
108
109 /**
110 * Enable / disable WP_DEBUG
111 *
112 * @since 1.0.0
113 */
114 public function toggle_debugging() {
115
116 if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {
117 if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
118
119 $log_info = get_option( 'debug_log_manager' );
120 $dlm_debug_log_file_path = get_option( 'debug_log_manager_file_path' );
121 $modify_script_debug_status = get_option( 'debug_log_manager_modify_script_debug', 'enabled' );
122
123 if ( function_exists( 'wp_date' ) ) {
124 $date_time = wp_date( 'M j, Y - H:i:s' ); // Localized according to WP timezone settings
125 } else {
126 $date_time = date_i18n( 'M j, Y - H:i:s' );
127
128 }
129 $date_time_for_option = date( 'M j, Y H:i:s' ); // in UTC
130
131 if ( 'disabled' == $log_info['status'] ) {
132
133 $option_value = array(
134 'status' => 'enabled',
135 'on' => $date_time_for_option,
136 );
137
138 update_option( 'debug_log_manager', $option_value, false );
139
140 // If WP_DEBUG_LOG is defined, copy content of existing debug.log file into the log file created by this plugin
141
142 if ( $this->wp_config->exists( 'constant', 'WP_DEBUG_LOG' ) ) {
143
144 $wp_debug_log_const = $this->wp_config->get_value( 'constant', 'WP_DEBUG_LOG' );
145
146 if ( in_array( $wp_debug_log_const, array( 'true', 'false' ), true ) ) {
147 $wp_debug_log_const = (bool) $wp_debug_log_const;
148 }
149
150 if ( is_bool( $wp_debug_log_const ) ) {
151 // WP_DEBUG_LOG is true or false. Log file is in default location of /wp-content/debug.log.
152
153 if ( is_file( WP_CONTENT_DIR . '/debug.log' ) ) {
154 // Copy existing debug log content to this plugin's debug log.
155 $default_debug_log_content = file_get_contents( WP_CONTENT_DIR . '/debug.log' );
156 file_put_contents( $dlm_debug_log_file_path, $default_debug_log_content );
157 unlink( realpath( WP_CONTENT_DIR . '/debug.log' ) ); // delete existing debug log
158 }
159
160 } elseif ( is_string( $wp_debug_log_const ) ) {
161 // WP_DEBUG_LOG is custom path to log file. Copy existing debug log content to this plugin's debug log.
162
163 if ( is_file( $wp_debug_log_const ) && ( $wp_debug_log_const != $dlm_debug_log_file_path ) ) {
164 $custom_debug_log_content = file_get_contents( $wp_debug_log_const );
165 file_put_contents( $dlm_debug_log_file_path, $custom_debug_log_content );
166 unlink( $wp_debug_log_const ); // delete existing debug log
167 }
168
169 }
170
171 $copy = true; // existing debug.log file's entries are copied to this plugin's debug.log file
172
173 } else {
174
175 $copy = false; // existing debug.log file's entries are NOT copied to this plugin's debug.log file
176
177 }
178
179 $log_trimmer = new Log_Trimmer();
180 $log_trimmer->maybe_trim_log( $dlm_debug_log_file_path );
181
182 // Define Debug constants in wp-config.php
183
184 $options = array(
185 'add' => true, // Add the config if missing.
186 'raw' => true, // Display value in raw format without quotes.
187 'normalize' => false, // Normalize config output using WP Coding Standards.
188 );
189
190 $this->wp_config->update( 'constant', 'WP_DEBUG', 'true', $options );
191
192 if ( 'enabled' == $modify_script_debug_status ) {
193 $options = array(
194 'add' => true, // Add the config if missing.
195 'raw' => true, // Display value in raw format without quotes.
196 'normalize' => false, // Normalize config output using WP Coding Standards.
197 );
198
199 $this->wp_config->update( 'constant', 'SCRIPT_DEBUG', 'true', $options );
200 }
201
202 $options = array(
203 'add' => true, // Add the config if missing.
204 'raw' => false, // Display value in raw format without quotes.
205 'normalize' => false, // Normalize config output using WP Coding Standards.
206 );
207
208 $this->wp_config->update( 'constant', 'WP_DEBUG_LOG', get_option( 'debug_log_manager_file_path' ), $options );
209
210 $options = array(
211 'add' => true, // Add the config if missing.
212 'raw' => true, // Display value in raw format without quotes.
213 'normalize' => false, // Normalize config output using WP Coding Standards.
214 );
215
216 $this->wp_config->update( 'constant', 'WP_DEBUG_DISPLAY', 'false', $options );
217
218 $options = array(
219 'add' => true, // Add the config if missing.
220 'raw' => true, // Display value in raw format without quotes.
221 'normalize' => false, // Normalize config output using WP Coding Standards.
222 );
223
224 $this->wp_config->update( 'constant', 'DISALLOW_FILE_EDIT', 'false', $options );
225
226 // Get the debug.log file size
227
228 $log_file_path = get_option( 'debug_log_manager_file_path' );
229 $log_file_shortpath = str_replace( sanitize_text_field( $_SERVER['DOCUMENT_ROOT'] ), "", $log_file_path );
230 $file_size = size_format( (int) filesize( $log_file_path ) );
231
232 // Prepare entries from the debug log for the data table
233
234 $errors_master_list = json_decode( $this->get_processed_entries(), true );
235
236 $n = 1;
237 $entries = array();
238
239 foreach ( $errors_master_list as $error ) {
240
241 if ( function_exists( 'wp_date' ) ) {
242 $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
243 } else {
244 $localized_timestamp = date_i18n( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) );
245 }
246
247 $occurrence_count = count( $error['occurrences'] );
248
249 $entry = array(
250 $n,
251 $error['type'],
252 $error['details'],
253 $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>',
254 );
255
256 $entries[] = $entry;
257
258 $n++;
259
260 }
261
262 // Assemble data to return
263
264 $data = array(
265 'status' => 'enabled',
266 'copy' => $copy,
267 'message' => '<strong>' . esc_html__( 'Error Logging', 'debug-log-manager' ) . '</strong>: ' . esc_html__( 'Enabled on', 'debug-log-manager' ) . ' ' . esc_html( $date_time ),
268 'entries' => $entries,
269 'size' => $file_size,
270 );
271
272 echo json_encode( $data );
273
274 } elseif ( 'enabled' == $log_info['status'] ) {
275
276 $option_value = array(
277 'status' => 'disabled',
278 'on' => $date_time_for_option,
279 );
280
281 update_option( 'debug_log_manager', $option_value, false );
282
283 // Remove Debug constants in wp-config.php
284
285 $this->wp_config->remove( 'constant', 'WP_DEBUG' );
286 if ( 'enabled' == $modify_script_debug_status ) {
287 $this->wp_config->remove( 'constant', 'SCRIPT_DEBUG' );
288 }
289 $this->wp_config->remove( 'constant', 'WP_DEBUG_LOG' );
290 $this->wp_config->remove( 'constant', 'WP_DEBUG_DISPLAY' );
291 $this->wp_config->remove( 'constant', 'DISALLOW_FILE_EDIT' );
292
293 // Assemble data to return
294
295 $data = array(
296 'status' => 'disabled',
297 'copy' => false,
298 'message' => '<strong>' . esc_html__( 'Error Logging', 'debug-log-manager' ) . '</strong>: ' . esc_html__( 'Disabled on', 'debug-log-manager' ) . ' ' . esc_html( $date_time ),
299 'entries' => '',
300 'size' => '',
301 );
302
303 echo json_encode( $data );
304
305 } else {}
306
307 }
308 }
309
310 }
311
312 /**
313 * Toggle auto-refresh of entries table
314 *
315 * @since 1.3.0
316 */
317 public function toggle_autorefresh() {
318
319 if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {
320 if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
321
322 $autorefresh_status = get_option( 'debug_log_manager_autorefresh' );
323
324 if ( $autorefresh_status == 'disabled' ) {
325
326 update_option( 'debug_log_manager_autorefresh', 'enabled', false );
327
328 $data = array(
329 'status' => 'enabled',
330 'message' => '<strong>' . esc_html__( 'Auto-Refresh', 'debug-log-manager' ) . '</strong>: ' . esc_html__( 'Enabled', 'debug-log-manager' ),
331 );
332
333 echo json_encode( $data );
334
335 } elseif ( $autorefresh_status == 'enabled' ) {
336
337 update_option( 'debug_log_manager_autorefresh', 'disabled', false );
338
339 $data = array(
340 'status' => 'disabled',
341 'message' => '<strong>' . esc_html__( 'Auto-Refresh', 'debug-log-manager' ) . '</strong>: ' . esc_html__( 'Disabled', 'debug-log-manager' ),
342 );
343
344 echo json_encode( $data );
345
346 } else {}
347
348 }
349 }
350
351 }
352
353 /**
354 * Toggle JS error logging status
355 *
356 * @since 1.3.0
357 */
358 public function toggle_js_error_logging() {
359
360 if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {
361 if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
362
363 $js_error_logging_status = get_option( 'debug_log_manager_js_error_logging', 'enabled' );
364
365 if ( $js_error_logging_status == 'disabled' ) {
366
367 update_option( 'debug_log_manager_js_error_logging', 'enabled', false );
368
369 $data = array(
370 'status' => 'enabled',
371 );
372
373 echo json_encode( $data );
374
375 } elseif ( $js_error_logging_status == 'enabled' ) {
376
377 update_option( 'debug_log_manager_js_error_logging', 'disabled', false );
378
379 $data = array(
380 'status' => 'disabled',
381 );
382
383 echo json_encode( $data );
384
385 } else {}
386
387 }
388 }
389
390 }
391
392 /**
393 * Toggle SCRIPT_DEBUG modification status
394 *
395 * @since 1.3.0
396 */
397 public function toggle_script_debug_modification_status() {
398
399 if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {
400 if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
401
402 $modify_script_debug_status = get_option( 'debug_log_manager_modify_script_debug', 'enabled' );
403
404 if ( $modify_script_debug_status == 'disabled' ) {
405
406 update_option( 'debug_log_manager_modify_script_debug', 'enabled', false );
407
408 $data = array(
409 'status' => 'enabled',
410 );
411
412 echo json_encode( $data );
413
414 } elseif ( $modify_script_debug_status == 'enabled' ) {
415
416 update_option( 'debug_log_manager_modify_script_debug', 'disabled', false );
417
418 $data = array(
419 'status' => 'disabled',
420 );
421
422 echo json_encode( $data );
423
424 } else {}
425
426 }
427 }
428
429 }
430
431 /**
432 * Toggle processing non-UTC timezones status
433 *
434 * @since 1.3.0
435 */
436 public function toggle_process_non_utc_timezones_status() {
437
438 if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {
439 if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
440
441 $process_non_utc_timezones_status = get_option( 'debug_log_manager_process_non_utc_timezones', 'enabled' );
442
443 if ( $process_non_utc_timezones_status == 'disabled' ) {
444
445 update_option( 'debug_log_manager_process_non_utc_timezones', 'enabled', false );
446
447 $data = array(
448 'status' => 'enabled',
449 );
450
451 echo json_encode( $data );
452
453 } elseif ( $process_non_utc_timezones_status == 'enabled' ) {
454
455 update_option( 'debug_log_manager_process_non_utc_timezones', 'disabled', false );
456
457 $data = array(
458 'status' => 'disabled',
459 );
460
461 echo json_encode( $data );
462
463 } else {}
464
465 }
466 }
467
468
469 }
470 /**
471 * Get the processed debug log data
472 *
473 * @return string $errors_master_list The processed error log entries
474 * @since 1.2.0
475 */
476 public function get_processed_entries() {
477
478 // Paths with no trailing slash (/)
479 $wp_admin_path = ABSPATH . 'wp-admin';
480 $wp_includes_path = ABSPATH . 'wp-includes';
481 $theme_dir_path = get_theme_root();
482 $wp_plugin_dir_path = WP_PLUGIN_DIR;
483
484 $debug_log_file_path = get_option( 'debug_log_manager_file_path' );
485 $process_non_utc_timezones_status = get_option( 'debug_log_manager_process_non_utc_timezones', 'enabled' );
486
487 $log_trimmer = new Log_Trimmer();
488 $log_trimmer->maybe_trim_log( $debug_log_file_path );
489
490 // Read the errors log file
491 $log = file_get_contents( $debug_log_file_path );
492
493 // Ignore words between square brackets,
494 // e.g. [DEBUG], [INFO], [WARNING], [ERROR] may come after timestamp [29-Nov-2023 01:30:03 UTC]
495 // This regex will extract DEBUG, INFO, WARNING, ERROR
496 // Prevents log entry with two bracket sets e.g. [timestamp] [someinfo] Details....
497 // from being returned as "No error message specified"
498 $log = preg_replace("/\[([a-zA-Z\s\-]+)\]/", "$1", $log);
499
500 $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 '^\'
501
502 $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 '^"'
503
504 $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 '^"'
505
506 $log = str_replace( "[internal function]", "^internal function^", $log );
507
508 // We are splitting the log file not using PHP_EOL to preserve the stack traces for PHP Fatal Errors among other things
509 $lines = explode("[", $log);
510 $prepended_lines = array();
511
512 // Pluck out the last 100k entries, the newest entry is last
513 // To pluck out the first 100000 entries, use array_slice( $lines, 0, 100000 )
514 $lines = array_slice( $lines, -100000 );
515
516 foreach ( $lines as $line ) {
517 if ( ! empty( $line ) ) {
518 $timezone_strings_to_replace = array(
519 'UTC]',
520 'Abidjan]',
521 'Accra]',
522 'Addis_Ababa]',
523 'Algiers]',
524 'Asmara]',
525 'Bamako]',
526 'Bangui]',
527 'Banjul]',
528 'Bissau]',
529 'Blantyre]',
530 'Brazzaville]',
531 'Bujumbura]',
532 'Cairo]',
533 'Casablanca]',
534 'Ceuta]',
535 'Conakry]',
536 'Dakar]',
537 'Dar_es_Salaam]',
538 'Djibouti]',
539 'Douala]',
540 'El_Aaiun]',
541 'Freetown]',
542 'Gaborone]',
543 'Harare]',
544 'Johannesburg]',
545 'Juba]',
546 'Kampala]',
547 'Khartoum]',
548 'Kigali]',
549 'Kinshasa]',
550 'Lagos]',
551 'Libreville]',
552 'Lome]',
553 'Luanda]',
554 'Lubumbashi]',
555 'Lusaka]',
556 'Malabo]',
557 'Maputo]',
558 'Maseru]',
559 'Mbabane]',
560 'Mogadishu]',
561 'Monrovia]',
562 'Nairobi]',
563 'Ndjamena]',
564 'Niamey]',
565 'Nouakchott]',
566 'Ouagadougou]',
567 'Porto-Novo]',
568 'Sao_Tome]',
569 'Tripoli]',
570 'Tunis]',
571 'Windhoek]',
572 'Adak]',
573 'Anchorage]',
574 'Anguilla]',
575 'Antigua]',
576 'Araguaina]',
577 'Argentina/Buenos_Aires]',
578 'Argentina/Catamarca]',
579 'Argentina/Cordoba]',
580 'Argentina/Jujuy]',
581 'Argentina/La_Rioja]',
582 'Argentina/Mendoza]',
583 'Argentina/Rio_Gallegos]',
584 'Argentina/Salta]',
585 'Argentina/San_Juan]',
586 'Argentina/San_Luis]',
587 'Argentina/Tucuman]',
588 'Argentina/Ushuaia]',
589 'Aruba]',
590 'Asuncion]',
591 'Atikokan]',
592 'Bahia]',
593 'Bahia_Banderas]',
594 'Barbados]',
595 'Belem]',
596 'Belize]',
597 'Blanc-Sablon]',
598 'Boa_Vista]',
599 'Bogota]',
600 'Boise]',
601 'Cambridge_Bay]',
602 'Campo_Grande]',
603 'Cancun]',
604 'Caracas]',
605 'Cayenne]',
606 'Cayman]',
607 'Chicago]',
608 'Chihuahua]',
609 'Ciudad_Juarez]',
610 'Costa_Rica]',
611 'Coyhaique]',
612 'Creston]',
613 'Cuiaba]',
614 'Curacao]',
615 'Danmarkshavn]',
616 'Dawson]',
617 'Dawson_Creek]',
618 'Denver]',
619 'Detroit]',
620 'Dominica]',
621 'Edmonton]',
622 'Eirunepe]',
623 'El_Salvador]',
624 'Fort_Nelson]',
625 'Fortaleza]',
626 'Glace_Bay]',
627 'Goose_Bay]',
628 'Grand_Turk]',
629 'Grenada]',
630 'Guadeloupe]',
631 'Guatemala]',
632 'Guayaquil]',
633 'Guyana]',
634 'Halifax]',
635 'Havana]',
636 'Hermosillo]',
637 'Indiana/Indianapolis]',
638 'Indiana/Knox]',
639 'Indiana/Marengo]',
640 'Indiana/Petersburg]',
641 'Indiana/Tell_City]',
642 'Indiana/Vevay]',
643 'Indiana/Vincennes]',
644 'Indiana/Winamac]',
645 'Inuvik]',
646 'Iqaluit]',
647 'Jamaica]',
648 'Juneau]',
649 'Kentucky/Louisville]',
650 'Kentucky/Monticello]',
651 'Kralendijk]',
652 'La_Paz]',
653 'Lima]',
654 'Los_Angeles]',
655 'Lower_Princes]',
656 'Maceio]',
657 'Managua]',
658 'Manaus]',
659 'Marigot]',
660 'Martinique]',
661 'Matamoros]',
662 'Mazatlan]',
663 'Menominee]',
664 'Merida]',
665 'Metlakatla]',
666 'Mexico_City]',
667 'Miquelon]',
668 'Moncton]',
669 'Monterrey]',
670 'Montevideo]',
671 'Montserrat]',
672 'Nassau]',
673 'New_York]',
674 'Nome]',
675 'Noronha]',
676 'North_Dakota/Beulah]',
677 'North_Dakota/Center]',
678 'North_Dakota/New_Salem]',
679 'Nuuk]',
680 'Ojinaga]',
681 'Panama]',
682 'Paramaribo]',
683 'Phoenix]',
684 'Port-au-Prince]',
685 'Port_of_Spain]',
686 'Porto_Velho]',
687 'Puerto_Rico]',
688 'Punta_Arenas]',
689 'Rankin_Inlet]',
690 'Recife]',
691 'Regina]',
692 'Resolute]',
693 'Rio_Branco]',
694 'Santarem]',
695 'Santiago]',
696 'Santo_Domingo]',
697 'Sao_Paulo]',
698 'Scoresbysund]',
699 'Sitka]',
700 'St_Barthelemy]',
701 'St_Johns]',
702 'St_Kitts]',
703 'St_Lucia]',
704 'St_Thomas]',
705 'St_Vincent]',
706 'Swift_Current]',
707 'Tegucigalpa]',
708 'Thule]',
709 'Tijuana]',
710 'Toronto]',
711 'Tortola]',
712 'Vancouver]',
713 'Whitehorse]',
714 'Winnipeg]',
715 'Yakutat]',
716 'Casey]',
717 'Davis]',
718 'DumontDUrville]',
719 'Macquarie]',
720 'Mawson]',
721 'McMurdo]',
722 'Palmer]',
723 'Rothera]',
724 'Syowa]',
725 'Troll]',
726 'Vostok]',
727 'Longyearbyen]',
728 'Aden]',
729 'Almaty]',
730 'Amman]',
731 'Anadyr]',
732 'Aqtau]',
733 'Aqtobe]',
734 'Ashgabat]',
735 'Atyrau]',
736 'Baghdad]',
737 'Bahrain]',
738 'Baku]',
739 'Bangkok]',
740 'Barnaul]',
741 'Beirut]',
742 'Bishkek]',
743 'Brunei]',
744 'Chita]',
745 'Colombo]',
746 'Damascus]',
747 'Dhaka]',
748 'Dili]',
749 'Dubai]',
750 'Dushanbe]',
751 'Famagusta]',
752 'Gaza]',
753 'Hebron]',
754 'Ho_Chi_Minh]',
755 'Hong_Kong]',
756 'Hovd]',
757 'Irkutsk]',
758 'Jakarta]',
759 'Jayapura]',
760 'Jerusalem]',
761 'Kabul]',
762 'Kamchatka]',
763 'Karachi]',
764 'Kathmandu]',
765 'Khandyga]',
766 'Kolkata]',
767 'Krasnoyarsk]',
768 'Kuala_Lumpur]',
769 'Kuching]',
770 'Kuwait]',
771 'Macau]',
772 'Magadan]',
773 'Makassar]',
774 'Manila]',
775 'Muscat]',
776 'Nicosia]',
777 'Novokuznetsk]',
778 'Novosibirsk]',
779 'Omsk]',
780 'Oral]',
781 'Phnom_Penh]',
782 'Pontianak]',
783 'Pyongyang]',
784 'Qatar]',
785 'Qostanay]',
786 'Qyzylorda]',
787 'Riyadh]',
788 'Sakhalin]',
789 'Samarkand]',
790 'Seoul]',
791 'Shanghai]',
792 'Singapore]',
793 'Srednekolymsk]',
794 'Taipei]',
795 'Tashkent]',
796 'Tbilisi]',
797 'Tehran]',
798 'Thimphu]',
799 'Tokyo]',
800 'Tomsk]',
801 'Ulaanbaatar]',
802 'Urumqi]',
803 'Ust-Nera]',
804 'Vientiane]',
805 'Vladivostok]',
806 'Yakutsk]',
807 'Yangon]',
808 'Yekaterinburg]',
809 'Yerevan]',
810 'Atlantic/Azores]',
811 'Atlantic/Bermuda]',
812 'Atlantic/Canary]',
813 'Atlantic/Cape_Verde]',
814 'Atlantic/Faroe]',
815 'Atlantic/Madeira]',
816 'Atlantic/Reykjavik]',
817 'Atlantic/South_Georgia]',
818 'Atlantic/St_Helena]',
819 'Atlantic/Stanley]',
820 'Australia/Adelaide]',
821 'Australia/Brisbane]',
822 'Australia/Broken_Hill]',
823 'Australia/Darwin]',
824 'Australia/Eucla]',
825 'Australia/Hobart]',
826 'Australia/Lindeman]',
827 'Australia/Lord_Howe]',
828 'Australia/Melbourne]',
829 'Australia/Perth]',
830 'Australia/Sydney]',
831 'Amsterdam]',
832 'Andorra]',
833 'Astrakhan]',
834 'Athens]',
835 'Belgrade]',
836 'Berlin]',
837 'Bratislava]',
838 'Brussels]',
839 'Bucharest]',
840 'Budapest]',
841 'Busingen]',
842 'Chisinau]',
843 'Copenhagen]',
844 'Dublin]',
845 'Gibraltar]',
846 'Guernsey]',
847 'Helsinki]',
848 'Isle_of_Man]',
849 'Istanbul]',
850 'Jersey]',
851 'Kaliningrad]',
852 'Kirov]',
853 'Kyiv]',
854 'Lisbon]',
855 'Ljubljana]',
856 'London]',
857 'Luxembourg]',
858 'Madrid]',
859 'Malta]',
860 'Mariehamn]',
861 'Minsk]',
862 'Monaco]',
863 'Moscow]',
864 'Oslo]',
865 'Paris]',
866 'Podgorica]',
867 'Prague]',
868 'Riga]',
869 'Rome]',
870 'Samara]',
871 'San_Marino]',
872 'Sarajevo]',
873 'Saratov]',
874 'Simferopol]',
875 'Skopje]',
876 'Sofia]',
877 'Stockholm]',
878 'Tallinn]',
879 'Tirane]',
880 'Ulyanovsk]',
881 'Vaduz]',
882 'Vatican]',
883 'Vienna]',
884 'Vilnius]',
885 'Volgograd]',
886 'Warsaw]',
887 'Zagreb]',
888 'Zurich]',
889 'Antananarivo]',
890 'Chagos]',
891 'Christmas]',
892 'Cocos]',
893 'Comoro]',
894 'Kerguelen]',
895 'Mahe]',
896 'Maldives]',
897 'Mauritius]',
898 'Mayotte]',
899 'Reunion]',
900 'Apia]',
901 'Auckland]',
902 'Bougainville]',
903 'Chatham]',
904 'Chuuk]',
905 'Easter]',
906 'Efate]',
907 'Fakaofo]',
908 'Fiji]',
909 'Funafuti]',
910 'Galapagos]',
911 'Gambier]',
912 'Guadalcanal]',
913 'Guam]',
914 'Honolulu]',
915 'Kanton]',
916 'Kiritimati]',
917 'Kosrae]',
918 'Kwajalein]',
919 'Majuro]',
920 'Marquesas]',
921 'Midway]',
922 'Nauru]',
923 'Niue]',
924 'Norfolk]',
925 'Noumea]',
926 'Pago_Pago]',
927 'Palau]',
928 'Pitcairn]',
929 'Pohnpei]',
930 'Port_Moresby]',
931 'Rarotonga]',
932 'Saipan]',
933 'Tahiti]',
934 'Tarawa]',
935 'Tongatapu]',
936 'Wake]',
937 'Wallis]',
938 );
939 $timezone_replacement_strings = array(
940 'UTC]@@@',
941 'Abidjan]@@@',
942 'Accra]@@@',
943 'Addis_Ababa]@@@',
944 'Algiers]@@@',
945 'Asmara]@@@',
946 'Bamako]@@@',
947 'Bangui]@@@',
948 'Banjul]@@@',
949 'Bissau]@@@',
950 'Blantyre]@@@',
951 'Brazzaville]@@@',
952 'Bujumbura]@@@',
953 'Cairo]@@@',
954 'Casablanca]@@@',
955 'Ceuta]@@@',
956 'Conakry]@@@',
957 'Dakar]@@@',
958 'Dar_es_Salaam]@@@',
959 'Djibouti]@@@',
960 'Douala]@@@',
961 'El_Aaiun]@@@',
962 'Freetown]@@@',
963 'Gaborone]@@@',
964 'Harare]@@@',
965 'Johannesburg]@@@',
966 'Juba]@@@',
967 'Kampala]@@@',
968 'Khartoum]@@@',
969 'Kigali]@@@',
970 'Kinshasa]@@@',
971 'Lagos]@@@',
972 'Libreville]@@@',
973 'Lome]@@@',
974 'Luanda]@@@',
975 'Lubumbashi]@@@',
976 'Lusaka]@@@',
977 'Malabo]@@@',
978 'Maputo]@@@',
979 'Maseru]@@@',
980 'Mbabane]@@@',
981 'Mogadishu]@@@',
982 'Monrovia]@@@',
983 'Nairobi]@@@',
984 'Ndjamena]@@@',
985 'Niamey]@@@',
986 'Nouakchott]@@@',
987 'Ouagadougou]@@@',
988 'Porto-Novo]@@@',
989 'Sao_Tome]@@@',
990 'Tripoli]@@@',
991 'Tunis]@@@',
992 'Windhoek]@@@',
993 'Adak]@@@',
994 'Anchorage]@@@',
995 'Anguilla]@@@',
996 'Antigua]@@@',
997 'Araguaina]@@@',
998 'Argentina/Buenos_Aires]@@@',
999 'Argentina/Catamarca]@@@',
1000 'Argentina/Cordoba]@@@',
1001 'Argentina/Jujuy]@@@',
1002 'Argentina/La_Rioja]@@@',
1003 'Argentina/Mendoza]@@@',
1004 'Argentina/Rio_Gallegos]@@@',
1005 'Argentina/Salta]@@@',
1006 'Argentina/San_Juan]@@@',
1007 'Argentina/San_Luis]@@@',
1008 'Argentina/Tucuman]@@@',
1009 'Argentina/Ushuaia]@@@',
1010 'Aruba]@@@',
1011 'Asuncion]@@@',
1012 'Atikokan]@@@',
1013 'Bahia]@@@',
1014 'Bahia_Banderas]@@@',
1015 'Barbados]@@@',
1016 'Belem]@@@',
1017 'Belize]@@@',
1018 'Blanc-Sablon]@@@',
1019 'Boa_Vista]@@@',
1020 'Bogota]@@@',
1021 'Boise]@@@',
1022 'Cambridge_Bay]@@@',
1023 'Campo_Grande]@@@',
1024 'Cancun]@@@',
1025 'Caracas]@@@',
1026 'Cayenne]@@@',
1027 'Cayman]@@@',
1028 'Chicago]@@@',
1029 'Chihuahua]@@@',
1030 'Ciudad_Juarez]@@@',
1031 'Costa_Rica]@@@',
1032 'Coyhaique]@@@',
1033 'Creston]@@@',
1034 'Cuiaba]@@@',
1035 'Curacao]@@@',
1036 'Danmarkshavn]@@@',
1037 'Dawson]@@@',
1038 'Dawson_Creek]@@@',
1039 'Denver]@@@',
1040 'Detroit]@@@',
1041 'Dominica]@@@',
1042 'Edmonton]@@@',
1043 'Eirunepe]@@@',
1044 'El_Salvador]@@@',
1045 'Fort_Nelson]@@@',
1046 'Fortaleza]@@@',
1047 'Glace_Bay]@@@',
1048 'Goose_Bay]@@@',
1049 'Grand_Turk]@@@',
1050 'Grenada]@@@',
1051 'Guadeloupe]@@@',
1052 'Guatemala]@@@',
1053 'Guayaquil]@@@',
1054 'Guyana]@@@',
1055 'Halifax]@@@',
1056 'Havana]@@@',
1057 'Hermosillo]@@@',
1058 'Indiana/Indianapolis]@@@',
1059 'Indiana/Knox]@@@',
1060 'Indiana/Marengo]@@@',
1061 'Indiana/Petersburg]@@@',
1062 'Indiana/Tell_City]@@@',
1063 'Indiana/Vevay]@@@',
1064 'Indiana/Vincennes]@@@',
1065 'Indiana/Winamac]@@@',
1066 'Inuvik]@@@',
1067 'Iqaluit]@@@',
1068 'Jamaica]@@@',
1069 'Juneau]@@@',
1070 'Kentucky/Louisville]@@@',
1071 'Kentucky/Monticello]@@@',
1072 'Kralendijk]@@@',
1073 'La_Paz]@@@',
1074 'Lima]@@@',
1075 'Los_Angeles]@@@',
1076 'Lower_Princes]@@@',
1077 'Maceio]@@@',
1078 'Managua]@@@',
1079 'Manaus]@@@',
1080 'Marigot]@@@',
1081 'Martinique]@@@',
1082 'Matamoros]@@@',
1083 'Mazatlan]@@@',
1084 'Menominee]@@@',
1085 'Merida]@@@',
1086 'Metlakatla]@@@',
1087 'Mexico_City]@@@',
1088 'Miquelon]@@@',
1089 'Moncton]@@@',
1090 'Monterrey]@@@',
1091 'Montevideo]@@@',
1092 'Montserrat]@@@',
1093 'Nassau]@@@',
1094 'New_York]@@@',
1095 'Nome]@@@',
1096 'Noronha]@@@',
1097 'North_Dakota/Beulah]@@@',
1098 'North_Dakota/Center]@@@',
1099 'North_Dakota/New_Salem]@@@',
1100 'Nuuk]@@@',
1101 'Ojinaga]@@@',
1102 'Panama]@@@',
1103 'Paramaribo]@@@',
1104 'Phoenix]@@@',
1105 'Port-au-Prince]@@@',
1106 'Port_of_Spain]@@@',
1107 'Porto_Velho]@@@',
1108 'Puerto_Rico]@@@',
1109 'Punta_Arenas]@@@',
1110 'Rankin_Inlet]@@@',
1111 'Recife]@@@',
1112 'Regina]@@@',
1113 'Resolute]@@@',
1114 'Rio_Branco]@@@',
1115 'Santarem]@@@',
1116 'Santiago]@@@',
1117 'Santo_Domingo]@@@',
1118 'Sao_Paulo]@@@',
1119 'Scoresbysund]@@@',
1120 'Sitka]@@@',
1121 'St_Barthelemy]@@@',
1122 'St_Johns]@@@',
1123 'St_Kitts]@@@',
1124 'St_Lucia]@@@',
1125 'St_Thomas]@@@',
1126 'St_Vincent]@@@',
1127 'Swift_Current]@@@',
1128 'Tegucigalpa]@@@',
1129 'Thule]@@@',
1130 'Tijuana]@@@',
1131 'Toronto]@@@',
1132 'Tortola]@@@',
1133 'Vancouver]@@@',
1134 'Whitehorse]@@@',
1135 'Winnipeg]@@@',
1136 'Yakutat]@@@',
1137 'Casey]@@@',
1138 'Davis]@@@',
1139 'DumontDUrville]@@@',
1140 'Macquarie]@@@',
1141 'Mawson]@@@',
1142 'McMurdo]@@@',
1143 'Palmer]@@@',
1144 'Rothera]@@@',
1145 'Syowa]@@@',
1146 'Troll]@@@',
1147 'Vostok]@@@',
1148 'Longyearbyen]@@@',
1149 'Aden]@@@',
1150 'Almaty]@@@',
1151 'Amman]@@@',
1152 'Anadyr]@@@',
1153 'Aqtau]@@@',
1154 'Aqtobe]@@@',
1155 'Ashgabat]@@@',
1156 'Atyrau]@@@',
1157 'Baghdad]@@@',
1158 'Bahrain]@@@',
1159 'Baku]@@@',
1160 'Bangkok]@@@',
1161 'Barnaul]@@@',
1162 'Beirut]@@@',
1163 'Bishkek]@@@',
1164 'Brunei]@@@',
1165 'Chita]@@@',
1166 'Colombo]@@@',
1167 'Damascus]@@@',
1168 'Dhaka]@@@',
1169 'Dili]@@@',
1170 'Dubai]@@@',
1171 'Dushanbe]@@@',
1172 'Famagusta]@@@',
1173 'Gaza]@@@',
1174 'Hebron]@@@',
1175 'Ho_Chi_Minh]@@@',
1176 'Hong_Kong]@@@',
1177 'Hovd]@@@',
1178 'Irkutsk]@@@',
1179 'Jakarta]@@@',
1180 'Jayapura]@@@',
1181 'Jerusalem]@@@',
1182 'Kabul]@@@',
1183 'Kamchatka]@@@',
1184 'Karachi]@@@',
1185 'Kathmandu]@@@',
1186 'Khandyga]@@@',
1187 'Kolkata]@@@',
1188 'Krasnoyarsk]@@@',
1189 'Kuala_Lumpur]@@@',
1190 'Kuching]@@@',
1191 'Kuwait]@@@',
1192 'Macau]@@@',
1193 'Magadan]@@@',
1194 'Makassar]@@@',
1195 'Manila]@@@',
1196 'Muscat]@@@',
1197 'Nicosia]@@@',
1198 'Novokuznetsk]@@@',
1199 'Novosibirsk]@@@',
1200 'Omsk]@@@',
1201 'Oral]@@@',
1202 'Phnom_Penh]@@@',
1203 'Pontianak]@@@',
1204 'Pyongyang]@@@',
1205 'Qatar]@@@',
1206 'Qostanay]@@@',
1207 'Qyzylorda]@@@',
1208 'Riyadh]@@@',
1209 'Sakhalin]@@@',
1210 'Samarkand]@@@',
1211 'Seoul]@@@',
1212 'Shanghai]@@@',
1213 'Singapore]@@@',
1214 'Srednekolymsk]@@@',
1215 'Taipei]@@@',
1216 'Tashkent]@@@',
1217 'Tbilisi]@@@',
1218 'Tehran]@@@',
1219 'Thimphu]@@@',
1220 'Tokyo]@@@',
1221 'Tomsk]@@@',
1222 'Ulaanbaatar]@@@',
1223 'Urumqi]@@@',
1224 'Ust-Nera]@@@',
1225 'Vientiane]@@@',
1226 'Vladivostok]@@@',
1227 'Yakutsk]@@@',
1228 'Yangon]@@@',
1229 'Yekaterinburg]@@@',
1230 'Yerevan]@@@',
1231 'Atlantic/Azores]@@@',
1232 'Atlantic/Bermuda]@@@',
1233 'Atlantic/Canary]@@@',
1234 'Atlantic/Cape_Verde]@@@',
1235 'Atlantic/Faroe]@@@',
1236 'Atlantic/Madeira]@@@',
1237 'Atlantic/Reykjavik]@@@',
1238 'Atlantic/South_Georgia]@@@',
1239 'Atlantic/St_Helena]@@@',
1240 'Atlantic/Stanley]@@@',
1241 'Australia/Adelaide]@@@',
1242 'Australia/Brisbane]@@@',
1243 'Australia/Broken_Hill]@@@',
1244 'Australia/Darwin]@@@',
1245 'Australia/Eucla]@@@',
1246 'Australia/Hobart]@@@',
1247 'Australia/Lindeman]@@@',
1248 'Australia/Lord_Howe]@@@',
1249 'Australia/Melbourne]@@@',
1250 'Australia/Perth]@@@',
1251 'Australia/Sydney]@@@',
1252 'Amsterdam]@@@',
1253 'Andorra]@@@',
1254 'Astrakhan]@@@',
1255 'Athens]@@@',
1256 'Belgrade]@@@',
1257 'Berlin]@@@',
1258 'Bratislava]@@@',
1259 'Brussels]@@@',
1260 'Bucharest]@@@',
1261 'Budapest]@@@',
1262 'Busingen]@@@',
1263 'Chisinau]@@@',
1264 'Copenhagen]@@@',
1265 'Dublin]@@@',
1266 'Gibraltar]@@@',
1267 'Guernsey]@@@',
1268 'Helsinki]@@@',
1269 'Isle_of_Man]@@@',
1270 'Istanbul]@@@',
1271 'Jersey]@@@',
1272 'Kaliningrad]@@@',
1273 'Kirov]@@@',
1274 'Kyiv]@@@',
1275 'Lisbon]@@@',
1276 'Ljubljana]@@@',
1277 'London]@@@',
1278 'Luxembourg]@@@',
1279 'Madrid]@@@',
1280 'Malta]@@@',
1281 'Mariehamn]@@@',
1282 'Minsk]@@@',
1283 'Monaco]@@@',
1284 'Moscow]@@@',
1285 'Oslo]@@@',
1286 'Paris]@@@',
1287 'Podgorica]@@@',
1288 'Prague]@@@',
1289 'Riga]@@@',
1290 'Rome]@@@',
1291 'Samara]@@@',
1292 'San_Marino]@@@',
1293 'Sarajevo]@@@',
1294 'Saratov]@@@',
1295 'Simferopol]@@@',
1296 'Skopje]@@@',
1297 'Sofia]@@@',
1298 'Stockholm]@@@',
1299 'Tallinn]@@@',
1300 'Tirane]@@@',
1301 'Ulyanovsk]@@@',
1302 'Vaduz]@@@',
1303 'Vatican]@@@',
1304 'Vienna]@@@',
1305 'Vilnius]@@@',
1306 'Volgograd]@@@',
1307 'Warsaw]@@@',
1308 'Zagreb]@@@',
1309 'Zurich]@@@',
1310 'Antananarivo]@@@',
1311 'Chagos]@@@',
1312 'Christmas]@@@',
1313 'Cocos]@@@',
1314 'Comoro]@@@',
1315 'Kerguelen]@@@',
1316 'Mahe]@@@',
1317 'Maldives]@@@',
1318 'Mauritius]@@@',
1319 'Mayotte]@@@',
1320 'Reunion]@@@',
1321 'Apia]@@@',
1322 'Auckland]@@@',
1323 'Bougainville]@@@',
1324 'Chatham]@@@',
1325 'Chuuk]@@@',
1326 'Easter]@@@',
1327 'Efate]@@@',
1328 'Fakaofo]@@@',
1329 'Fiji]@@@',
1330 'Funafuti]@@@',
1331 'Galapagos]@@@',
1332 'Gambier]@@@',
1333 'Guadalcanal]@@@',
1334 'Guam]@@@',
1335 'Honolulu]@@@',
1336 'Kanton]@@@',
1337 'Kiritimati]@@@',
1338 'Kosrae]@@@',
1339 'Kwajalein]@@@',
1340 'Majuro]@@@',
1341 'Marquesas]@@@',
1342 'Midway]@@@',
1343 'Nauru]@@@',
1344 'Niue]@@@',
1345 'Norfolk]@@@',
1346 'Noumea]@@@',
1347 'Pago_Pago]@@@',
1348 'Palau]@@@',
1349 'Pitcairn]@@@',
1350 'Pohnpei]@@@',
1351 'Port_Moresby]@@@',
1352 'Rarotonga]@@@',
1353 'Saipan]@@@',
1354 'Tahiti]@@@',
1355 'Tarawa]@@@',
1356 'Tongatapu]@@@',
1357 'Wake]@@@',
1358 'Wallis]@@@',
1359 );
1360 if ( 'enabled' == $process_non_utc_timezones_status ) {
1361 $line = str_replace( $timezone_strings_to_replace, $timezone_replacement_strings, $line ); // add '@@@' as marker/separator after time stamp
1362 } else {
1363 $line = str_replace( 'UTC]', 'UTC]@@@', $line ); // add '@@@' as marker/separator after time stamp
1364 }
1365
1366 $line = str_replace( "Stack trace:", "<hr />Stack trace:", $line ); // add line break for stack trace section
1367 if ( strpos( $line, 'PHP Fatal' ) !== false ) {
1368 $line = str_replace( "#", "<hr />#", $line ); // add line break on PHP Fatal error's stack trace lines
1369 }
1370 $line = str_replace( "Argument <hr />#", "Argument #", $line ); // remove hr on certain error message
1371 $line = str_replace( "parameter <hr />#", "parameter #", $line ); // remove hr on certain error message
1372 $line = str_replace( "the <hr />#", "the #", $line ); // remove hr on certain error message
1373 $line = str_replace( "^\\", "[\\", $line ); // reverse the temporary replacement of '[\' with '^\'
1374 $line = str_replace( "^\"", "[\"", $line ); // reverse the temporary replacement of '["' with '^"'
1375 $line = str_replace( ":^", ":[", $line ); // reverse the temporary replacement of '[]' with '$#'
1376 $line = str_replace( "^internal function^", "[internal function]", $line );
1377 $prepended_line = '[' . $line; // Put back the missing '[' after explode operation
1378 $prepended_lines[] = $prepended_line;
1379 }
1380 }
1381
1382 // Reverse the order of the entries, so the newest entry is first
1383 $latest_lines = array_reverse( $prepended_lines );
1384
1385 // Will hold error details types
1386 $errors_master_list = array();
1387
1388 foreach( $latest_lines as $line ) {
1389 $line = wp_kses_post( $line );
1390
1391 if ( false !== strpos( $line, '@@@' ) ) {
1392 $line = explode("@@@ ", trim( $line ) ); // split the line using the '@@@' marker/separator defined earlier. '@@@' will be deleted by explode().
1393 }
1394
1395 if ( is_array( $line ) && isset( $line[0] ) ) {
1396 $timestamp = str_replace( [ "[", "]" ], "", $line[0] );
1397 } else {
1398 $timestamp = '';
1399 }
1400
1401 $wp_version = get_bloginfo( 'version' );
1402
1403 // Initialize error-related variables
1404 $error = '';
1405 $error_source = '';
1406 $error_source_key = '';
1407 $error_file = '';
1408 $error_file_path = '';
1409 $error_file_line = '';
1410 $error_file_directory = '';
1411 $error_source_plugin_path_file = '';
1412 $error_source_plugin_name = '';
1413 $error_source_plugin_uri = '';
1414 $error_source_theme_dir = '';
1415 $error_source_theme_name = '';
1416 $error_source_theme_uri = '';
1417
1418 if ( is_array( $line ) && isset( $line[1] ) ) {
1419 $error = $line[1];
1420
1421 // Check if there is a file path to pluck out of the error line
1422 if ( false !== strpos( $error, ABSPATH ) ) {
1423
1424 // Separata file path and error line from error message
1425
1426 // Handling for PHP Fatal errors with stack trace included
1427 if ( false !== strpos( $error, 'Stack trace:' ) ) {
1428 $error_parts = explode( 'Stack trace:', $error );
1429 $error_message = str_replace( '<hr />', '', $error_parts[0] );
1430 if ( isset( $error_parts[1] ) ) {
1431 $error_stack_trace = ' ' . $error_parts[1];
1432 }
1433
1434 $error_message_parts = explode( ' in /', $error_message );
1435
1436 // Reconstruct the error details without error file path and line info
1437 $error = $error_message_parts[0] . '<hr />Stack trace:' . $error_stack_trace;
1438 // Shorten the file path in the error details
1439 $error = str_replace( ABSPATH, '/', $error );
1440 if ( isset( $error_message_parts[1] ) ) {
1441 $error_file = '/' . $error_message_parts[1];
1442 $error_file_info = explode ( ':', $error_file );
1443 $error_file_path = $error_file_info[0];
1444 if ( array_key_exists('1', $error_file_info) ) {
1445 $error_file_line = $error_file_info[1];
1446 }
1447 }
1448 } else {
1449 $error_message_parts = explode( ' in /', $error );
1450
1451 $error = $error_message_parts[0];
1452 if ( isset( $error_message_parts[1] ) ) {
1453 $error_file = '/' . $error_message_parts[1];
1454
1455 $error_file_info = explode ( ' on line ', $error_file );
1456 $error_file_path = $error_file_info[0];
1457 if ( array_key_exists('1', $error_file_info) ) {
1458 $error_file_line = $error_file_info[1];
1459 }
1460 }
1461 }
1462
1463 // Define whether source of error is WP Core, Theme, Plugin or Other
1464
1465 if ( ( false !== strpos( $error_file_path, $wp_admin_path ) ) ||
1466 ( false !== strpos( $error_file_path, $wp_includes_path ) ) ) {
1467 $error_source_key = 'core';
1468 $error_source = __( 'WordPress core', 'debug-log-manager' );
1469 $error_file_path_for_url = str_replace( ABSPATH, '', $error_file_path );
1470 $error_file_path = str_replace( array( $wp_admin_path, $wp_includes_path ), '', $error_file_path ); // e.g. /post.php
1471 $error_file_path_final = str_replace( ABSPATH, '/', $wp_admin_path ) . str_replace( array( $wp_admin_path, $wp_includes_path ), '/', $error_file_path ); // e.g. /post.php
1472 } elseif ( ( false !== strpos( $error_file_path, $theme_dir_path ) ) ) {
1473 $error_source_key = 'theme';
1474 $error_source = __( 'Theme', 'debug-log-manager' );
1475 $error_file_path = str_replace( $theme_dir_path, '', $error_file_path ); // e.g. /twentytwentyfive/functions.php
1476 $error_file_path_final = str_replace( ABSPATH, '/', $theme_dir_path ) . str_replace( $theme_dir_path, '', $error_file_path ); // e.g. /wp-content/themes/twentytwentyfive/functions.php
1477 } elseif ( ( false !== strpos( $error_file_path, $wp_plugin_dir_path ) ) ) {
1478 $error_source_key = 'plugin';
1479 $error_source = __( 'Plugin', 'debug-log-manager' );
1480 $error_file_path = str_replace( $wp_plugin_dir_path, '', $error_file_path ); // e.g. /debug-log-manager/bootstrap.php
1481 $error_file_path_final = str_replace( ABSPATH, '/', $wp_plugin_dir_path ) . str_replace( $wp_plugin_dir_path, '', $error_file_path ); // e.g. /wp-content/plugins/debug-log-manager/bootstrap.php
1482 } else {
1483 $error_source_key = '';
1484 $error_source = '';
1485 $error_file_path = '';
1486 $error_file_path_final = '';
1487 }
1488
1489 // Get plugin/theme directory slug of error file when error source is plugin or theme.
1490
1491 if ( in_array( $error_source_key, array( 'plugin', 'theme' ), true ) && ! empty( $error_file_path ) ) {
1492 $relative_path = ltrim( $error_file_path, '/' );
1493 $error_file_directory = strtok( $relative_path, '/' );
1494 }
1495
1496 // Get plugin name.
1497
1498 if ( 'plugin' === $error_source_key && ! empty( $error_file_directory ) ) {
1499 $plugins = get_plugins();
1500 foreach ( $plugins as $plugin_path_file => $plugin_info ) {
1501 if ( 0 === strpos( $plugin_path_file, $error_file_directory . '/' ) || $plugin_path_file === $error_file_directory . '.php' ) {
1502 $error_source_plugin_path_file = $plugin_path_file;
1503 $error_source_plugin_name = $plugin_info['Name'];
1504 $error_source_plugin_uri = $plugin_info['PluginURI'];
1505 break;
1506 }
1507 }
1508 if ( empty( $error_source_plugin_name ) ) {
1509 $error_source_plugin_name = $error_file_directory;
1510 }
1511 }
1512
1513 // Get theme name.
1514
1515 if ( 'theme' === $error_source_key && ! empty( $error_file_directory ) ) {
1516 $theme = wp_get_theme( $error_file_directory );
1517 if ( $theme->exists() ) {
1518 $error_source_theme_dir = $error_file_directory;
1519 $error_source_theme_name = $theme->get( 'Name' );
1520 $error_source_theme_uri = $theme->get( 'ThemeURI' );
1521 } else {
1522 $error_source_theme_dir = $error_file_directory;
1523 $error_source_theme_name = $error_file_directory;
1524 }
1525 }
1526
1527 }
1528
1529 } else {
1530
1531 // $error = __( 'No error message specified...', 'debug-log-manager' );
1532 $error = $line; // Raw log entry
1533
1534 }
1535
1536 // Stopgap measure to prevent fatal error in strpos() beneath, which expects $error to be a string
1537 if ( is_array( $error ) ) {
1538 $error = maybe_serialize( $error );
1539 }
1540
1541 if ( false !== strpos( $error, '[DLM JS]' ) ) {
1542 $error_type = __( 'JavaScript', 'debug-log-manager' );
1543 $error_details = str_replace( '[DLM JS] ', '', $error );
1544 } elseif ( false !== strpos( $error, 'JavaScript Error' ) ) {
1545 $error_type = __( 'JavaScript', 'debug-log-manager' );
1546 $error_details = str_replace( 'JavaScript Error: ', '', $error );
1547 } elseif ( ( false !== strpos( $error, 'PHP Fatal' ) )
1548 || ( false !== strpos( $error, 'FATAL' ) )
1549 || ( false !== strpos( $error, 'E_ERROR' ) ) )
1550 {
1551 $error_type = __( 'PHP Fatal', 'debug-log-manager' );
1552 $error_details = str_replace( "PHP Fatal error: ", "", $error );
1553 $error_details = str_replace( "PHP Fatal: ", "", $error_details );
1554 $error_details = str_replace( "FATAL ", "", $error_details );
1555 $error_details = str_replace( "E_ERROR: ", "", $error_details );
1556 } elseif ( ( false !== strpos( $error, 'PHP Warning' ) )
1557 || ( false !== strpos( $error, 'E_WARNING' ) ) )
1558 {
1559 $error_type = __( 'PHP Warning', 'debug-log-manager' );
1560 $error_details = str_replace( "PHP Warning: ", "", $error );
1561 $error_details = str_replace( "E_WARNING: ", "", $error_details );
1562 } elseif ( ( false !== strpos( $error, 'PHP Notice' ) )
1563 || ( false !== strpos( $error, 'E_NOTICE' ) ) )
1564 {
1565 $error_type = __( 'PHP Notice', 'debug-log-manager' );
1566 $error_details = str_replace( "PHP Notice: ", "", $error );
1567 $error_details = str_replace( "E_NOTICE: ", "", $error_details );
1568 } elseif ( false !== strpos( $error, 'PHP Deprecated' ) ) {
1569 $error_type = __( 'PHP Deprecated', 'debug-log-manager' );
1570 $error_details = str_replace( "PHP Deprecated: ", "", $error );
1571 } elseif ( ( false !== strpos( $error, 'PHP Parse' ) )
1572 || ( false !== strpos( $error, 'E_PARSE' ) ) )
1573 {
1574 $error_type = __( 'PHP Parse', 'debug-log-manager' );
1575 $error_details = str_replace( "PHP Parse error: ", "", $error );
1576 $error_details = str_replace( "E_PARSE: ", "", $error_details );
1577 } elseif ( false !== strpos( $error, 'EXCEPTION:' ) ) {
1578 $error_type = __( 'PHP Exception', 'debug-log-manager' );
1579 $error_details = str_replace( "EXCEPTION: ", "", $error );
1580 } elseif ( false !== strpos( $error, 'WordPress database error' ) ) {
1581 $error_type = __( 'Database', 'debug-log-manager' );
1582 $error_details = str_replace( "WordPress database error ", "", $error );
1583 } else {
1584 $error_type = __( 'Other', 'debug-log-manager' );
1585 $error_details = $error;
1586 if ( $this->is_json( $error_details ) ) {
1587 // For JSON string in error message, originally added via error_log( json_encode( $variable ) )
1588 // This will output said JSON string as well-formated array in the log entries table
1589 $error_details = '<pre>' . print_r( json_decode( $error_details, true ), true ) . '</pre>';
1590 }
1591 }
1592
1593 // 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.
1594
1595 if ( ! empty( $error_source_key ) ) {
1596 if ( 'core' === $error_source_key ) {
1597 $file_viewer_url = 'https://github.com/WordPress/wordpress-develop/blob/' . $wp_version . '/src/' . $error_file_path_for_url . '#L' . $error_file_line;
1598 $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_final . '<span class="dashicons dashicons-visibility offset-down"></span></a><br />' . __( 'Line', 'debug-log-manager' ) . ': ' . $error_file_line;
1599 } elseif ( 'theme' === $error_source_key ) {
1600 if ( ! empty( $error_source_theme_uri ) ) {
1601 $theme_source_name = '<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>';
1602 } else {
1603 $theme_source_name = $error_source_theme_name;
1604 }
1605 if ( ! defined( 'DISALLOW_FILE_EDIT' ) || ( false === constant( 'DISALLOW_FILE_EDIT' ) ) ) {
1606 if ( ! empty( $error_source_theme_dir ) ) {
1607 $file_viewer_url = get_admin_url() . 'theme-editor.php?file=' . urlencode( str_replace( '/' . $error_source_theme_dir . '/', '', $error_file_path ) ) . '&theme=' . $error_source_theme_dir;
1608 $file_line = __( 'File', 'debug-log-manager' ) . ': <a href="' . $file_viewer_url . '" target="_blank" class="error-source-link">' . $error_file_path_final . '<span class="dashicons dashicons-visibility offset-down"></span></a>';
1609 } else {
1610 $file_line = __( 'File', 'debug-log-manager' ) . ': ' . $error_file_path_final;
1611 }
1612 $error_details = '<span class="error-details">' . $error_details . '</span><hr />' . $error_source . ': ' . $theme_source_name . '<br />' . $file_line . '<br />' . __( 'Line', 'debug-log-manager' ) . ': ' . $error_file_line;
1613 }
1614 if ( defined( 'DISALLOW_FILE_EDIT' ) && ( true === constant( 'DISALLOW_FILE_EDIT' ) ) ) {
1615 $error_details = '<span class="error-details">' . $error_details . '</span><hr />' . $error_source . ': ' . $theme_source_name . '<br />' . __( 'File', 'debug-log-manager' ) . ': ' . $error_file_path_final . '<br />' . __( 'Line', 'debug-log-manager' ) . ': ' . $error_file_line;
1616 }
1617 } elseif ( 'plugin' === $error_source_key ) {
1618 if ( ! empty( $error_source_plugin_uri ) ) {
1619 $plugin_source_name = '<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>';
1620 } else {
1621 $plugin_source_name = $error_source_plugin_name;
1622 }
1623 if ( ! defined( 'DISALLOW_FILE_EDIT' ) || ( false === constant( 'DISALLOW_FILE_EDIT' ) ) ) {
1624 if ( ! empty( $error_source_plugin_path_file ) ) {
1625 $file_viewer_url = get_admin_url() . 'plugin-editor.php?file=' . urlencode( substr( $error_file_path, 1 ) ) . '&plugin=' . urlencode( $error_source_plugin_path_file );
1626 $file_line = __( 'File', 'debug-log-manager' ) . ': <a href="' . $file_viewer_url . '" target="_blank" class="error-source-link">' . $error_file_path_final . '<span class="dashicons dashicons-visibility offset-down"></span></a>';
1627 } else {
1628 $file_line = __( 'File', 'debug-log-manager' ) . ': ' . $error_file_path_final;
1629 }
1630 $error_details = '<span class="error-details">' . $error_details . '</span><hr />' . $error_source . ': ' . $plugin_source_name . '<br />' . $file_line . '<br />' . __( 'Line', 'debug-log-manager' ) . ': ' . $error_file_line;
1631 }
1632 if ( defined( 'DISALLOW_FILE_EDIT' ) && ( true === constant( 'DISALLOW_FILE_EDIT' ) ) ) {
1633 $error_details = '<span class="error-details">' . $error_details . '</span><hr />' . $error_source . ': ' . $plugin_source_name . '<br />' . __( 'File', 'debug-log-manager' ) . ': ' . $error_file_path_final . '<br />' . __( 'Line', 'debug-log-manager' ) . ': ' . $error_file_line;
1634 }
1635 }
1636 }
1637
1638 // https://www.php.net/manual/en/function.array-search.php#120784
1639 if ( array_search( trim( $error_details ), array_column( $errors_master_list, 'details' ) ) === false ) {
1640
1641 $errors_master_list[] = array(
1642 'type' => $error_type,
1643 'details' => trim( $error_details ),
1644 'occurrences' => array( $timestamp ),
1645 );
1646
1647 } else {
1648
1649 $error_position = array_search( trim( $error_details ), array_column( $errors_master_list, 'details' ) ); // integer
1650
1651 array_push( $errors_master_list[$error_position]['occurrences'], $timestamp );
1652
1653 }
1654
1655 }
1656
1657 return json_encode( $errors_master_list );
1658
1659 }
1660
1661 /**
1662 * Get the latest entries for auto-refresh feature
1663 *
1664 * @since 1.0.0
1665 */
1666 public function get_latest_entries() {
1667
1668 if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {
1669
1670 if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
1671
1672 $errors_master_list = json_decode( $this->get_processed_entries(), true );
1673
1674 $n = 1;
1675 $entries = array();
1676
1677 foreach ( $errors_master_list as $error ) {
1678
1679 if ( function_exists( 'wp_date' ) ) {
1680 $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
1681 } else {
1682 $localized_timestamp = date_i18n( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) );
1683 }
1684
1685 $occurrence_count = count( $error['occurrences'] );
1686
1687 $entry = array(
1688 $n,
1689 $error['type'],
1690 $error['details'],
1691 $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>',
1692 );
1693
1694 $entries[] = $entry;
1695
1696 $n++;
1697
1698 }
1699
1700 $data = array(
1701 'entries' => $entries,
1702 );
1703
1704 }
1705
1706 } else {
1707
1708 $data = array();
1709
1710 }
1711
1712 echo json_encode( $data );
1713
1714 }
1715
1716 /**
1717 * Get debug log in data table format
1718 *
1719 * @since 1.0.0
1720 */
1721 public function get_entries_datatable() {
1722
1723 ?>
1724 <div>
1725 <select id="errorTypeFilter" class="dlm-error-type-filter">
1726 <option value=""><?php esc_html_e( 'All Types', 'debug-log-manager' ); ?></option>
1727 <option value="<?php esc_attr_e( 'PHP Fatal', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Fatal', 'debug-log-manager' ); ?></option>
1728 <option value="<?php esc_attr_e( 'PHP Warning', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Warning', 'debug-log-manager' ); ?></option>
1729 <option value="<?php esc_attr_e( 'PHP Notice', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Notice', 'debug-log-manager' ); ?></option>
1730 <option value="<?php esc_attr_e( 'PHP Deprecated', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Deprecated', 'debug-log-manager' ); ?></option>
1731 <option value="<?php esc_attr_e( 'PHP Parse', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Parse', 'debug-log-manager' ); ?></option>
1732 <option value="<?php esc_attr_e( 'PHP Exception', 'debug-log-manager' ); ?>"><?php esc_html_e( 'PHP Exception', 'debug-log-manager' ); ?></option>
1733 <option value="<?php esc_attr_e( 'Database', 'debug-log-manager' ); ?>"><?php esc_html_e( 'Database', 'debug-log-manager' ); ?></option>
1734 <option value="<?php esc_attr_e( 'JavaScript', 'debug-log-manager' ); ?>"><?php esc_html_e( 'JavaScript', 'debug-log-manager' ); ?></option>
1735 <option value="<?php esc_attr_e( 'Other', 'debug-log-manager' ); ?>"><?php esc_html_e( 'Other', 'debug-log-manager' ); ?></option>
1736 </select>
1737 </div>
1738 <table id="debug-log" class="wp-list-table widefat striped">
1739 <thead>
1740 <tr>
1741 <th class="dlm-entry-no">#</th>
1742 <th class="dlm-entry-type"><?php esc_html_e( 'Type', 'debug-log-manager' ); ?></th>
1743 <th class="dlm-entry-details"><?php esc_html_e( 'Details', 'debug-log-manager' ); ?></th>
1744 <th class="dlm-entry-datetime"><?php esc_html_e( 'Last Occurrence', 'debug-log-manager' ); ?></th>
1745 </tr>
1746 </thead>
1747 <tbody>
1748 <?php
1749
1750 $errors_master_list = json_decode( $this->get_processed_entries(), true );
1751
1752 $n = 1;
1753
1754 foreach ( $errors_master_list as $error ) {
1755
1756 if ( function_exists( 'wp_date' ) ) {
1757 $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
1758 } else {
1759 $localized_timestamp = date_i18n( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) );
1760 }
1761
1762 $occurrence_count = count( $error['occurrences'] );
1763 ?>
1764
1765 <tr>
1766 <td class="dlm-entry-no"><?php echo esc_html( $n ); ?></td>
1767 <td class="dlm-entry-type"><?php echo esc_html( $error['type'] ); ?></td>
1768 <td class="dlm-entry-details"><?php echo wp_kses( $error['details'], 'post' ); ?></td>
1769 <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>
1770 </tr>
1771
1772 <?php
1773 $n++;
1774
1775 }
1776
1777 ?>
1778 </tbody>
1779 </table>
1780 <?php
1781
1782 }
1783
1784 /**
1785 * Get entries for dashboard widget
1786 *
1787 * @since 1.8.0
1788 */
1789 public function get_dashboard_widget_entries() {
1790
1791 $errors_master_list = json_decode( $this->get_processed_entries(), true );
1792
1793 $n = 1;
1794 $entries_to_show = 5;
1795
1796 ?>
1797 <style>
1798
1799 #debug_log_manager_widget.postbox .inside {
1800 margin: 0;
1801 padding: 0;
1802 }
1803
1804 .dlm-dashboard-widget-entry {
1805 padding: 12px;
1806 border-bottom: 1px solid #e6e7e7;
1807 word-wrap: break-word; /* All browsers since IE 5.5+ */
1808 overflow-wrap: break-word; /* Renamed property in CSS3 draft spec */
1809 }
1810
1811 .dlm-dashboard-widget-entry:nth-child(odd) {
1812 background-color: #f6f7f7;
1813 }
1814
1815 .dlm-dashboard-widget-entry-message a.error-source-link {
1816 color: #50575e;
1817 text-decoration: none;
1818 }
1819
1820 .dlm-dashboard-widget-entry-message a.error-source-link span {
1821 position: relative;
1822 margin-left: 2px;
1823 color: #777;
1824 font-size: 18px;
1825 width: 18px;
1826 height: 18px;
1827 transition: .25s;
1828 text-decoration: none;
1829 }
1830
1831 .dlm-dashboard-widget-entry-message a.error-source-link span.offset-up {
1832 top: -1px;
1833 }
1834
1835 .dlm-dashboard-widget-entry-message a.error-source-link span.offset-down {
1836 top: 1px;
1837 }
1838
1839 .dlm-dashboard-widget-entry-message a.error-source-link:hover {
1840 color: #2271b1;
1841 text-decoration: underline;
1842 }
1843
1844 #debug-log .dlm-entry-details a.error-source-link:hover span {
1845 color: #2271b1;
1846 text-decoration: none;
1847 }
1848
1849 .dlm-dashboard-widget-entry-meta {
1850 display: flex;
1851 }
1852
1853 .dlm-dashboard-widget-entry-type {
1854 margin-right: 4px;
1855 font-weight: 600;
1856 }
1857
1858 .dlm-dashboard-widget-footer {
1859 display: flex;
1860 justify-content: space-between;
1861 align-items: center;
1862 width: 100%;
1863 box-sizing: border-box;
1864 padding: 12px;
1865 background-color: #f6f7f7;
1866 }
1867
1868 </style>
1869 <div class="dlm-dashboard-widget-entries">
1870 <?php
1871
1872 foreach ( $errors_master_list as $error ) {
1873
1874 if ( $n <= $entries_to_show ) {
1875
1876 if ( function_exists( 'wp_date' ) ) {
1877 $localized_timestamp = wp_date( 'M j, Y, H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
1878 } else {
1879 $localized_timestamp = date_i18n( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) );
1880 }
1881
1882 $occurrence_count = count( $error['occurrences'] );
1883
1884 ?>
1885 <div class="dlm-dashboard-widget-entry">
1886 <div class="dlm-dashboard-widget-entry-meta">
1887 <div class="dlm-dashboard-widget-entry-type">
1888 <?php echo esc_html( $error['type'] ); ?>
1889 </div>
1890 <div class="dlm-dashboard-widget-entry-datetime">
1891 | <?php echo esc_html( $localized_timestamp ); ?>
1892 </div>
1893 </div>
1894 <div class="dlm-dashboard-widget-entry-message">
1895 <?php echo wp_kses( $error['details'], 'post' ); ?>
1896 </div>
1897 </div>
1898 <?php
1899
1900 }
1901
1902 $n++;
1903
1904 }
1905
1906 ?>
1907 </div>
1908 <div class="dlm-dashboard-widget-footer">
1909 <div class="dlm-dashboard-widget-logging-status">
1910 <?php echo wp_kses_post( $this->get_status() ); ?>
1911 </div>
1912 <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>
1913 </div>
1914 <?php
1915
1916 }
1917
1918 /**
1919 * Clear log file
1920 *
1921 * @since 1.0.0
1922 */
1923 public function clear_log() {
1924
1925 if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {
1926
1927 if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
1928
1929 $debug_log_file_path = get_option( 'debug_log_manager_file_path' );
1930
1931 file_put_contents( $debug_log_file_path, '' );
1932
1933 echo true;
1934
1935 }
1936
1937 }
1938
1939 }
1940
1941 /**
1942 * Disable WP core's plugin/theme editor
1943 *
1944 * @since 2.0.0
1945 */
1946 public function disable_wp_file_editor() {
1947
1948 if ( isset( $_REQUEST ) && current_user_can( 'manage_options' ) ) {
1949 if ( wp_verify_nonce( sanitize_text_field( $_REQUEST['nonce'] ), 'dlm-app' . get_current_user_id() ) ) {
1950
1951 $options = array(
1952 'add' => true, // Add the config if missing.
1953 'raw' => true, // Display value in raw format without quotes.
1954 'normalize' => false, // Normalize config output using WP Coding Standards.
1955 );
1956
1957 $this->wp_config->update( 'constant', 'DISALLOW_FILE_EDIT', 'true', $options );
1958
1959 // Prepare entries from the debug log for the data table
1960
1961 $errors_master_list = json_decode( $this->get_processed_entries(), true );
1962
1963 $n = 1;
1964 $entries = array();
1965
1966 foreach ( $errors_master_list as $error ) {
1967
1968 if ( function_exists( 'wp_date' ) ) {
1969 $localized_timestamp = wp_date( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) ); // last occurrence
1970 } else {
1971 $localized_timestamp = date_i18n( 'M j, Y - H:i:s', strtotime( $error['occurrences'][0] ) );
1972 }
1973
1974 $occurrence_count = count( $error['occurrences'] );
1975
1976 $entry = array(
1977 $n,
1978 $error['type'],
1979 $error['details'],
1980 $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>',
1981 );
1982
1983 $entries[] = $entry;
1984
1985 $n++;
1986
1987 }
1988
1989 // Assemble data to return
1990
1991 $data = array(
1992 'status' => 'disabled', // Plugin/theme editor
1993 'entries' => $entries, // To parse data table with unlinked plugin/theme file paths
1994 );
1995
1996 echo json_encode( $data );
1997
1998 }
1999 }
2000
2001 }
2002
2003 /**
2004 * Whether JS error logging is enabled.
2005 *
2006 * @since 2.5.1
2007 * @return bool
2008 */
2009 private function is_js_error_logging_enabled() {
2010
2011 $default_value = array(
2012 'status' => 'disabled',
2013 'on' => date( 'Y-m-d H:i:s' ),
2014 );
2015
2016 $log_info = get_option( 'debug_log_manager', $default_value );
2017 $js_error_logging_status = get_option( 'debug_log_manager_js_error_logging', 'enabled' );
2018
2019 return ( 'enabled' === $log_info['status'] && 'enabled' === $js_error_logging_status );
2020
2021 }
2022
2023 /**
2024 * Sanitize a field before writing to debug.log.
2025 *
2026 * @since 2.5.1
2027 * @param mixed $value Raw value.
2028 * @param int $max_length Maximum allowed length.
2029 * @return string
2030 */
2031 private function sanitize_log_field( $value, $max_length = 500 ) {
2032
2033 if ( ! is_string( $value ) ) {
2034 $value = (string) $value;
2035 }
2036
2037 $filtered = sanitize_text_field( $value );
2038
2039 // Strip ASCII control characters and DEL.
2040 $filtered = preg_replace( '/[\x00-\x1F\x7F]/u', '', $filtered );
2041
2042 // Strip Unicode line and paragraph separators.
2043 $filtered = preg_replace( '/[\x{2028}\x{2029}]/u', '', $filtered );
2044
2045 // Collapse whitespace.
2046 $filtered = preg_replace( '/\s+/u', ' ', $filtered );
2047 $filtered = trim( $filtered );
2048
2049 if ( function_exists( 'mb_substr' ) ) {
2050 return mb_substr( $filtered, 0, $max_length );
2051 }
2052
2053 return substr( $filtered, 0, $max_length );
2054
2055 }
2056
2057 /**
2058 * Validate Origin or Referer matches this site.
2059 *
2060 * @since 2.5.1
2061 * @return bool
2062 */
2063 private function validate_request_origin() {
2064
2065 /**
2066 * Skip same-site Origin/Referer validation for JS error logging.
2067 *
2068 * @since 2.5.1
2069 * @param bool $skip Whether to skip validation.
2070 */
2071 if ( apply_filters( 'dlm_js_error_log_skip_origin_check', false ) ) {
2072 return true;
2073 }
2074
2075 $site_host = wp_parse_url( home_url(), PHP_URL_HOST );
2076
2077 if ( empty( $site_host ) ) {
2078 return false;
2079 }
2080
2081 $request_host = '';
2082
2083 if ( ! empty( $_SERVER['HTTP_ORIGIN'] ) ) {
2084 $request_host = wp_parse_url( esc_url_raw( wp_unslash( $_SERVER['HTTP_ORIGIN'] ) ), PHP_URL_HOST );
2085 } elseif ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
2086 $request_host = wp_parse_url( esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ), PHP_URL_HOST );
2087 } else {
2088 return false;
2089 }
2090
2091 if ( empty( $request_host ) ) {
2092 return false;
2093 }
2094
2095 return ( strtolower( $request_host ) === strtolower( $site_host ) );
2096
2097 }
2098
2099 /**
2100 * Check rate limit for JS error logging.
2101 *
2102 * @since 2.5.1
2103 * @return bool True if within limit, false if exceeded.
2104 */
2105 private function check_js_error_rate_limit() {
2106
2107 $ip = '';
2108 if ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
2109 $ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
2110 }
2111
2112 $ua = '';
2113 if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
2114 $ua = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
2115 }
2116
2117 $key = 'dlm_js_log_' . md5( $ip . $ua );
2118 $count = (int) get_transient( $key );
2119
2120 if ( $count >= self::JS_LOG_RATE_LIMIT ) {
2121 return false;
2122 }
2123
2124 set_transient( $key, $count + 1, self::JS_LOG_RATE_WINDOW );
2125
2126 return true;
2127
2128 }
2129
2130 /**
2131 * Log javascript errors
2132 *
2133 * @since 1.4.0
2134 */
2135 public function log_js_errors() {
2136
2137 if ( ! $this->is_js_error_logging_enabled() ) {
2138 wp_die();
2139 }
2140
2141 // Since we are using XHR for the js error logging, JSON data comes in via php://input.
2142 $request = json_decode( file_get_contents( 'php://input' ), true );
2143
2144 if ( ! is_array( $request ) ) {
2145 wp_die();
2146 }
2147
2148 // Verify error content and nonce and then log the JS error.
2149 // Source: https://plugins.svn.wordpress.org/lh-javascript-error-log/trunk/lh-javascript-error-log.php
2150 if ( ! isset( $request['message'], $request['script'], $request['lineNo'], $request['columnNo'] ) || empty( $request['nonce'] ) || ! wp_verify_nonce( $request['nonce'], DLM__SLUG ) ) {
2151 wp_die();
2152 }
2153
2154 $request_type = isset( $request['type'] ) ? sanitize_text_field( $request['type'] ) : '';
2155
2156 if ( 'wp-admin' === $request_type ) {
2157 if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
2158 wp_die();
2159 }
2160 } elseif ( ! is_user_logged_in() ) {
2161 if ( ! $this->validate_request_origin() ) {
2162 wp_die();
2163 }
2164 }
2165
2166 if ( ! $this->check_js_error_rate_limit() ) {
2167 wp_die( '', '', 429 );
2168 }
2169
2170 $message = $this->sanitize_log_field( $request['message'] );
2171 $script = $this->sanitize_log_field( $request['script'] );
2172 $line_number = $this->sanitize_log_field( $request['lineNo'], 20 );
2173 $column_number = $this->sanitize_log_field( $request['columnNo'], 20 );
2174 $page_url = isset( $request['pageUrl'] ) ? $this->sanitize_log_field( $request['pageUrl'] ) : '';
2175
2176 error_log( self::JS_LOG_PREFIX . $message . ' in ' . $script . ' on line ' . $line_number . ' column ' . $column_number . ' at ' . get_site_url() . $page_url );
2177
2178 wp_die();
2179
2180 }
2181
2182 /**
2183 * Check if a string is valid JSON
2184 *
2185 * @link https://stackoverflow.com/a/6041773
2186 * @since 2.1.0
2187 */
2188 public function is_json( $string ) {
2189
2190 json_decode( $string );
2191 return json_last_error() === JSON_ERROR_NONE;
2192
2193 }
2194
2195 }