collector->get_data(); if ( empty( $data->errors ) && empty( $data->silenced ) && empty( $data->suppressed ) ) { return; } $labels = [ 'errors' => array( 'warning' => _x( 'Warning', 'PHP error level', 'query-monitor' ), 'notice' => _x( 'Notice', 'PHP error level', 'query-monitor' ), 'strict' => _x( 'Strict', 'PHP error level', 'query-monitor' ), 'deprecated' => _x( 'Deprecated', 'PHP error level', 'query-monitor' ), ), 'suppressed' => array( 'warning' => _x( 'Warning (Suppressed)', 'Suppressed PHP error level', 'query-monitor' ), 'notice' => _x( 'Notice (Suppressed)', 'Suppressed PHP error level', 'query-monitor' ), 'strict' => _x( 'Strict (Suppressed)', 'Suppressed PHP error level', 'query-monitor' ), 'deprecated' => _x( 'Deprecated (Suppressed)', 'Suppressed PHP error level', 'query-monitor' ), ), 'silenced' => array( 'warning' => _x( 'Warning (Silenced)', 'Silenced PHP error level', 'query-monitor' ), 'notice' => _x( 'Notice (Silenced)', 'Silenced PHP error level', 'query-monitor' ), 'strict' => _x( 'Strict (Silenced)', 'Silenced PHP error level', 'query-monitor' ), 'deprecated' => _x( 'Deprecated (Silenced)', 'Silenced PHP error level', 'query-monitor' ), ), ]; $components = $data->components; $count = 0; usort( $components, '\QM_Component::sort' ); $this->before_tabular_output(); echo ''; echo ''; echo ''; echo $this->build_filter( 'type', $labels['errors'], __( 'Level', 'query-monitor' ) ); // WPCS: XSS ok. echo ''; echo '' . esc_html__( 'Message', 'query-monitor' ) . ''; echo '' . esc_html__( 'Location', 'query-monitor' ) . ''; echo '' . esc_html__( 'Count', 'query-monitor' ) . ''; echo ''; $values = wp_list_pluck( $components, 'name' ); echo $this->build_filter( 'component', $values, __( 'Component', 'query-monitor' ) ); // WPCS: XSS ok. echo ''; echo ''; echo ''; echo ''; foreach ( $labels as $error_group => $error_types ) { foreach ( $error_types as $type => $title ) { if ( ! isset( $data->{$error_group}[ $type ] ) ) { continue; } foreach ( $data->{$error_group}[ $type ] as $error_key => $error ) { $count += $error['calls']; $row_attr = array(); $row_attr['data-qm-type'] = $type; $row_attr['data-qm-key'] = $error_key; $row_attr['data-qm-count'] = $error['calls']; if ( $error['component'] ) { $component = $error['component']; $row_attr['data-qm-component'] = $component->name; if ( ! $component->is_core() ) { $row_attr['data-qm-component'] .= ' non-core'; } } $attr = ''; foreach ( $row_attr as $a => $v ) { $attr .= ' ' . $a . '="' . esc_attr( $v ) . '"'; } $is_warning = ( 'errors' === $error_group && 'warning' === $type ); if ( $is_warning ) { $class = 'qm-warn'; } else { $class = ''; } echo ''; // WPCS: XSS ok. echo ''; if ( $is_warning ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo QueryMonitor::icon( 'warning' ); } else { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo QueryMonitor::icon( 'blank' ); } echo esc_html( $title ); echo ''; echo '' . esc_html( $error['message'] ) . ''; $stack = array(); if ( $error['filtered_trace'] ) { $filtered_trace = $error['filtered_trace']; // debug_backtrace() (used within QM_Backtrace) doesn't like being used within an error handler so // we need to handle its somewhat unreliable stack trace items. // https://bugs.php.net/bug.php?id=39070 // https://bugs.php.net/bug.php?id=64987 foreach ( $filtered_trace as $i => $item ) { if ( isset( $item['file'], $item['line'] ) ) { $stack[] = self::output_filename( $item['display'], $item['file'], $item['line'] ); } elseif ( 0 === $i ) { $stack[] = self::output_filename( $item['display'], $error['file'], $error['line'] ); } else { $stack[] = $item['display'] . '
' . __( 'Unknown location', 'query-monitor' ) . ''; } } } echo ''; if ( ! empty( $stack ) ) { echo self::build_toggler(); // WPCS: XSS ok; } echo '
    '; echo '
  1. '; echo self::output_filename( $error['filename'] . ':' . $error['line'], $error['file'], $error['line'], true ); // WPCS: XSS ok. echo '
  2. '; if ( ! empty( $stack ) ) { echo '
  3. ' . implode( '
  4. ', $stack ) . '
  5. '; // WPCS: XSS ok. } echo '
