: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$email = compact( 'to', 'subject', 'body', 'headers' );
* Filters the email sent following an automatic background update for plugins and themes.
* Array of email arguments that will be passed to wp_mail().
* @type string $to The email recipient. An array of emails
* can be returned, as handled by wp_mail().
* @type string $subject The email's subject.
* @type string $body The email message body.
* @type string $headers Any email headers, defaults to no headers.
* @param string $type The type of email being sent. Can be one of 'success', 'fail', 'mixed'.
* @param array $successful_updates A list of updates that succeeded.
* @param array $failed_updates A list of updates that failed.
$email = apply_filters( 'auto_plugin_theme_update_email', $email, $type, $successful_updates, $failed_updates );
$result = wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] );
update_option( 'auto_plugin_theme_update_emails', $past_failure_emails );
* Prepares and sends an email of a full log of background update results, useful for debugging and geekery.
protected function send_debug_email() {
foreach ( $this->update_results as $type => $updates ) {
$update_count += count( $updates );
/* translators: %s: Network home URL. */
$body[] = sprintf( __( 'WordPress site: %s' ), network_home_url( '/' ) );
if ( isset( $this->update_results['core'] ) ) {
$result = $this->update_results['core'][0];
if ( $result->result && ! is_wp_error( $result->result ) ) {
/* translators: %s: WordPress version. */
$body[] = sprintf( __( 'SUCCESS: WordPress was successfully updated to %s' ), $result->name );
/* translators: %s: WordPress version. */
$body[] = sprintf( __( 'FAILED: WordPress failed to update to %s' ), $result->name );
// Plugins, Themes, Translations.
foreach ( array( 'plugin', 'theme', 'translation' ) as $type ) {
if ( ! isset( $this->update_results[ $type ] ) ) {
$success_items = wp_list_filter( $this->update_results[ $type ], array( 'result' => true ) );
'plugin' => __( 'The following plugins were successfully updated:' ),
'theme' => __( 'The following themes were successfully updated:' ),
'translation' => __( 'The following translations were successfully updated:' ),
$body[] = $messages[ $type ];
foreach ( wp_list_pluck( $success_items, 'name' ) as $name ) {
/* translators: %s: Name of plugin / theme / translation. */
$body[] = ' * ' . sprintf( __( 'SUCCESS: %s' ), $name );
if ( $success_items !== $this->update_results[ $type ] ) {
'plugin' => __( 'The following plugins failed to update:' ),
'theme' => __( 'The following themes failed to update:' ),
'translation' => __( 'The following translations failed to update:' ),
$body[] = $messages[ $type ];
foreach ( $this->update_results[ $type ] as $item ) {
if ( ! $item->result || is_wp_error( $item->result ) ) {
/* translators: %s: Name of plugin / theme / translation. */
$body[] = ' * ' . sprintf( __( 'FAILED: %s' ), $item->name );
if ( '' !== get_bloginfo( 'name' ) ) {
$site_title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
$site_title = parse_url( home_url(), PHP_URL_HOST );
This debugging email is sent when you are using a development version of WordPress.
If you think these failures might be due to a bug in WordPress, could you report it?
* Open a thread in the support forums: https://wordpress.org/support/forum/alphabeta
* Or, if you're comfortable writing a bug report: https://core.trac.wordpress.org/
Thanks! -- The WordPress Team"
/* translators: Background update failed notification email subject. %s: Site title. */
$subject = sprintf( __( '[%s] Background Update Failed' ), $site_title );
/* translators: Background update finished notification email subject. %s: Site title. */
$subject = sprintf( __( '[%s] Background Update Finished' ), $site_title );
foreach ( array( 'core', 'plugin', 'theme', 'translation' ) as $type ) {
if ( ! isset( $this->update_results[ $type ] ) ) {
foreach ( $this->update_results[ $type ] as $update ) {
$body[] = str_repeat( '-', strlen( $update->name ) );
foreach ( $update->messages as $message ) {
$body[] = ' ' . html_entity_decode( str_replace( '…', '...', $message ) );
if ( is_wp_error( $update->result ) ) {
$results = array( 'update' => $update->result );
// If we rolled back, we want to know an error that occurred then too.
if ( 'rollback_was_required' === $update->result->get_error_code() ) {
$results = (array) $update->result->get_error_data();
foreach ( $results as $result_type => $result ) {
if ( ! is_wp_error( $result ) ) {
if ( 'rollback' === $result_type ) {
/* translators: 1: Error code, 2: Error message. */
$body[] = ' ' . sprintf( __( 'Rollback Error: [%1$s] %2$s' ), $result->get_error_code(), $result->get_error_message() );
/* translators: 1: Error code, 2: Error message. */
$body[] = ' ' . sprintf( __( 'Error: [%1$s] %2$s' ), $result->get_error_code(), $result->get_error_message() );
if ( $result->get_error_data() ) {
$body[] = ' ' . implode( ', ', (array) $result->get_error_data() );
'to' => get_site_option( 'admin_email' ),
'body' => implode( "\n", $body ),
* Filters the debug email that can be sent following an automatic
* background core update.
* Array of email arguments that will be passed to wp_mail().
* @type string $to The email recipient. An array of emails
* can be returned, as handled by wp_mail().
* @type string $subject Email subject.
* @type string $body Email message body.
* @type string $headers Any email headers. Default empty.
* @param int $failures The number of failures encountered while upgrading.
* @param mixed $results The results of all attempted updates.
$email = apply_filters( 'automatic_updates_debug_email', $email, $failures, $this->update_results );
wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] );
* Performs a loopback request to check for potential fatal errors.
* Fatal errors cannot be detected unless maintenance mode is enabled.
* @global int $upgrading The Unix timestamp marking when upgrading WordPress began.
* @return bool Whether a fatal error was detected.
protected function has_fatal_error() {
$maintenance_file = ABSPATH . '.maintenance';
if ( ! file_exists( $maintenance_file ) ) {
require $maintenance_file;
if ( ! is_int( $upgrading ) ) {
$scrape_key = md5( $upgrading );
$scrape_nonce = (string) $upgrading;
$transient = 'scrape_key_' . $scrape_key;
set_transient( $transient, $scrape_nonce, 30 );
$cookies = wp_unslash( $_COOKIE );
'wp_scrape_key' => $scrape_key,
'wp_scrape_nonce' => $scrape_nonce,
'Cache-Control' => 'no-cache',
/** This filter is documented in wp-includes/class-wp-http-streams.php */
$sslverify = apply_filters( 'https_local_ssl_verify', false );
// Include Basic auth in the loopback request.
if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) {
$headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) );
// Time to wait for loopback request to finish.
$timeout = 50; // 50 seconds.
$is_debug = WP_DEBUG && WP_DEBUG_LOG;
error_log( ' Scraping home page...' );
$needle_start = "###### wp_scraping_result_start:$scrape_key ######";
$needle_end = "###### wp_scraping_result_end:$scrape_key ######";
$url = add_query_arg( $scrape_params, home_url( '/' ) );
$response = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) );
if ( is_wp_error( $response ) ) {
error_log( 'Loopback request failed: ' . $response->get_error_message() );
// If this outputs `true` in the log, it means there were no fatal errors detected.
error_log( var_export( substr( $response['body'], strpos( $response['body'], '###### wp_scraping_result_start:' ) ), true ) );
$body = wp_remote_retrieve_body( $response );
$scrape_result_position = strpos( $body, $needle_start );
if ( false !== $scrape_result_position ) {
$error_output = substr( $body, $scrape_result_position + strlen( $needle_start ) );
$error_output = substr( $error_output, 0, strpos( $error_output, $needle_end ) );
$result = json_decode( trim( $error_output ), true );
delete_transient( $transient );
// Only fatal errors will result in a 'type' key.
return isset( $result['type'] );