Make WordPress Core


Ignore:
Timestamp:
02/20/2024 07:25:38 AM (2 years ago)
Author:
costdev
Message:

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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-plugin-dependencies.php

    r57617 r57658  
    106106
    107107        /**
    108          * Initializes by fetching plugin header and plugin API data,
    109          * and deactivating dependents with unmet dependencies.
     108         * Whether Plugin Dependencies have been initialized.
     109         *
     110         * @since 6.5.0
     111         *
     112         * @var bool
     113         */
     114        protected static $initialized = false;
     115
     116        /**
     117         * Initializes by fetching plugin header and plugin API data.
    110118         *
    111119         * @since 6.5.0
    112120         */
    113121        public static function initialize() {
    114                 self::read_dependencies_from_plugin_headers();
    115                 self::get_dependency_api_data();
    116                 self::deactivate_dependents_with_unmet_dependencies();
     122                if ( false === self::$initialized ) {
     123                        self::read_dependencies_from_plugin_headers();
     124                        self::get_dependency_api_data();
     125                        self::$initialized = true;
     126                }
    117127        }
    118128
     
    126136         */
    127137        public static function has_dependents( $plugin_file ) {
    128                 return in_array( self::convert_to_slug( $plugin_file ), self::$dependency_slugs, true );
     138                return in_array( self::convert_to_slug( $plugin_file ), (array) self::$dependency_slugs, true );
    129139        }
    130140
     
    173183                $dependents = array();
    174184
    175                 foreach ( self::$dependencies as $dependent => $dependencies ) {
     185                foreach ( (array) self::$dependencies as $dependent => $dependencies ) {
    176186                        if ( in_array( $slug, $dependencies, true ) ) {
    177187                                $dependents[] = $dependent;
     
    370380
    371381        /**
    372          * Displays an admin notice if dependencies have been deactivated.
    373          *
    374          * @since 6.5.0
    375          */
    376         public static function display_admin_notice_for_deactivated_dependents() {
    377                 /*
    378                  * Plugin deactivated if dependencies not met.
    379                  * Transient on a 10 second timeout.
    380                  */
    381                 $deactivate_requires = get_site_transient( 'wp_plugin_dependencies_deactivated_plugins' );
    382                 if ( ! empty( $deactivate_requires ) ) {
    383                         $deactivated_plugins = '';
    384                         foreach ( $deactivate_requires as $deactivated ) {
    385                                 $deactivated_plugins .= '<li>' . esc_html( self::$plugins[ $deactivated ]['Name'] ) . '</li>';
    386                         }
    387                         wp_admin_notice(
    388                                 sprintf(
    389                                         /* translators: 1: plugin names */
    390                                         __( 'The following plugin(s) have been deactivated due to uninstalled or inactive dependencies: %s' ),
    391                                         "<ul>$deactivated_plugins</ul>"
    392                                 ),
    393                                 array(
    394                                         'type'        => 'error',
    395                                         'dismissible' => true,
    396                                 )
    397                         );
    398                 }
    399         }
    400 
    401         /**
    402382         * Displays an admin notice if circular dependencies are installed.
    403383         *
     
    544524                }
    545525
    546                 $all_plugin_data = get_option( 'plugin_data', array() );
    547 
    548                 if ( empty( $all_plugin_data ) ) {
    549                         require_once ABSPATH . '/wp-admin/includes/plugin.php';
    550                         $all_plugin_data = get_plugins();
    551                 }
    552 
    553                 self::$plugins = $all_plugin_data;
     526                require_once ABSPATH . '/wp-admin/includes/plugin.php';
     527                self::$plugins = get_plugins();
    554528
    555529                return self::$plugins;
     
    622596
    623597        /**
    624          * Gets plugin filepaths for active plugins that depend on the dependency.
    625          *
    626          * Recurses for each dependent that is also a dependency.
    627          *
    628          * @param string $plugin_file The dependency's filepath, relative to the plugin directory.
    629          * @return string[] An array of active dependent plugin filepaths, relative to the plugin directory.
    630          */
    631         protected static function get_active_dependents_in_dependency_tree( $plugin_file ) {
    632                 $all_dependents = array();
    633                 $dependents     = self::get_dependents( self::convert_to_slug( $plugin_file ) );
    634 
    635                 if ( empty( $dependents ) ) {
    636                         return $all_dependents;
    637                 }
    638 
    639                 require_once ABSPATH . '/wp-admin/includes/plugin.php';
    640                 foreach ( $dependents as $dependent ) {
    641                         if ( is_plugin_active( $dependent ) ) {
    642                                 $all_dependents[] = $dependent;
    643                                 $all_dependents   = array_merge(
    644                                         $all_dependents,
    645                                         self::get_active_dependents_in_dependency_tree( $dependent )
    646                                 );
    647                         }
    648                 }
    649 
    650                 return $all_dependents;
    651         }
    652 
    653         /**
    654          * Deactivates dependent plugins with unmet dependencies.
    655          *
    656          * @since 6.5.0
    657          */
    658         protected static function deactivate_dependents_with_unmet_dependencies() {
    659                 $dependents_to_deactivate = array();
    660                 $circular_dependencies    = array_reduce(
    661                         self::get_circular_dependencies(),
    662                         function ( $all_circular, $circular_pair ) {
    663                                 return array_merge( $all_circular, $circular_pair );
    664                         },
    665                         array()
    666                 );
    667 
    668                 require_once ABSPATH . '/wp-admin/includes/plugin.php';
    669                 foreach ( self::$dependencies as $dependent => $dependencies ) {
    670                         // Skip dependents that are no longer installed or aren't active.
    671                         if ( ! array_key_exists( $dependent, self::$plugins ) || is_plugin_inactive( $dependent ) ) {
    672                                 continue;
    673                         }
    674 
    675                         // Skip plugins within a circular dependency tree or plugins that have no unmet dependencies.
    676                         if ( in_array( $dependent, $circular_dependencies, true ) || ! self::has_unmet_dependencies( $dependent ) ) {
    677                                 continue;
    678                         }
    679 
    680                         $dependents_to_deactivate[] = $dependent;
    681 
    682                         // Also add any plugins that rely on any of this plugin's dependents.
    683                         $dependents_to_deactivate = array_merge(
    684                                 $dependents_to_deactivate,
    685                                 self::get_active_dependents_in_dependency_tree( $dependent )
    686                         );
    687                 }
    688 
    689                 // Bail early if there are no dependents to deactivate.
    690                 if ( empty( $dependents_to_deactivate ) ) {
    691                         return;
    692                 }
    693 
    694                 $dependents_to_deactivate = array_unique( $dependents_to_deactivate );
    695 
    696                 deactivate_plugins( $dependents_to_deactivate );
    697                 set_site_transient( 'wp_plugin_dependencies_deactivated_plugins', $dependents_to_deactivate, 10 );
    698         }
    699 
    700         /**
    701598         * Gets the filepath of installed dependencies.
    702599         * If a dependency is not installed, the filepath defaults to false.
     
    709606                if ( is_array( self::$dependency_filepaths ) ) {
    710607                        return self::$dependency_filepaths;
     608                }
     609
     610                if ( null === self::$dependency_slugs ) {
     611                        return array();
    711612                }
    712613
     
    847748                }
    848749
     750                if ( null === self::$dependencies ) {
     751                        return array();
     752                }
     753
    849754                self::$circular_dependencies_slugs = array();
    850755
Note: See TracChangeset for help on using the changeset viewer.