mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2025-10-16 12:05:38 +00:00
Plugin Dependencies: Remove auto-deactivation and bootstrapping logic.
Automatic deactivation of dependents with unmet dependencies requires a write operation to the database. This was performed during Core's bootstrap, which risked the database and cache becoming out-of-sync on sites with heavy traffic. No longer loading plugins that have unmet requirements has not had a final approach decided core-wide, and is still in discussion in #60491 to be handled in a future release. The `plugin_data` option, used to persistently store plugin data for detecting unmet dependencies during Core's bootstrap, is no longer needed. Follow-up to [57545], [57592], [57606], [57617]. Props dd32, azaozz, swissspidy, desrosj, afragen, pbiron, zunaid321, costdev. Fixes #60457. See #60491, #60510, #60518. git-svn-id: https://develop.svn.wordpress.org/trunk@57658 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
d7672c46b9
commit
9173f8baa9
@ -155,12 +155,6 @@ class Plugin_Upgrader extends WP_Upgrader {
|
|||||||
// Force refresh of plugin update information.
|
// Force refresh of plugin update information.
|
||||||
wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
|
wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
|
||||||
|
|
||||||
$all_plugin_data = get_option( 'plugin_data', array() );
|
|
||||||
$plugin_file = $this->new_plugin_data['file'];
|
|
||||||
unset( $this->new_plugin_data['file'] );
|
|
||||||
$all_plugin_data[ $plugin_file ] = $this->new_plugin_data;
|
|
||||||
update_option( 'plugin_data', $all_plugin_data );
|
|
||||||
|
|
||||||
if ( $parsed_args['overwrite_package'] ) {
|
if ( $parsed_args['overwrite_package'] ) {
|
||||||
/**
|
/**
|
||||||
* Fires when the upgrader has successfully overwritten a currently installed
|
* Fires when the upgrader has successfully overwritten a currently installed
|
||||||
@ -488,16 +482,7 @@ class Plugin_Upgrader extends WP_Upgrader {
|
|||||||
foreach ( $files as $file ) {
|
foreach ( $files as $file ) {
|
||||||
$info = get_plugin_data( $file, false, false );
|
$info = get_plugin_data( $file, false, false );
|
||||||
if ( ! empty( $info['Name'] ) ) {
|
if ( ! empty( $info['Name'] ) ) {
|
||||||
$basename = basename( $file );
|
$this->new_plugin_data = $info;
|
||||||
$dirname = basename( dirname( $file ) );
|
|
||||||
|
|
||||||
if ( '.' === $dirname ) {
|
|
||||||
$plugin_file = $basename;
|
|
||||||
} else {
|
|
||||||
$plugin_file = "$dirname/$basename";
|
|
||||||
}
|
|
||||||
$this->new_plugin_data = ( $info );
|
|
||||||
$this->new_plugin_data['file'] = $plugin_file;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -927,7 +927,7 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible
|
|||||||
|
|
||||||
// Determine the status of plugin dependencies.
|
// Determine the status of plugin dependencies.
|
||||||
$installed_plugins = get_plugins();
|
$installed_plugins = get_plugins();
|
||||||
$active_plugins = get_option( 'active_plugins' );
|
$active_plugins = get_option( 'active_plugins', array() );
|
||||||
$plugin_dependencies_count = count( $requires_plugins );
|
$plugin_dependencies_count = count( $requires_plugins );
|
||||||
$installed_plugin_dependencies_count = 0;
|
$installed_plugin_dependencies_count = 0;
|
||||||
$active_plugin_dependencies_count = 0;
|
$active_plugin_dependencies_count = 0;
|
||||||
|
|||||||
@ -333,7 +333,6 @@ function get_plugins( $plugin_folder = '' ) {
|
|||||||
return $wp_plugins;
|
return $wp_plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
$new_plugin_data = array();
|
|
||||||
foreach ( $plugin_files as $plugin_file ) {
|
foreach ( $plugin_files as $plugin_file ) {
|
||||||
if ( ! is_readable( "$plugin_root/$plugin_file" ) ) {
|
if ( ! is_readable( "$plugin_root/$plugin_file" ) ) {
|
||||||
continue;
|
continue;
|
||||||
@ -346,13 +345,6 @@ function get_plugins( $plugin_folder = '' ) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$new_plugin_file = str_replace(
|
|
||||||
trailingslashit( WP_PLUGIN_DIR ),
|
|
||||||
'',
|
|
||||||
"$plugin_root/$plugin_file"
|
|
||||||
);
|
|
||||||
|
|
||||||
$new_plugin_data[ $new_plugin_file ] = $plugin_data;
|
|
||||||
$wp_plugins[ plugin_basename( $plugin_file ) ] = $plugin_data;
|
$wp_plugins[ plugin_basename( $plugin_file ) ] = $plugin_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,10 +353,6 @@ function get_plugins( $plugin_folder = '' ) {
|
|||||||
$cache_plugins[ $plugin_folder ] = $wp_plugins;
|
$cache_plugins[ $plugin_folder ] = $wp_plugins;
|
||||||
wp_cache_set( 'plugins', $cache_plugins, 'plugins' );
|
wp_cache_set( 'plugins', $cache_plugins, 'plugins' );
|
||||||
|
|
||||||
if ( ! wp_installing() ) {
|
|
||||||
update_option( 'plugin_data', $new_plugin_data );
|
|
||||||
}
|
|
||||||
|
|
||||||
return $wp_plugins;
|
return $wp_plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -975,7 +963,6 @@ function delete_plugins( $plugins, $deprecated = '' ) {
|
|||||||
$plugins_dir = trailingslashit( $plugins_dir );
|
$plugins_dir = trailingslashit( $plugins_dir );
|
||||||
|
|
||||||
$plugin_translations = wp_get_installed_translations( 'plugins' );
|
$plugin_translations = wp_get_installed_translations( 'plugins' );
|
||||||
$all_plugin_data = get_option( 'plugin_data', array() );
|
|
||||||
|
|
||||||
$errors = array();
|
$errors = array();
|
||||||
|
|
||||||
@ -1020,7 +1007,6 @@ function delete_plugins( $plugins, $deprecated = '' ) {
|
|||||||
$errors[] = $plugin_file;
|
$errors[] = $plugin_file;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
unset( $all_plugin_data[ $plugin_file ] );
|
|
||||||
|
|
||||||
$plugin_slug = dirname( $plugin_file );
|
$plugin_slug = dirname( $plugin_file );
|
||||||
|
|
||||||
@ -1069,7 +1055,6 @@ function delete_plugins( $plugins, $deprecated = '' ) {
|
|||||||
|
|
||||||
return new WP_Error( 'could_not_remove_plugin', sprintf( $message, implode( ', ', $errors ) ) );
|
return new WP_Error( 'could_not_remove_plugin', sprintf( $message, implode( ', ', $errors ) ) );
|
||||||
}
|
}
|
||||||
update_option( 'plugin_data', $all_plugin_data );
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1214,6 +1199,8 @@ function validate_plugin_requirements( $plugin ) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WP_Plugin_Dependencies::initialize();
|
||||||
|
|
||||||
if ( WP_Plugin_Dependencies::has_unmet_dependencies( $plugin ) ) {
|
if ( WP_Plugin_Dependencies::has_unmet_dependencies( $plugin ) ) {
|
||||||
$dependencies = WP_Plugin_Dependencies::get_dependencies( $plugin );
|
$dependencies = WP_Plugin_Dependencies::get_dependencies( $plugin );
|
||||||
$unmet_dependencies = array();
|
$unmet_dependencies = array();
|
||||||
|
|||||||
@ -135,8 +135,8 @@ get_current_screen()->set_screen_reader_content(
|
|||||||
*/
|
*/
|
||||||
require_once ABSPATH . 'wp-admin/admin-header.php';
|
require_once ABSPATH . 'wp-admin/admin-header.php';
|
||||||
|
|
||||||
|
WP_Plugin_Dependencies::initialize();
|
||||||
WP_Plugin_Dependencies::display_admin_notice_for_unmet_dependencies();
|
WP_Plugin_Dependencies::display_admin_notice_for_unmet_dependencies();
|
||||||
WP_Plugin_Dependencies::display_admin_notice_for_deactivated_dependents();
|
|
||||||
WP_Plugin_Dependencies::display_admin_notice_for_circular_dependencies();
|
WP_Plugin_Dependencies::display_admin_notice_for_circular_dependencies();
|
||||||
?>
|
?>
|
||||||
<div class="wrap <?php echo esc_attr( "plugin-install-tab-$tab" ); ?>">
|
<div class="wrap <?php echo esc_attr( "plugin-install-tab-$tab" ); ?>">
|
||||||
|
|||||||
@ -40,6 +40,8 @@ $_SERVER['REQUEST_URI'] = remove_query_arg( $query_args_to_remove, $_SERVER['REQ
|
|||||||
|
|
||||||
wp_enqueue_script( 'updates' );
|
wp_enqueue_script( 'updates' );
|
||||||
|
|
||||||
|
WP_Plugin_Dependencies::initialize();
|
||||||
|
|
||||||
if ( $action ) {
|
if ( $action ) {
|
||||||
|
|
||||||
switch ( $action ) {
|
switch ( $action ) {
|
||||||
@ -741,7 +743,6 @@ if ( isset( $_GET['error'] ) ) {
|
|||||||
?>
|
?>
|
||||||
|
|
||||||
<?php WP_Plugin_Dependencies::display_admin_notice_for_unmet_dependencies(); ?>
|
<?php WP_Plugin_Dependencies::display_admin_notice_for_unmet_dependencies(); ?>
|
||||||
<?php WP_Plugin_Dependencies::display_admin_notice_for_deactivated_dependents(); ?>
|
|
||||||
<?php WP_Plugin_Dependencies::display_admin_notice_for_circular_dependencies(); ?>
|
<?php WP_Plugin_Dependencies::display_admin_notice_for_circular_dependencies(); ?>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h1 class="wp-heading-inline">
|
<h1 class="wp-heading-inline">
|
||||||
|
|||||||
@ -105,15 +105,25 @@ class WP_Plugin_Dependencies {
|
|||||||
protected static $circular_dependencies_slugs;
|
protected static $circular_dependencies_slugs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes by fetching plugin header and plugin API data,
|
* Whether Plugin Dependencies have been initialized.
|
||||||
* and deactivating dependents with unmet dependencies.
|
*
|
||||||
|
* @since 6.5.0
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected static $initialized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes by fetching plugin header and plugin API data.
|
||||||
*
|
*
|
||||||
* @since 6.5.0
|
* @since 6.5.0
|
||||||
*/
|
*/
|
||||||
public static function initialize() {
|
public static function initialize() {
|
||||||
self::read_dependencies_from_plugin_headers();
|
if ( false === self::$initialized ) {
|
||||||
self::get_dependency_api_data();
|
self::read_dependencies_from_plugin_headers();
|
||||||
self::deactivate_dependents_with_unmet_dependencies();
|
self::get_dependency_api_data();
|
||||||
|
self::$initialized = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,7 +135,7 @@ class WP_Plugin_Dependencies {
|
|||||||
* @return bool Whether the plugin has plugins that depend on it.
|
* @return bool Whether the plugin has plugins that depend on it.
|
||||||
*/
|
*/
|
||||||
public static function has_dependents( $plugin_file ) {
|
public static function has_dependents( $plugin_file ) {
|
||||||
return in_array( self::convert_to_slug( $plugin_file ), self::$dependency_slugs, true );
|
return in_array( self::convert_to_slug( $plugin_file ), (array) self::$dependency_slugs, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,7 +182,7 @@ class WP_Plugin_Dependencies {
|
|||||||
public static function get_dependents( $slug ) {
|
public static function get_dependents( $slug ) {
|
||||||
$dependents = array();
|
$dependents = array();
|
||||||
|
|
||||||
foreach ( self::$dependencies as $dependent => $dependencies ) {
|
foreach ( (array) self::$dependencies as $dependent => $dependencies ) {
|
||||||
if ( in_array( $slug, $dependencies, true ) ) {
|
if ( in_array( $slug, $dependencies, true ) ) {
|
||||||
$dependents[] = $dependent;
|
$dependents[] = $dependent;
|
||||||
}
|
}
|
||||||
@ -368,36 +378,6 @@ class WP_Plugin_Dependencies {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays an admin notice if dependencies have been deactivated.
|
|
||||||
*
|
|
||||||
* @since 6.5.0
|
|
||||||
*/
|
|
||||||
public static function display_admin_notice_for_deactivated_dependents() {
|
|
||||||
/*
|
|
||||||
* Plugin deactivated if dependencies not met.
|
|
||||||
* Transient on a 10 second timeout.
|
|
||||||
*/
|
|
||||||
$deactivate_requires = get_site_transient( 'wp_plugin_dependencies_deactivated_plugins' );
|
|
||||||
if ( ! empty( $deactivate_requires ) ) {
|
|
||||||
$deactivated_plugins = '';
|
|
||||||
foreach ( $deactivate_requires as $deactivated ) {
|
|
||||||
$deactivated_plugins .= '<li>' . esc_html( self::$plugins[ $deactivated ]['Name'] ) . '</li>';
|
|
||||||
}
|
|
||||||
wp_admin_notice(
|
|
||||||
sprintf(
|
|
||||||
/* translators: 1: plugin names */
|
|
||||||
__( 'The following plugin(s) have been deactivated due to uninstalled or inactive dependencies: %s' ),
|
|
||||||
"<ul>$deactivated_plugins</ul>"
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'type' => 'error',
|
|
||||||
'dismissible' => true,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays an admin notice if circular dependencies are installed.
|
* Displays an admin notice if circular dependencies are installed.
|
||||||
*
|
*
|
||||||
@ -543,14 +523,8 @@ class WP_Plugin_Dependencies {
|
|||||||
return self::$plugins;
|
return self::$plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
$all_plugin_data = get_option( 'plugin_data', array() );
|
require_once ABSPATH . '/wp-admin/includes/plugin.php';
|
||||||
|
self::$plugins = get_plugins();
|
||||||
if ( empty( $all_plugin_data ) ) {
|
|
||||||
require_once ABSPATH . '/wp-admin/includes/plugin.php';
|
|
||||||
$all_plugin_data = get_plugins();
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$plugins = $all_plugin_data;
|
|
||||||
|
|
||||||
return self::$plugins;
|
return self::$plugins;
|
||||||
}
|
}
|
||||||
@ -620,83 +594,6 @@ class WP_Plugin_Dependencies {
|
|||||||
return $sanitized_slugs;
|
return $sanitized_slugs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets plugin filepaths for active plugins that depend on the dependency.
|
|
||||||
*
|
|
||||||
* Recurses for each dependent that is also a dependency.
|
|
||||||
*
|
|
||||||
* @param string $plugin_file The dependency's filepath, relative to the plugin directory.
|
|
||||||
* @return string[] An array of active dependent plugin filepaths, relative to the plugin directory.
|
|
||||||
*/
|
|
||||||
protected static function get_active_dependents_in_dependency_tree( $plugin_file ) {
|
|
||||||
$all_dependents = array();
|
|
||||||
$dependents = self::get_dependents( self::convert_to_slug( $plugin_file ) );
|
|
||||||
|
|
||||||
if ( empty( $dependents ) ) {
|
|
||||||
return $all_dependents;
|
|
||||||
}
|
|
||||||
|
|
||||||
require_once ABSPATH . '/wp-admin/includes/plugin.php';
|
|
||||||
foreach ( $dependents as $dependent ) {
|
|
||||||
if ( is_plugin_active( $dependent ) ) {
|
|
||||||
$all_dependents[] = $dependent;
|
|
||||||
$all_dependents = array_merge(
|
|
||||||
$all_dependents,
|
|
||||||
self::get_active_dependents_in_dependency_tree( $dependent )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $all_dependents;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deactivates dependent plugins with unmet dependencies.
|
|
||||||
*
|
|
||||||
* @since 6.5.0
|
|
||||||
*/
|
|
||||||
protected static function deactivate_dependents_with_unmet_dependencies() {
|
|
||||||
$dependents_to_deactivate = array();
|
|
||||||
$circular_dependencies = array_reduce(
|
|
||||||
self::get_circular_dependencies(),
|
|
||||||
function ( $all_circular, $circular_pair ) {
|
|
||||||
return array_merge( $all_circular, $circular_pair );
|
|
||||||
},
|
|
||||||
array()
|
|
||||||
);
|
|
||||||
|
|
||||||
require_once ABSPATH . '/wp-admin/includes/plugin.php';
|
|
||||||
foreach ( self::$dependencies as $dependent => $dependencies ) {
|
|
||||||
// Skip dependents that are no longer installed or aren't active.
|
|
||||||
if ( ! array_key_exists( $dependent, self::$plugins ) || is_plugin_inactive( $dependent ) ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip plugins within a circular dependency tree or plugins that have no unmet dependencies.
|
|
||||||
if ( in_array( $dependent, $circular_dependencies, true ) || ! self::has_unmet_dependencies( $dependent ) ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dependents_to_deactivate[] = $dependent;
|
|
||||||
|
|
||||||
// Also add any plugins that rely on any of this plugin's dependents.
|
|
||||||
$dependents_to_deactivate = array_merge(
|
|
||||||
$dependents_to_deactivate,
|
|
||||||
self::get_active_dependents_in_dependency_tree( $dependent )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bail early if there are no dependents to deactivate.
|
|
||||||
if ( empty( $dependents_to_deactivate ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dependents_to_deactivate = array_unique( $dependents_to_deactivate );
|
|
||||||
|
|
||||||
deactivate_plugins( $dependents_to_deactivate );
|
|
||||||
set_site_transient( 'wp_plugin_dependencies_deactivated_plugins', $dependents_to_deactivate, 10 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the filepath of installed dependencies.
|
* Gets the filepath of installed dependencies.
|
||||||
* If a dependency is not installed, the filepath defaults to false.
|
* If a dependency is not installed, the filepath defaults to false.
|
||||||
@ -710,6 +607,10 @@ class WP_Plugin_Dependencies {
|
|||||||
return self::$dependency_filepaths;
|
return self::$dependency_filepaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( null === self::$dependency_slugs ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
self::$dependency_filepaths = array();
|
self::$dependency_filepaths = array();
|
||||||
|
|
||||||
$plugin_dirnames = self::get_plugin_dirnames();
|
$plugin_dirnames = self::get_plugin_dirnames();
|
||||||
@ -846,6 +747,10 @@ class WP_Plugin_Dependencies {
|
|||||||
return self::$circular_dependencies_pairs;
|
return self::$circular_dependencies_pairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( null === self::$dependencies ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
self::$circular_dependencies_slugs = array();
|
self::$circular_dependencies_slugs = array();
|
||||||
|
|
||||||
self::$circular_dependencies_pairs = array();
|
self::$circular_dependencies_pairs = array();
|
||||||
|
|||||||
@ -388,6 +388,7 @@ require ABSPATH . WPINC . '/script-modules.php';
|
|||||||
require ABSPATH . WPINC . '/interactivity-api/class-wp-interactivity-api.php';
|
require ABSPATH . WPINC . '/interactivity-api/class-wp-interactivity-api.php';
|
||||||
require ABSPATH . WPINC . '/interactivity-api/class-wp-interactivity-api-directives-processor.php';
|
require ABSPATH . WPINC . '/interactivity-api/class-wp-interactivity-api-directives-processor.php';
|
||||||
require ABSPATH . WPINC . '/interactivity-api/interactivity-api.php';
|
require ABSPATH . WPINC . '/interactivity-api/interactivity-api.php';
|
||||||
|
require ABSPATH . WPINC . '/class-wp-plugin-dependencies.php';
|
||||||
|
|
||||||
wp_script_modules()->add_hooks();
|
wp_script_modules()->add_hooks();
|
||||||
wp_interactivity()->add_hooks();
|
wp_interactivity()->add_hooks();
|
||||||
@ -419,12 +420,6 @@ wp_plugin_directory_constants();
|
|||||||
|
|
||||||
$GLOBALS['wp_plugin_paths'] = array();
|
$GLOBALS['wp_plugin_paths'] = array();
|
||||||
|
|
||||||
// Load and initialize WP_Plugin_Dependencies.
|
|
||||||
require_once ABSPATH . WPINC . '/class-wp-plugin-dependencies.php';
|
|
||||||
if ( ! defined( 'WP_RUN_CORE_TESTS' ) ) {
|
|
||||||
WP_Plugin_Dependencies::initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load must-use plugins.
|
// Load must-use plugins.
|
||||||
foreach ( wp_get_mu_plugins() as $mu_plugin ) {
|
foreach ( wp_get_mu_plugins() as $mu_plugin ) {
|
||||||
$_wp_plugin_file = $mu_plugin;
|
$_wp_plugin_file = $mu_plugin;
|
||||||
@ -499,85 +494,7 @@ if ( ! is_multisite() && wp_is_fatal_error_handler_enabled() ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load active plugins.
|
// Load active plugins.
|
||||||
$all_plugin_data = get_option( 'plugin_data', array() );
|
|
||||||
$failed_plugins = array();
|
|
||||||
$plugins_dir_strlen = strlen( trailingslashit( WP_PLUGIN_DIR ) );
|
|
||||||
foreach ( wp_get_active_and_valid_plugins() as $plugin ) {
|
foreach ( wp_get_active_and_valid_plugins() as $plugin ) {
|
||||||
$plugin_file = substr( $plugin, $plugins_dir_strlen );
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Skip any plugins that have not been added to the 'plugin_data' option yet.
|
|
||||||
*
|
|
||||||
* Some plugin files may be added locally and activated, but will not yet be
|
|
||||||
* added to the 'plugin_data' option. This causes the 'active_plugins' option
|
|
||||||
* and the 'plugin_data' option to be temporarily out of sync until the next
|
|
||||||
* call to `get_plugins()`.
|
|
||||||
*/
|
|
||||||
if ( isset( $all_plugin_data[ $plugin_file ] ) ) {
|
|
||||||
$plugin_headers = $all_plugin_data[ $plugin_file ];
|
|
||||||
$errors = array();
|
|
||||||
$requirements = array(
|
|
||||||
'requires' => ! empty( $plugin_headers['RequiresWP'] ) ? $plugin_headers['RequiresWP'] : '',
|
|
||||||
'requires_php' => ! empty( $plugin_headers['RequiresPHP'] ) ? $plugin_headers['RequiresPHP'] : '',
|
|
||||||
);
|
|
||||||
$compatible_wp = is_wp_version_compatible( $requirements['requires'] );
|
|
||||||
$compatible_php = is_php_version_compatible( $requirements['requires_php'] );
|
|
||||||
|
|
||||||
$php_update_message = '</p><p>' . sprintf(
|
|
||||||
/* translators: %s: URL to Update PHP page. */
|
|
||||||
__( '<a href="%s">Learn more about updating PHP</a>.' ),
|
|
||||||
esc_url( wp_get_update_php_url() )
|
|
||||||
);
|
|
||||||
|
|
||||||
$annotation = wp_get_update_php_annotation();
|
|
||||||
|
|
||||||
if ( $annotation ) {
|
|
||||||
$php_update_message .= '</p><p><em>' . $annotation . '</em>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! $compatible_wp && ! $compatible_php ) {
|
|
||||||
$errors[] = sprintf(
|
|
||||||
/* translators: 1: Current WordPress version, 2: Current PHP version, 3: Plugin name, 4: Required WordPress version, 5: Required PHP version. */
|
|
||||||
_x( '<strong>Error:</strong> Current versions of WordPress (%1$s) and PHP (%2$s) do not meet minimum requirements for %3$s. The plugin requires WordPress %4$s and PHP %5$s.', 'plugin' ),
|
|
||||||
get_bloginfo( 'version' ),
|
|
||||||
PHP_VERSION,
|
|
||||||
$plugin_headers['Name'],
|
|
||||||
$requirements['requires'],
|
|
||||||
$requirements['requires_php']
|
|
||||||
) . $php_update_message;
|
|
||||||
} elseif ( ! $compatible_php ) {
|
|
||||||
$errors[] = sprintf(
|
|
||||||
/* translators: 1: Current PHP version, 2: Plugin name, 3: Required PHP version. */
|
|
||||||
_x( '<strong>Error:</strong> Current PHP version (%1$s) does not meet minimum requirements for %2$s. The plugin requires PHP %3$s.', 'plugin' ),
|
|
||||||
PHP_VERSION,
|
|
||||||
$plugin_headers['Name'],
|
|
||||||
$requirements['requires_php']
|
|
||||||
) . $php_update_message;
|
|
||||||
} elseif ( ! $compatible_wp ) {
|
|
||||||
$errors[] = sprintf(
|
|
||||||
/* translators: 1: Current WordPress version, 2: Plugin name, 3: Required WordPress version. */
|
|
||||||
_x( '<strong>Error:</strong> Current WordPress version (%1$s) does not meet minimum requirements for %2$s. The plugin requires WordPress %3$s.', 'plugin' ),
|
|
||||||
get_bloginfo( 'version' ),
|
|
||||||
$plugin_headers['Name'],
|
|
||||||
$requirements['requires']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! empty( $errors ) ) {
|
|
||||||
$failed_plugins[ $plugin_file ] = '';
|
|
||||||
foreach ( $errors as $error ) {
|
|
||||||
$failed_plugins[ $plugin_file ] .= wp_get_admin_notice(
|
|
||||||
$error,
|
|
||||||
array(
|
|
||||||
'type' => 'error',
|
|
||||||
'dismissible' => true,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wp_register_plugin_realpath( $plugin );
|
wp_register_plugin_realpath( $plugin );
|
||||||
|
|
||||||
$_wp_plugin_file = $plugin;
|
$_wp_plugin_file = $plugin;
|
||||||
@ -595,20 +512,6 @@ foreach ( wp_get_active_and_valid_plugins() as $plugin ) {
|
|||||||
}
|
}
|
||||||
unset( $plugin, $_wp_plugin_file );
|
unset( $plugin, $_wp_plugin_file );
|
||||||
|
|
||||||
if ( ! empty( $failed_plugins ) ) {
|
|
||||||
add_action(
|
|
||||||
'admin_notices',
|
|
||||||
function () use ( $failed_plugins ) {
|
|
||||||
global $pagenow;
|
|
||||||
|
|
||||||
if ( 'index.php' === $pagenow || 'plugins.php' === $pagenow ) {
|
|
||||||
echo implode( '', $failed_plugins );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
unset( $failed_plugins );
|
|
||||||
|
|
||||||
// Load pluggable functions.
|
// Load pluggable functions.
|
||||||
require ABSPATH . WPINC . '/pluggable.php';
|
require ABSPATH . WPINC . '/pluggable.php';
|
||||||
require ABSPATH . WPINC . '/pluggable-deprecated.php';
|
require ABSPATH . WPINC . '/pluggable-deprecated.php';
|
||||||
|
|||||||
@ -32,6 +32,7 @@ abstract class WP_PluginDependencies_UnitTestCase extends WP_UnitTestCase {
|
|||||||
'dependency_filepaths' => null,
|
'dependency_filepaths' => null,
|
||||||
'circular_dependencies_pairs' => null,
|
'circular_dependencies_pairs' => null,
|
||||||
'circular_dependencies_slugs' => null,
|
'circular_dependencies_slugs' => null,
|
||||||
|
'initialized' => false,
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,12 +63,12 @@ abstract class WP_PluginDependencies_UnitTestCase extends WP_UnitTestCase {
|
|||||||
/**
|
/**
|
||||||
* Resets all static properties to a default value after each test.
|
* Resets all static properties to a default value after each test.
|
||||||
*/
|
*/
|
||||||
public function set_up() {
|
public function tear_down() {
|
||||||
parent::set_up();
|
|
||||||
|
|
||||||
foreach ( self::$static_properties as $name => $default_value ) {
|
foreach ( self::$static_properties as $name => $default_value ) {
|
||||||
$this->set_property_value( $name, $default_value );
|
$this->set_property_value( $name, $default_value );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent::tear_down();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -17,6 +17,30 @@ require_once __DIR__ . '/base.php';
|
|||||||
*/
|
*/
|
||||||
class Tests_Admin_WPPluginDependencies_GetDependencyFilepath extends WP_PluginDependencies_UnitTestCase {
|
class Tests_Admin_WPPluginDependencies_GetDependencyFilepath extends WP_PluginDependencies_UnitTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that false is returned if Plugin Dependencies has not been initialized.
|
||||||
|
*
|
||||||
|
* @ticket 60457
|
||||||
|
*/
|
||||||
|
public function test_should_return_false_before_initialization() {
|
||||||
|
// Ensure Plugin Dependencies has not been initialized.
|
||||||
|
$this->assertFalse(
|
||||||
|
$this->get_property_value( 'initialized' ),
|
||||||
|
'Plugin Dependencies has been initialized.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
self::$static_properties['dependency_slugs'],
|
||||||
|
$this->get_property_value( 'dependency_slugs' ),
|
||||||
|
'"dependency_slugs" was not set to its default value.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertFalse(
|
||||||
|
self::$instance->get_dependency_filepath( 'dependency' ),
|
||||||
|
'false was not returned before initialization.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that the expected dependency filepaths are retrieved for installed dependencies.
|
* Tests that the expected dependency filepaths are retrieved for installed dependencies.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -17,6 +17,44 @@ require_once __DIR__ . '/base.php';
|
|||||||
*/
|
*/
|
||||||
class Tests_Admin_WPPluginDependencies_HasCircularDependency extends WP_PluginDependencies_UnitTestCase {
|
class Tests_Admin_WPPluginDependencies_HasCircularDependency extends WP_PluginDependencies_UnitTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that false is returned if Plugin Dependencies has not been initialized.
|
||||||
|
*
|
||||||
|
* @ticket 60457
|
||||||
|
*/
|
||||||
|
public function test_should_return_false_before_initialization() {
|
||||||
|
$this->set_property_value(
|
||||||
|
'plugins',
|
||||||
|
array(
|
||||||
|
'dependent/dependent.php' => array(
|
||||||
|
'Name' => 'Dependent',
|
||||||
|
'RequiresPlugins' => 'dependency',
|
||||||
|
),
|
||||||
|
'dependency/dependency.php' => array(
|
||||||
|
'Name' => 'Dependency',
|
||||||
|
'RequiresPlugins' => 'dependent',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure Plugin Dependencies has not been initialized.
|
||||||
|
$this->assertFalse(
|
||||||
|
$this->get_property_value( 'initialized' ),
|
||||||
|
'Plugin Dependencies has been initialized.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
self::$static_properties['circular_dependencies_slugs'],
|
||||||
|
$this->get_property_value( 'circular_dependencies_slugs' ),
|
||||||
|
'"circular_dependencies_slugs" was not set to its default value.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertFalse(
|
||||||
|
self::$instance->has_circular_dependency( 'dependency' ),
|
||||||
|
'false was not returned before initialization.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that a plugin with a circular dependency will return true.
|
* Tests that a plugin with a circular dependency will return true.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -15,6 +15,68 @@ require_once __DIR__ . '/base.php';
|
|||||||
*/
|
*/
|
||||||
class Tests_Admin_WPPluginDependencies_Initialize extends WP_PluginDependencies_UnitTestCase {
|
class Tests_Admin_WPPluginDependencies_Initialize extends WP_PluginDependencies_UnitTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that initialization runs only once.
|
||||||
|
*
|
||||||
|
* @ticket 60457
|
||||||
|
*
|
||||||
|
* @dataProvider data_static_properties_set_during_initialization
|
||||||
|
*
|
||||||
|
* @param string $property_name The name of the property to check.
|
||||||
|
*/
|
||||||
|
public function test_should_only_initialize_once( $property_name ) {
|
||||||
|
$this->assertFalse(
|
||||||
|
$this->get_property_value( 'initialized' ),
|
||||||
|
'Plugin Dependencies has already been initialized.'
|
||||||
|
);
|
||||||
|
|
||||||
|
self::$instance->initialize();
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
$this->get_property_value( 'initialized' ),
|
||||||
|
'"initialized" was not set to true during initialization.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$default_value = self::$static_properties[ $property_name ];
|
||||||
|
|
||||||
|
$this->assertNotSame(
|
||||||
|
$default_value,
|
||||||
|
$this->get_property_value( $property_name ),
|
||||||
|
"\"{$property_name}\" was not set during initialization."
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reset it to its default.
|
||||||
|
$this->set_property_value( $property_name, self::$static_properties[ $property_name ] );
|
||||||
|
|
||||||
|
self::$instance->initialize();
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
$default_value,
|
||||||
|
$this->get_property_value( $property_name ),
|
||||||
|
"\"{$property_name}\" was set during the second initialization attempt."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider.
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
public function data_static_properties_set_during_initialization() {
|
||||||
|
/*
|
||||||
|
* This does not include 'dependency_api_data' as it is only set
|
||||||
|
* on certain pages. This is tested later.
|
||||||
|
*/
|
||||||
|
return self::text_array_to_dataprovider(
|
||||||
|
array(
|
||||||
|
'plugins',
|
||||||
|
'dependencies',
|
||||||
|
'dependency_slugs',
|
||||||
|
'dependent_slugs',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that `$dependency_api_data` is set on certain screens.
|
* Tests that `$dependency_api_data` is set on certain screens.
|
||||||
*
|
*
|
||||||
@ -238,99 +300,4 @@ class Tests_Admin_WPPluginDependencies_Initialize extends WP_PluginDependencies_
|
|||||||
self::$instance->initialize();
|
self::$instance->initialize();
|
||||||
$this->assertSame( $expected_slugs, $this->get_property_value( 'dependent_slugs' ) );
|
$this->assertSame( $expected_slugs, $this->get_property_value( 'dependent_slugs' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that dependents with unmet dependencies are deactivated.
|
|
||||||
*
|
|
||||||
* @ticket 22316
|
|
||||||
*
|
|
||||||
* @covers WP_Plugin_Dependencies::deactivate_dependents_with_unmet_dependencies
|
|
||||||
* @covers WP_Plugin_Dependencies::has_unmet_dependencies
|
|
||||||
* @covers WP_Plugin_Dependencies::get_active_dependents_in_dependency_tree
|
|
||||||
*
|
|
||||||
* @dataProvider data_should_only_deactivate_dependents_with_unmet_dependencies
|
|
||||||
*
|
|
||||||
* @param array $active_plugins An array of active plugin paths.
|
|
||||||
* @param array $plugins An array of installed plugins.
|
|
||||||
* @param array $expected The expected value of 'active_plugins' after initialization.
|
|
||||||
*/
|
|
||||||
public function test_should_deactivate_dependents_with_uninstalled_dependencies( $active_plugins, $plugins, $expected ) {
|
|
||||||
update_option( 'active_plugins', $active_plugins );
|
|
||||||
|
|
||||||
$this->set_property_value( 'plugins', $plugins );
|
|
||||||
self::$instance::initialize();
|
|
||||||
|
|
||||||
$this->assertSame( $expected, array_values( get_option( 'active_plugins', array() ) ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data provider.
|
|
||||||
*
|
|
||||||
* @return array[]
|
|
||||||
*/
|
|
||||||
public function data_should_only_deactivate_dependents_with_unmet_dependencies() {
|
|
||||||
return array(
|
|
||||||
'a dependent with an uninstalled dependency' => array(
|
|
||||||
'active_plugins' => array( 'dependent/dependent.php' ),
|
|
||||||
'plugins' => array(
|
|
||||||
'dependent/dependent.php' => array( 'RequiresPlugins' => 'dependency' ),
|
|
||||||
),
|
|
||||||
'expected' => array(),
|
|
||||||
),
|
|
||||||
'a dependent with an inactive dependency' => array(
|
|
||||||
'active_plugins' => array( 'dependent/dependent.php' ),
|
|
||||||
'plugins' => array(
|
|
||||||
'dependent/dependent.php' => array( 'RequiresPlugins' => 'dependency' ),
|
|
||||||
'dependency/dependency.php' => array( 'RequiresPlugins' => '' ),
|
|
||||||
),
|
|
||||||
'expected' => array(),
|
|
||||||
),
|
|
||||||
'a dependent with two dependencies, one uninstalled, one inactive' => array(
|
|
||||||
'active_plugins' => array( 'dependent/dependent.php' ),
|
|
||||||
'plugins' => array(
|
|
||||||
'dependent/dependent.php' => array( 'RequiresPlugins' => 'dependency, dependency2' ),
|
|
||||||
'dependency2/dependency2.php' => array( 'RequiresPlugins' => '' ),
|
|
||||||
),
|
|
||||||
'expected' => array(),
|
|
||||||
),
|
|
||||||
'a dependent with a dependency that is installed and active' => array(
|
|
||||||
'active_plugins' => array( 'dependent/dependent.php', 'dependency/dependency.php' ),
|
|
||||||
'plugins' => array(
|
|
||||||
'dependent/dependent.php' => array( 'RequiresPlugins' => 'dependency' ),
|
|
||||||
'dependency/dependency.php' => array( 'RequiresPlugins' => '' ),
|
|
||||||
),
|
|
||||||
'expected' => array( 'dependent/dependent.php', 'dependency/dependency.php' ),
|
|
||||||
),
|
|
||||||
'one dependent with two dependencies that are installed and active' => array(
|
|
||||||
'active_plugins' => array(
|
|
||||||
'dependent/dependent.php',
|
|
||||||
'dependency/dependency.php',
|
|
||||||
'dependency2/dependency2.php',
|
|
||||||
),
|
|
||||||
'plugins' => array(
|
|
||||||
'dependent/dependent.php' => array( 'RequiresPlugins' => 'dependency, dependency2' ),
|
|
||||||
'dependency/dependency.php' => array( 'RequiresPlugins' => '' ),
|
|
||||||
'dependency2/dependency2.php' => array( 'RequiresPlugins' => '' ),
|
|
||||||
),
|
|
||||||
'expected' => array(
|
|
||||||
'dependent/dependent.php',
|
|
||||||
'dependency/dependency.php',
|
|
||||||
'dependency2/dependency2.php',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'two dependents, one with an uninstalled dependency, and one with an active dependency' => array(
|
|
||||||
'active_plugins' => array(
|
|
||||||
'dependent/dependent.php',
|
|
||||||
'dependent2/dependent2.php',
|
|
||||||
'dependency2/dependency2.php',
|
|
||||||
),
|
|
||||||
'plugins' => array(
|
|
||||||
'dependent/dependent.php' => array( 'RequiresPlugins' => 'dependency' ),
|
|
||||||
'dependent2/dependent2.php' => array( 'RequiresPlugins' => 'dependency2' ),
|
|
||||||
'dependency2/dependency2.php' => array( 'RequiresPlugins' => '' ),
|
|
||||||
),
|
|
||||||
'expected' => array( 'dependent2/dependent2.php', 'dependency2/dependency2.php' ),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user