From f07591d1fd3724a09bd07fbedcbff08461e9b937 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 22 Sep 2017 01:35:09 +0000 Subject: [PATCH] Plugin Editor: Improve reliability of detecting PHP fatal errors when editing an active plugin. * Invalidate PHP opcache after file is updated to ensure `include` will ''include'' the written changes. * Define `WP_ADMIN` when activating plugin in sandbox so plugin code targeting admin will be loaded. * Do actions that get triggered when loading the admin to ensure plugin code runs that could cause errors on plugin editor screen (and lock out access). * Fix ability to re-activate a plugin after editing a PHP file other than the main plugin file, and ensure PHP fatal error will be displayed in such cases. * Consolidate duplicated code into `plugin_sandbox_scrape()` and re-use in `activate_plugin()`. * Show an error notice instead of a success notice when a file is updated but a plugin was deactivated due to a fatal error. * Update style of warning when editing an active plugin to be styled as an actual warning notice. See #12423, #21622. Fixes #39766. git-svn-id: https://develop.svn.wordpress.org/trunk@41560 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/common.css | 13 ++++++++-- src/wp-admin/includes/plugin.php | 41 ++++++++++++++++++++++++++++---- src/wp-admin/plugin-editor.php | 29 ++++++++++++++++++---- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/src/wp-admin/css/common.css b/src/wp-admin/css/common.css index 6fe50ac93c..d22368d637 100644 --- a/src/wp-admin/css/common.css +++ b/src/wp-admin/css/common.css @@ -2203,6 +2203,14 @@ h1.nav-tab-wrapper, /* Back-compat for pre-4.4 */ #template > div { margin-right: 190px; } +#template .active-plugin-edit-warning { + margin-top: 1em; + margin-right: 30%; + margin-right: calc( 184px + 3% ); +} +#template .active-plugin-edit-warning p { + width: auto; +} .metabox-holder .stuffbox > h3, /* Back-compat for pre-4.4 */ .metabox-holder .postbox > h3, /* Back-compat for pre-4.4 */ @@ -3609,9 +3617,10 @@ img { margin-top: -5px; } - #template > div { + #template > div, + #template .active-plugin-edit-warning { float: none; - margin: 0; + margin: 1em 0; width: auto; } diff --git a/src/wp-admin/includes/plugin.php b/src/wp-admin/includes/plugin.php index 7d804e1c66..ba6df12a9c 100644 --- a/src/wp-admin/includes/plugin.php +++ b/src/wp-admin/includes/plugin.php @@ -555,10 +555,8 @@ function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silen if ( !empty($redirect) ) wp_redirect(add_query_arg('_error_nonce', wp_create_nonce('plugin-activation-error_' . $plugin), $redirect)); // we'll override this later if the plugin can be included without fatal error ob_start(); - wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin ); - $_wp_plugin_file = $plugin; - include_once( WP_PLUGIN_DIR . '/' . $plugin ); - $plugin = $_wp_plugin_file; // Avoid stomping of the $plugin variable in a plugin. + + plugin_sandbox_scrape( $plugin ); if ( ! $silent ) { /** @@ -1881,9 +1879,42 @@ function wp_clean_plugins_cache( $clear_update_cache = true ) { } /** - * @param string $plugin + * Simulate loading the WordPress admin with a given plugin active to attempt to generate errors. + * + * Actions are re-triggered in the WP bootstrap process for the WP Admin, and the WP_ADMIN constant is defined. + * + * @since 3.0.0 + * @since 4.4.0 Function was moved into the `wp-admin/includes/plugin.php` file. + * @since 4.9.0 Add defining of WP_ADMIN and triggering admin WP bootstrap actions. + * + * @global array $wp_actions + * @param string $plugin Plugin file to load. */ function plugin_sandbox_scrape( $plugin ) { + global $wp_actions; wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin ); + + if ( ! defined( 'WP_ADMIN' ) ) { + define( 'WP_ADMIN', true ); + } + + $tested_actions = array( + 'plugins_loaded' => array(), + 'setup_theme' => array(), + 'after_setup_theme' => array(), + 'init' => array(), + 'wp_loaded' => array(), + 'admin_init' => array(), + ); + $old_wp_actions = $wp_actions; + array_map( 'remove_all_actions', array_keys( $tested_actions ) ); + include( WP_PLUGIN_DIR . '/' . $plugin ); + + // Trigger key actions that are done on the plugin editor to cause the relevant plugin hooks to fire and potentially cause errors. + foreach ( $tested_actions as $action => $args ) { + do_action_ref_array( $action, $args ); + } + + $wp_actions = $old_wp_actions; // Restore actions. } diff --git a/src/wp-admin/plugin-editor.php b/src/wp-admin/plugin-editor.php index 14d2b65039..239316ae2b 100644 --- a/src/wp-admin/plugin-editor.php +++ b/src/wp-admin/plugin-editor.php @@ -46,7 +46,20 @@ if ( isset( $_REQUEST['plugin'] ) ) { if ( empty( $plugin ) ) { if ( $file ) { - $plugin = $file; + + // Locate the plugin for a given plugin file being edited. + $file_dirname = dirname( $file ); + foreach ( array_keys( $plugins ) as $plugin_candidate ) { + if ( $plugin_candidate === $file || ( '.' !== $file_dirname && dirname( $plugin_candidate ) === $file_dirname ) ) { + $plugin = $plugin_candidate; + break; + } + } + + // Fallback to the file as the plugin. + if ( empty( $plugin ) ) { + $plugin = $file; + } } else { $plugin = array_keys( $plugins ); $plugin = $plugin[0]; @@ -72,6 +85,10 @@ if ( isset( $_REQUEST['action'] ) && 'update' === $_REQUEST['action'] ) { fwrite($f, $newcontent); fclose($f); + if ( preg_match( '/\.php$/', $real_file ) && function_exists( 'opcache_invalidate' ) ) { + opcache_invalidate( $real_file, true ); + } + $network_wide = is_plugin_active_for_network( $file ); // Deactivate so we can test it. @@ -220,12 +237,12 @@ if ( isset( $_REQUEST['action'] ) && 'update' === $_REQUEST['action'] ) {

-

fatal error.') ?>

+

fatal error.' ); ?>

'error_scrape', - 'plugin' => urlencode( $file ), + 'plugin' => urlencode( $plugin ), '_wpnonce' => urlencode( $_GET['_error_nonce'] ), ), admin_url( 'plugins.php' ) ); ?> @@ -315,7 +332,9 @@ foreach ( $plugin_files as $plugin_file ) : -

Warning: Making changes to active plugins is not recommended. If your changes cause a fatal error, the plugin will be automatically deactivated.'); ?>

+
+

Warning: Making changes to active plugins is not recommended. If your changes cause a fatal error, the plugin will be automatically deactivated.'); ?>

+