Make WordPress Core


Ignore:
Timestamp:
02/20/2024 07:25:38 AM (16 months 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.