WordPress.org

Make WordPress Core


Ignore:
Timestamp:
01/09/2019 08:04:55 PM (8 months ago)
Author:
flixos90
Message:

Bootstrap/Load: Introduce fatal error recovery mechanism allowing users to still log in to their admin dashboard.

This changeset introduces a WP_Shutdown_Handler class that detects fatal errors and which extension (plugin or theme) causes them. Such an error is then recorded, and an error message is displayed. Subsequently, in certain protected areas, for example the admin, the broken extension will be paused, ensuring that the website is still usable in the respective area. The major benefit is that this mechanism allows site owners to still log in to their website, to fix the problem by either disabling the extension or solving the bug and then resuming the extension.

Extensions are only paused in certain designated areas. The frontend for example stays unaffected, as it is impossible to know what pausing the extension would cause to be missing, so it might be preferrable to clearly see that the website is temporarily not accessible instead.

The fatal error recovery is especially important in scope of encouraging the switch to a maintained PHP version, as not necessarily every WordPress extension is compatible with all PHP versions. If problems occur now, non-technical site owners that do not have immediate access to the codebase are not locked out of their site and can at least temporarily solve the problem quickly.

Websites that have custom requirements in that regard can implement their own shutdown handler by adding a shutdown-handler.php drop-in that returns the handler instance to use, which must be based on a class that inherits WP_Shutdown_Handler. That handler will then be used in place of the default one.

Websites that would like to modify specifically the error template displayed in the frontend can add a php-error.php drop-in that works similarly to the existing db-error.php drop-in.

Props afragen, bradleyt, flixos90, ocean90, schlessera, SergeyBiryukov, spacedmonkey.
Fixes #44458.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/themes.php

    r43571 r44524  
    3333        switch_theme( $theme->get_stylesheet() );
    3434        wp_redirect( admin_url( 'themes.php?activated=true' ) );
     35        exit;
     36    } elseif ( 'resume' === $_GET['action'] ) {
     37        check_admin_referer( 'resume-theme_' . $_GET['stylesheet'] );
     38        $theme = wp_get_theme( $_GET['stylesheet'] );
     39
     40        if ( ! current_user_can( 'resume_themes' ) ) {
     41            wp_die(
     42                '<h1>' . __( 'You need a higher level of permission.' ) . '</h1>' .
     43                '<p>' . __( 'Sorry, you are not allowed to resume this theme.' ) . '</p>',
     44                403
     45            );
     46        }
     47
     48        $result = resume_theme( $theme->get_stylesheet() );
     49
     50        if ( is_wp_error( $result ) ) {
     51            wp_die( $result );
     52        }
     53
     54        wp_redirect( admin_url( 'themes.php?resumed=true' ) );
    3555        exit;
    3656    } elseif ( 'delete' == $_GET['action'] ) {
     
    174194    <hr class="wp-header-end">
    175195<?php
    176 if ( ! validate_current_theme() || isset( $_GET['broken'] ) ) :
     196if ( ! validate_current_theme() || isset( $_GET['broken'] ) ) {
    177197    ?>
    178 <div id="message1" class="updated notice is-dismissible"><p><?php _e( 'The active theme is broken. Reverting to the default theme.' ); ?></p></div>
     198    <div id="message1" class="updated notice is-dismissible"><p><?php _e( 'The active theme is broken. Reverting to the default theme.' ); ?></p></div>
    179199    <?php
    180 elseif ( isset( $_GET['activated'] ) ) :
     200} elseif ( isset( $_GET['activated'] ) ) {
    181201    if ( isset( $_GET['previewed'] ) ) {
    182202        ?>
    183203        <div id="message2" class="updated notice is-dismissible"><p><?php _e( 'Settings saved and theme activated.' ); ?> <a href="<?php echo home_url( '/' ); ?>"><?php _e( 'Visit site' ); ?></a></p></div>
    184         <?php } else { ?>
    185 <div id="message2" class="updated notice is-dismissible"><p><?php _e( 'New theme activated.' ); ?> <a href="<?php echo home_url( '/' ); ?>"><?php _e( 'Visit site' ); ?></a></p></div>
    186                                                                         <?php
    187 }
    188     elseif ( isset( $_GET['deleted'] ) ) :
     204        <?php
     205    } else {
    189206        ?>
    190 <div id="message3" class="updated notice is-dismissible"><p><?php _e( 'Theme deleted.' ); ?></p></div>
    191 <?php elseif ( isset( $_GET['delete-active-child'] ) ) : ?>
     207        <div id="message2" class="updated notice is-dismissible"><p><?php _e( 'New theme activated.' ); ?> <a href="<?php echo home_url( '/' ); ?>"><?php _e( 'Visit site' ); ?></a></p></div>
     208        <?php
     209    }
     210} elseif ( isset( $_GET['deleted'] ) ) {
     211    ?>
     212    <div id="message3" class="updated notice is-dismissible"><p><?php _e( 'Theme deleted.' ); ?></p></div>
     213    <?php
     214} elseif ( isset( $_GET['delete-active-child'] ) ) {
     215    ?>
    192216    <div id="message4" class="error"><p><?php _e( 'You cannot delete a theme while it has an active child theme.' ); ?></p></div>
    193217    <?php
    194 endif;
     218} elseif ( isset( $_GET['resumed'] ) ) {
     219    ?>
     220    <div id="message5" class="updated notice is-dismissible"><p><?php _e( 'Theme resumed.' ); ?></p></div>
     221    <?php
     222}
    195223
    196224$ct = wp_get_theme();
     
    345373
    346374    <?php
     375    $can_resume  = current_user_can( 'resume_themes' );
    347376    $can_delete  = current_user_can( 'delete_themes' );
    348377    $can_install = current_user_can( 'install_themes' );
     
    352381        <th><?php _ex( 'Name', 'theme name' ); ?></th>
    353382        <th><?php _e( 'Description' ); ?></th>
     383        <?php if ( $can_resume ) { ?>
     384            <td></td>
     385        <?php } ?>
    354386        <?php if ( $can_delete ) { ?>
    355387            <td></td>
     
    364396            <td><?php echo $broken_theme->errors()->get_error_message(); ?></td>
    365397            <?php
     398            if ( $can_resume ) {
     399                if ( 'theme_paused' === $broken_theme->errors()->get_error_code() ) {
     400                    $stylesheet = $broken_theme->get_stylesheet();
     401                    $resume_url = add_query_arg(
     402                        array(
     403                            'action'     => 'resume',
     404                            'stylesheet' => urlencode( $stylesheet ),
     405                        ),
     406                        admin_url( 'themes.php' )
     407                    );
     408                    $resume_url = wp_nonce_url( $resume_url, 'resume-theme_' . $stylesheet );
     409                    ?>
     410                    <td><a href="<?php echo esc_url( $resume_url ); ?>" class="button resume-theme"><?php _e( 'Resume' ); ?></a></td>
     411                    <?php
     412                } else {
     413                    ?>
     414                    <td></td>
     415                    <?php
     416                }
     417            }
     418
    366419            if ( $can_delete ) {
    367420                $stylesheet = $broken_theme->get_stylesheet();
Note: See TracChangeset for help on using the changeset viewer.