diff --git a/src/wp-includes/plugin.php b/src/wp-includes/plugin.php index dd8798a074..bff15c1383 100644 --- a/src/wp-includes/plugin.php +++ b/src/wp-includes/plugin.php @@ -946,3 +946,65 @@ function _wp_filter_build_unique_id($tag, $function, $priority) { return $function[0] . '::' . $function[1]; } } + +/** + * Back up global variables used for actions and filters. + * + * Prevents redefinition of these globals in advanced-cache.php from accidentally + * destroying existing data. + * + * @since 4.6.0 + * @access private + * + * @global array $wp_filter Stores all filters and actions. + * @global array $wp_actions Stores the amount of times an action was triggered. + * @global array $merged_filters Merges the filter hooks using this function. + * @global array $wp_current_filter Stores the list of current filters with the current one last. + * @staticvar array $backup_globals Backed up globals. + * @return array the staticvar from the first time it is set. + */ +function _backup_plugin_globals(){ + global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter; + static $backup_globals = array(); + if ( empty( $backup_globals ) ) { + $backup_globals = array( + 'backup_wp_filter' => $wp_filter, + 'backup_wp_actions' => $wp_actions, + 'backup_merged_filters' => $merged_filters, + 'backup_wp_current_filter' => $wp_current_filter, + ); + }; + return $backup_globals; +} + +/** + * Safely restore backed up global variables used for actions and filters. + * + * @since 4.6.0 + * @access private + * + * @global array $wp_filter Stores all filters and actions. + * @global array $wp_actions Stores the amount of times an action was triggered. + * @global array $merged_filters Merges the filter hooks using this function. + * @global array $wp_current_filter Stores the list of current filters with the current one last. + * @staticvar array $backup_globals Backed up globals. + */ +function _restore_plugin_globals(){ + global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter; + $backup_globals = _backup_plugin_globals(); + if ( $wp_filter !== $backup_globals['backup_wp_filter'] ){ + $wp_filter = array_merge_recursive( $wp_filter, $backup_globals['backup_wp_filter'] ); + } + + if ( $wp_actions !== $backup_globals['backup_wp_actions'] ){ + $wp_actions = array_merge_recursive( $wp_actions, $backup_globals['backup_wp_actions'] ); + } + + if ( $merged_filters !== $backup_globals['backup_merged_filters'] ){ + $merged_filters = array_merge_recursive( $merged_filters, $backup_globals['backup_merged_filters'] ); + } + + if ( $wp_current_filter !== $backup_globals['backup_wp_current_filter'] ){ + $wp_current_filter = array_merge_recursive( $wp_current_filter, $backup_globals['backup_wp_current_filter'] ); + } +} diff --git a/src/wp-settings.php b/src/wp-settings.php index fb8cd22d42..8beee6fdb8 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -20,6 +20,7 @@ define( 'WPINC', 'wp-includes' ); // Include files required for initialization. require( ABSPATH . WPINC . '/load.php' ); require( ABSPATH . WPINC . '/default-constants.php' ); +require( ABSPATH . WPINC . '/plugin.php' ); /* * These can't be directly globalized in version.php. When updating, @@ -70,8 +71,11 @@ timer_start(); wp_debug_mode(); // For an advanced caching plugin to use. Uses a static drop-in because you would only want one. -if ( WP_CACHE ) +if ( WP_CACHE ) { + _backup_plugin_globals(); WP_DEBUG ? include( WP_CONTENT_DIR . '/advanced-cache.php' ) : @include( WP_CONTENT_DIR . '/advanced-cache.php' ); + _restore_plugin_globals(); +} // Define WP_LANG_DIR if not set. wp_set_lang_dir(); @@ -81,7 +85,6 @@ require( ABSPATH . WPINC . '/compat.php' ); require( ABSPATH . WPINC . '/functions.php' ); require( ABSPATH . WPINC . '/class-wp.php' ); require( ABSPATH . WPINC . '/class-wp-error.php' ); -require( ABSPATH . WPINC . '/plugin.php' ); require( ABSPATH . WPINC . '/pomo/mo.php' ); // Include the wpdb class and, if present, a db.php database drop-in. diff --git a/tests/phpunit/tests/actions.php b/tests/phpunit/tests/actions.php index 583c8cea15..ad53ec0199 100644 --- a/tests/phpunit/tests/actions.php +++ b/tests/phpunit/tests/actions.php @@ -328,6 +328,69 @@ class Tests_Actions extends WP_UnitTestCase { $this->assertFalse( doing_filter( 'testing' ) ); // No longer doing this filter } + /** + * @ticket 36819 + */ + function test_backup_plugin_globals_returns_filters() { + $backup = _backup_plugin_globals(); + $this->assertArrayHasKey( 'backup_wp_filter', $backup ); + $this->assertArrayHasKey( 'backup_wp_actions', $backup ); + $this->assertArrayHasKey( 'backup_wp_current_filter', $backup ); + $this->assertArrayHasKey( 'backup_merged_filters', $backup ); + } + + /** + * @ticket 36819 + */ + function test_backup_plugin_globals_returns_filters_from_first_time_called() { + $backup = _backup_plugin_globals(); + + $a = new MockAction(); + $tag = rand_str(); + + add_action($tag, array(&$a, 'action')); + + $new_backup = _backup_plugin_globals(); + $this->assertEquals( $backup, $new_backup ); + } + + /** + * @ticket 36819 + */ + function test_restore_plugin_globals_from_stomp() { + global $wp_actions; + $original_actions = $wp_actions; + + _backup_plugin_globals(); + + $wp_actions = array(); + + $this->assertEmpty( $wp_actions ); + _restore_plugin_globals(); + + $this->assertEquals( $GLOBALS['wp_actions'], $original_actions ); + } + + /** + * @ticket 36819 + */ + function test_restore_plugin_globals_includes_additions() { + global $wp_filter; + $original_filter = $wp_filter; + + $backup = _backup_plugin_globals(); + + $a = new MockAction(); + $tag = rand_str(); + add_action($tag, array(&$a, 'action')); + + $this->assertNotEquals( $GLOBALS['wp_filter'], $original_filter ); + + _restore_plugin_globals(); + + $this->assertNotEquals( $GLOBALS['wp_filter'], $original_filter ); + } + function apply_testing_filter() { $this->apply_testing_filter = true;