likely_culprit_detector = $likely_culprit_detector; } /** * Set the title of the error page. * * @param string $title Title to use. * @return self */ public function with_title( $title ) { $this->title = $title; return $this; } /** * Set the message of the error page. * * @param string $message Message to use. * @return self */ public function with_message( $message ) { $this->message = $message; return $this; } /** * Set the message of the error page. * * @param string $link_url Link URL. * @param string $link_text Link text. * @return self */ public function with_back_link( $link_url, $link_text ) { $this->link_url = $link_url; $this->link_text = $link_text; return $this; } /** * Set the throwable of the error page. * * @param Throwable|Exception $throwable Exception or Error to use. The Throwable type does not exist in PHP 5, * which is why type is absent from the function parameter. * @throws InvalidArgumentException If $throwable is not an Exception or an Error. * @return self */ public function with_throwable( $throwable ) { // @phpstan-ignore-next-line if ( ! ( $throwable instanceof Exception || $throwable instanceof Error ) ) { throw new InvalidArgumentException( 'Parameter must be Throwable (Exception or Error).' ); } $this->throwable = $throwable; return $this; } /** * Set the response_code of the error page. * * @param int $response_code Response code to use. * @return self */ public function with_response_code( $response_code ) { $this->response_code = $response_code; return $this; } /** * Render the error page. * * This first sets the required headers and then returns the HTML to send as * output. * * @return string */ public function render() { $this->send_to_error_log(); $this->set_headers(); $text_direction = function_exists( 'is_rtl' ) && is_rtl() ? 'rtl' : 'ltr'; $styles = $this->get_styles( $text_direction ); $html = $this->get_html( $styles, $text_direction ); return $html; } /** * Send the throwable that was caught to the error log. */ private function send_to_error_log() { // Don't send to error log if fatal errors are not to be reported. $error_level = error_reporting(); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting,WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting if ( ! (bool) ( $error_level & E_ERROR ) ) { return; } if ( null !== $this->throwable ) { error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log sprintf( "%s - %s (%s) [%s]\n%s", $this->message, $this->throwable->getMessage(), $this->throwable->getCode(), get_class( $this->throwable ), $this->throwable->getTraceAsString() ) ); } } /** * Sets the required headers. * * This will only adapt the headers if they haven't yet been sent. */ private function set_headers() { if ( ! headers_sent() ) { // Define the content type for the error page. header( 'Content-Type: text/html; charset=utf-8' ); // Mark the page as a server failure so it won't get indexed. status_header( $this->response_code ); // Let the browser know this result shouldn't get cached. nocache_headers(); } } /** * Get the HTML output for the error page. * * @param string $styles CSS styles to use. * @param string $text_direction Text direction. Can be 'ltr' or 'rtl'. * @return string HTML output. */ private function get_html( $styles, $text_direction ) { $no_robots = get_option( 'blog_public' ) ? "\n" : "\n"; return <<
{$no_robots}' . wp_kses_post( $this->message ) . '
'; } /** * Render support link. * * @return string */ private function render_support_link() { return '' . wp_kses( sprintf( /* translators: %s is the AMP support forum URL. */ __( 'If you get stuck, you may want to share any details in a new topic on the plugin\'s support forum.', 'amp' ), esc_url( __( 'https://wordpress.org/support/plugin/amp/', 'amp' ) ) ), [ 'a' => [ 'href' => true, 'target' => true, 'rel' => true, ], ] ) . '
'; } /** * Render the source of the throwable. * * @return string File source data. */ private function render_source() { if ( null === $this->throwable ) { return ''; } $source = $this->likely_culprit_detector->analyze_throwable( $this->throwable ); if ( ! empty( $source['type'] ) && ! empty( $source['name'] ) ) { $name_markup = "{$source['name']}
";
switch ( $source['type'] ) {
case 'plugin':
/* translators: placeholder is the slug of the plugin */
$message = sprintf( __( 'It appears the plugin with slug %s is responsible; please contact the author.', 'amp' ), $name_markup );
break;
case 'mu-plugin':
/* translators: placeholder is the slug of the must-use plugin */
$message = sprintf( __( 'It appears the must-use plugin with slug %s is responsible; please contact the author.', 'amp' ), $name_markup );
break;
case 'theme':
/* translators: placeholder is the slug of the theme */
$message = sprintf( __( 'It appears the theme with slug %s is responsible; please contact the author.', 'amp' ), $name_markup );
break;
default:
return '';
}
return wp_kses(
"{$message}
", array_fill_keys( [ 'p', 'strong', 'code' ], [] ) ); } return ''; } /** * Render the throwable of the error page. * * The exception/error details are only rendered if both WP_DEBUG and WP_DEBUG_DISPLAY are true. * * @return string HTML describing the exception/error that was thrown. */ private function render_throwable() { if ( null === $this->throwable ) { return ''; } if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG || ! defined( 'WP_DEBUG_DISPLAY' ) || ! WP_DEBUG_DISPLAY ) { return wpautop( wp_kses_post( __( 'The exact details of the error are hidden for security reasons. To learn more about this error, enable the WordPress debugging display (by setting bothWP_DEBUG
and WP_DEBUG_DISPLAY
to true
), or look into the PHP error log. Learn more about Debugging in WordPress.', 'amp' )
)
);
}
$contents = implode(
"\n",
[
sprintf(
'%s (%s) [%s]',
esc_html( $this->throwable->getMessage() ),
esc_html( $this->throwable->getCode() ),
esc_html( get_class( $this->throwable ) )
),
sprintf(
'%s:%d',
esc_html( $this->throwable->getFile() ),
esc_html( $this->throwable->getLine() )
),
'',
sprintf(
'%s',
esc_html( $this->throwable->getTraceAsString() )
),
]
);
return "{$contents}"; } /** * Render back link. * * @return string Back link. */ private function render_back_link() { if ( empty( $this->link_text ) || empty( $this->link_url ) ) { return ''; } return sprintf( '', esc_url( $this->link_url ), esc_html( $this->link_text ) ); } /** * Get the CSS styles to use for the error page. * * @see _default_wp_die_handler() Where styles are adapted from. * * @param string $text_direction Text direction. Can be 'ltr' or 'rtl'. * @return string CSS styles to use. */ private function get_styles( $text_direction ) { $rtl_font_tweak = 'rtl' === $text_direction ? 'body { font-family: Tahoma, Arial; }' : ''; return <<