'; echo '' . esc_html( number_format_i18n( $error['calls'] ) ) . ''; if ( ! empty( $component ) ) { echo '' . esc_html( $component->name ) . ''; } else { echo '' . esc_html__( 'Unknown', 'query-monitor' ) . ''; } echo ''; } } } echo ''; echo ''; echo ''; echo ''; printf( /* translators: %s: Number of PHP errors */ esc_html( _nx( 'Total: %s', 'Total: %s', $count, 'PHP error count', 'query-monitor' ) ), '' . esc_html( number_format_i18n( $count ) ) . '' ); echo ''; echo ''; echo ''; $this->after_tabular_output(); } /** * @param array $class * @return array */ public function admin_class( array $class ) { /** @var QM_Data_PHP_Errors $data */ $data = $this->collector->get_data(); if ( ! empty( $data->errors ) ) { foreach ( $data->errors as $type => $errors ) { $class[] = 'qm-' . $type; } } return $class; } /** * @param array $menu * @return array */ public function admin_menu( array $menu ) { /** @var QM_Data_PHP_Errors $data */ $data = $this->collector->get_data(); $menu_label = array(); $types = array( /* translators: %s: Number of deprecated PHP errors */ 'deprecated' => _nx_noop( '%s Deprecated', '%s Deprecated', 'PHP error level', 'query-monitor' ), /* translators: %s: Number of strict PHP errors */ 'strict' => _nx_noop( '%s Strict', '%s Stricts', 'PHP error level', 'query-monitor' ), /* translators: %s: Number of PHP notices */ 'notice' => _nx_noop( '%s Notice', '%s Notices', 'PHP error level', 'query-monitor' ), /* translators: %s: Number of PHP warnings */ 'warning' => _nx_noop( '%s Warning', '%s Warnings', 'PHP error level', 'query-monitor' ), ); $key = 'quiet'; $generic = false; foreach ( $types as $type => $label ) { $count = 0; $has_errors = false; if ( isset( $data->suppressed[ $type ] ) ) { $has_errors = true; $generic = true; } if ( isset( $data->silenced[ $type ] ) ) { $has_errors = true; $generic = true; } if ( isset( $data->errors[ $type ] ) ) { $has_errors = true; $key = $type; $count += (int) array_sum( array_column( $data->errors[ $type ], 'calls' ) ); } if ( ! $has_errors ) { continue; } if ( $count ) { $label = sprintf( translate_nooped_plural( $label, $count, 'query-monitor' ), number_format_i18n( $count ) ); $menu_label[] = $label; } } if ( empty( $menu_label ) && ! $generic ) { return $menu; } /* translators: %s: List of PHP error types */ $title = __( 'PHP Errors (%s)', 'query-monitor' ); /* translators: used between list items, there is a space after the comma */ $sep = __( ', ', 'query-monitor' ); if ( count( $menu_label ) ) { $title = sprintf( $title, implode( $sep, array_reverse( $menu_label ) ) ); } else { $title = __( 'PHP Errors', 'query-monitor' ); } $menu[ $this->collector->id() ] = $this->menu( array( 'id' => "query-monitor-{$key}s", 'title' => $title, ) ); return $menu; } /** * @param array $menu * @return array */ public function panel_menu( array $menu ) { if ( ! isset( $menu[ $this->collector->id() ] ) ) { return $menu; } /** @var QM_Data_PHP_Errors $data */ $data = $this->collector->get_data(); $count = 0; $types = array( 'suppressed', 'silenced', 'errors', ); foreach ( $types as $type ) { if ( ! empty( $data->{$type} ) ) { foreach ( $data->{$type} as $errors ) { $count += array_sum( array_column( $errors, 'calls' ) ); } } } $menu[ $this->collector->id() ]['title'] = esc_html( sprintf( /* translators: %s: Number of errors */ __( 'PHP Errors (%s)', 'query-monitor' ), number_format_i18n( $count ) ) ); return $menu; } } /** * @param array $output * @param QM_Collectors $collectors * @return array */ function register_qm_output_html_php_errors( array $output, QM_Collectors $collectors ) { $collector = QM_Collectors::get( 'php_errors' ); if ( $collector ) { $output['php_errors'] = new QM_Output_Html_PHP_Errors( $collector ); } return $output; } add_filter( 'qm/outputter/html', 'register_qm_output_html_php_errors', 110, 2 );