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. Built from https://develop.svn.wordpress.org/trunk@41560 git-svn-id: http://core.svn.wordpress.org/trunk@41393 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
415e9379f9
commit
aff467e1cb
|
@ -2203,6 +2203,14 @@ h1.nav-tab-wrapper, /* Back-compat for pre-4.4 */
|
||||||
#template > div {
|
#template > div {
|
||||||
margin-left: 190px;
|
margin-left: 190px;
|
||||||
}
|
}
|
||||||
|
#template .active-plugin-edit-warning {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-left: 30%;
|
||||||
|
margin-left: calc( 184px + 3% );
|
||||||
|
}
|
||||||
|
#template .active-plugin-edit-warning p {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.metabox-holder .stuffbox > h3, /* Back-compat for pre-4.4 */
|
.metabox-holder .stuffbox > h3, /* Back-compat for pre-4.4 */
|
||||||
.metabox-holder .postbox > 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;
|
margin-top: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#template > div {
|
#template > div,
|
||||||
|
#template .active-plugin-edit-warning {
|
||||||
float: none;
|
float: none;
|
||||||
margin: 0;
|
margin: 1em 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2203,6 +2203,14 @@ h1.nav-tab-wrapper, /* Back-compat for pre-4.4 */
|
||||||
#template > div {
|
#template > div {
|
||||||
margin-right: 190px;
|
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 .stuffbox > h3, /* Back-compat for pre-4.4 */
|
||||||
.metabox-holder .postbox > 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;
|
margin-top: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#template > div {
|
#template > div,
|
||||||
|
#template .active-plugin-edit-warning {
|
||||||
float: none;
|
float: none;
|
||||||
margin: 0;
|
margin: 1em 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -555,10 +555,8 @@ function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silen
|
||||||
if ( !empty($redirect) )
|
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
|
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();
|
ob_start();
|
||||||
wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
|
|
||||||
$_wp_plugin_file = $plugin;
|
plugin_sandbox_scrape( $plugin );
|
||||||
include_once( WP_PLUGIN_DIR . '/' . $plugin );
|
|
||||||
$plugin = $_wp_plugin_file; // Avoid stomping of the $plugin variable in a plugin.
|
|
||||||
|
|
||||||
if ( ! $silent ) {
|
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 ) {
|
function plugin_sandbox_scrape( $plugin ) {
|
||||||
|
global $wp_actions;
|
||||||
wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
|
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 );
|
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.
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,20 @@ if ( isset( $_REQUEST['plugin'] ) ) {
|
||||||
|
|
||||||
if ( empty( $plugin ) ) {
|
if ( empty( $plugin ) ) {
|
||||||
if ( $file ) {
|
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 {
|
} else {
|
||||||
$plugin = array_keys( $plugins );
|
$plugin = array_keys( $plugins );
|
||||||
$plugin = $plugin[0];
|
$plugin = $plugin[0];
|
||||||
|
@ -72,6 +85,10 @@ if ( isset( $_REQUEST['action'] ) && 'update' === $_REQUEST['action'] ) {
|
||||||
fwrite($f, $newcontent);
|
fwrite($f, $newcontent);
|
||||||
fclose($f);
|
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 );
|
$network_wide = is_plugin_active_for_network( $file );
|
||||||
|
|
||||||
// Deactivate so we can test it.
|
// Deactivate so we can test it.
|
||||||
|
@ -220,12 +237,12 @@ if ( isset( $_REQUEST['action'] ) && 'update' === $_REQUEST['action'] ) {
|
||||||
<?php if (isset($_GET['a'])) : ?>
|
<?php if (isset($_GET['a'])) : ?>
|
||||||
<div id="message" class="updated notice is-dismissible"><p><?php _e('File edited successfully.') ?></p></div>
|
<div id="message" class="updated notice is-dismissible"><p><?php _e('File edited successfully.') ?></p></div>
|
||||||
<?php elseif (isset($_GET['phperror'])) : ?>
|
<?php elseif (isset($_GET['phperror'])) : ?>
|
||||||
<div id="message" class="updated"><p><?php _e('This plugin has been deactivated because your changes resulted in a <strong>fatal error</strong>.') ?></p>
|
<div id="message" class="notice notice-error"><p><?php _e( 'This plugin has been deactivated because your changes resulted in a <strong>fatal error</strong>.' ); ?></p>
|
||||||
<?php
|
<?php
|
||||||
if ( wp_verify_nonce( $_GET['_error_nonce'], 'plugin-activation-error_' . $file ) ) {
|
if ( wp_verify_nonce( $_GET['_error_nonce'], 'plugin-activation-error_' . $plugin ) ) {
|
||||||
$iframe_url = add_query_arg( array(
|
$iframe_url = add_query_arg( array(
|
||||||
'action' => 'error_scrape',
|
'action' => 'error_scrape',
|
||||||
'plugin' => urlencode( $file ),
|
'plugin' => urlencode( $plugin ),
|
||||||
'_wpnonce' => urlencode( $_GET['_error_nonce'] ),
|
'_wpnonce' => urlencode( $_GET['_error_nonce'] ),
|
||||||
), admin_url( 'plugins.php' ) );
|
), admin_url( 'plugins.php' ) );
|
||||||
?>
|
?>
|
||||||
|
@ -315,7 +332,9 @@ foreach ( $plugin_files as $plugin_file ) :
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if ( is_writeable($real_file) ) : ?>
|
<?php if ( is_writeable($real_file) ) : ?>
|
||||||
<?php if ( in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) ) { ?>
|
<?php if ( in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) ) { ?>
|
||||||
<p><?php _e('<strong>Warning:</strong> Making changes to active plugins is not recommended. If your changes cause a fatal error, the plugin will be automatically deactivated.'); ?></p>
|
<div class="notice notice-warning inline active-plugin-edit-warning">
|
||||||
|
<p><?php _e('<strong>Warning:</strong> Making changes to active plugins is not recommended. If your changes cause a fatal error, the plugin will be automatically deactivated.'); ?></p>
|
||||||
|
</div>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
<p class="submit">
|
<p class="submit">
|
||||||
<?php
|
<?php
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*
|
*
|
||||||
* @global string $wp_version
|
* @global string $wp_version
|
||||||
*/
|
*/
|
||||||
$wp_version = '4.9-alpha-41559';
|
$wp_version = '4.9-alpha-41560';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||||
|
|
Loading…
Reference in New Issue