Skip to content

Commit

Permalink
Autoloader: use all of the map files to populate the global maps (#15002
Browse files Browse the repository at this point in the history
)

* Autoloader: loop through all map files in enqueue_files()

The latest autoloader needs to loop through all of the classmap and
filemap files created by the new autoloader and populate the globals.
Add a loop through the active plugins to the enqueue_files() function.

* Autoloader: improve enqueue_files

Improve enqueue_files() by:
 - checking the map files with is_readable instead of file_exists
 - checking that the local map variables are arrays before using them
 - move the file_loader() block out of the active plugins loop

* Autoloader: remove plugins_loaded action conditional around file_loader

We no longer need to wait until the plugins_loaded action to call file_loader(),
so remove the conditionals and the plugins_loaded hook.

* Autoloader: Handle an activating plugin

When a plugin is in the process of activating, it won't be in the active
plugins list yet. We need to make sure that its classmap and filemap files
are processed and the globals are updated so that the autoloader can use
them with the newly activated plugin.

 - In enqueue_files(), process the current plugin's map file and the map files
   for all active plugins.
 - In set_up_autoloader(), detect if a new autoloader has been loaded by an
   activating plugin. If it has, reset the global classmap.

* Autoloader: handle an activating plugin

When a plugin is in the process of activating, it's not in the active
plugins list. We need to add the activating plugin's packages to the
autoloader before the plugin loads.

* Added a check against bulk activation.

* Added network activated plugins to the active plugin list.

* Added a nonce 'check'.

Co-authored-by: Igor Zinovyev <zinigor@gmail.com>
  • Loading branch information
kbrown9 and zinigor committed May 22, 2020
1 parent bc00025 commit 8f040fb
Showing 1 changed file with 144 additions and 31 deletions.
175 changes: 144 additions & 31 deletions packages/autoloader/src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function file_loader() {
global $jetpack_packages_filemap;
foreach ( $jetpack_packages_filemap as $file_identifier => $file_data ) {
if ( empty( $GLOBALS['__composer_autoload_files'][ $file_identifier ] ) ) {
require $file_data['path'];
require_once $file_data['path'];

$GLOBALS['__composer_autoload_files'][ $file_identifier ] = true;
}
Expand Down Expand Up @@ -134,38 +134,139 @@ function autoloader( $class_name ) {
* Used for running the code that initializes class and file maps.
*/
function enqueue_files() {
// TODO: Needs to traverse through the classmaps/filemaps of all of the active plugins.
$class_map = require dirname( __FILE__ ) . '/composer/jetpack_autoload_classmap.php';
$active_plugins = get_active_plugins();
$paths = array_map( __NAMESPACE__ . '\create_map_path_array', $active_plugins );

foreach ( $class_map as $class_name => $class_info ) {
enqueue_package_class( $class_name, $class_info['version'], $class_info['path'] );
foreach ( $paths as $path ) {
if ( is_readable( $path['class'] ) ) {
$class_map = require $path['class'];

if ( is_array( $class_map ) ) {
foreach ( $class_map as $class_name => $class_info ) {
enqueue_package_class( $class_name, $class_info['version'], $class_info['path'] );
}
}
}

if ( is_readable( $path['file'] ) ) {
$file_map = require $path['file'];

if ( is_array( $file_map ) ) {
foreach ( $file_map as $file_identifier => $file_data ) {
enqueue_package_file( $file_identifier, $file_data['version'], $file_data['path'] );
}
}
}
}

file_loader();
}

/**
* Returns an array containing the active plugins. If plugin is activating, it
* is included in the array.
*
* @return Array An array of plugin names as strings.
*/
function get_active_plugins() {
$active_plugins = array_merge(
is_multisite()
? array_keys( get_site_option( 'active_sitewide_plugins', array() ) )
: array(),
(array) get_option( 'active_plugins', array() )
);
$current_plugin = get_current_plugin();

if ( ! in_array( $current_plugin, $active_plugins, true ) ) {
// The current plugin isn't active, so it must be activating. Add it to the list.
$active_plugins[] = $current_plugin;
}

$autoload_file = dirname( __FILE__ ) . '/composer/jetpack_autoload_filemap.php';
// If the activating plugin is not the only activating plugin, we need to add others too.
$active_plugins = array_unique( array_merge( $active_plugins, get_activating_plugins() ) );

$include_files = file_exists( $autoload_file ) ? require $autoload_file : array();
return $active_plugins;
}

foreach ( $include_files as $file_identifier => $file_data ) {
enqueue_package_file( $file_identifier, $file_data['version'], $file_data['path'] );
/**
* Creates an array containing the paths to the classmap and filemap for the given plugin.
* The filenames are the names of the files generated by the Jetpack Autoloader version >2.0.
*
* @param String $plugin The plugin string.
* @return Array An array containing the paths to the plugin's classmap and filemap.
*/
function create_map_path_array( $plugin ) {
$plugin_path = plugin_dir_path( trailingslashit( WP_PLUGIN_DIR ) . $plugin );

return array(
'class' => trailingslashit( $plugin_path ) . 'vendor/composer/jetpack_autoload_classmap.php',
'file' => trailingslashit( $plugin_path ) . 'vendor/composer/jetpack_autoload_filemap.php',
);
}

/**
* Checks whether the current plugin is active.
*
* @return Boolean True if the current plugin is active, else false.
*/
function is_current_plugin_active() {
$active_plugins = (array) get_option( 'active_plugins', array() );
$current_plugin = get_current_plugin();

return in_array( $current_plugin, $active_plugins, true );
}

/**
* Returns the name of activating plugin if a plugin is activating via a request.
*
* @return Array The array of the activating plugins or empty array.
*/
function get_activating_plugins() {

// phpcs:disable WordPress.Security.NonceVerification.Recommended

$action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : false;
$plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : false;
$nonce = isset( $_REQUEST['_wpnonce'] ) ? $_REQUEST['_wpnonce'] : false;

/**
* Note: we're not actually checking the nonce here becase it's too early
* in the execution. The pluggable functions are not yet loaded to give
* plugins a chance to plug their versions. Therefore we're doing the bare
* minimum: checking whether the nonce exists and it's in the right place.
* The request will fail later if the nonce doesn't pass the check.
*/

// In case of a single plugin activation there will be a plugin slug.
if ( 'activate' === $action && ! empty( $nonce ) ) {
return array( wp_unslash( $plugin ) );
}

// TODO: The plugins_loaded action checks aren't necessary anymore.
if (
function_exists( 'has_action' )
&& function_exists( 'did_action' )
&& ! did_action( 'plugins_loaded' )
&& false === has_action( 'plugins_loaded', __NAMESPACE__ . '\file_loader' )
) {
// Add action if it has not been added and has not happened yet.
// Priority -10 to load files as early as possible in case plugins try to use them during `plugins_loaded`.
add_action( 'plugins_loaded', __NAMESPACE__ . '\file_loader', 0, -10 );
$plugins = isset( $_REQUEST['checked'] ) ? $_REQUEST['checked'] : array();

} elseif (
! function_exists( 'did_action' )
|| did_action( 'plugins_loaded' )
) {
file_loader(); // Either WordPress is not loaded or plugin is doing it wrong. Either way we'll load the files so nothing breaks.
// In case of bulk activation there will be an array of plugins.
if ( 'activate-selected' === $action && ! empty( $nonce ) ) {
return array_map( 'wp_unslash', $plugins );
}

// phpcs:enable WordPress.Security.NonceVerification.Recommended

return array();
}

/**
* Returns the name of the current plugin.
*
* @return String The name of the current plugin.
*/
function get_current_plugin() {
if ( ! function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}

$dir = explode( '/', plugin_basename( __FILE__ ) )[0];
$file = array_keys( get_plugins( "/$dir" ) )[0];
return "$dir/$file";
}

/**
Expand All @@ -175,19 +276,31 @@ function set_up_autoloader() {
global $latest_autoloader_version;
global $jetpack_packages_classmap;

if ( ! is_current_plugin_active() ) {
// The current plugin is activating, so reset the autoloader.
$latest_autoloader_version = null;
$jetpack_packages_classmap = array();
}

$classmap_file = trailingslashit( dirname( __FILE__ ) ) . 'composer/jetpack_autoload_classmap.php';
$autoloader_packages = require $classmap_file;

$loaded_autoloader_version = $autoloader_packages['Automattic\\Jetpack\\Autoloader\\AutoloadGenerator']['version'];

$autoloader_version = $loaded_autoloader_version;
$autoloader_path = __FILE__;
$current_autoloader_version = $autoloader_packages['Automattic\\Jetpack\\Autoloader\\AutoloadGenerator']['version'];
$current_autoloader_path = trailingslashit( dirname( __FILE__ ) ) . 'autoload_packages.php';

// Find the latest autoloader.
if ( ! $latest_autoloader_version ) {
$active_plugins = (array) get_option( 'active_plugins', array() );
$autoloader_version = $current_autoloader_version;
$autoloader_path = $current_autoloader_path;
$current_plugin = get_current_plugin();

$active_plugins = get_active_plugins();

foreach ( $active_plugins as $plugin ) {
if ( $current_plugin === $plugin ) {
continue;
}

$plugin_path = plugin_dir_path( trailingslashit( WP_PLUGIN_DIR ) . $plugin );
$classmap_path = trailingslashit( $plugin_path ) . 'vendor/composer/jetpack_autoload_classmap.php';
if ( file_exists( $classmap_path ) ) {
Expand All @@ -204,13 +317,13 @@ function set_up_autoloader() {
}

$latest_autoloader_version = $autoloader_version;
if ( __FILE__ !== $autoloader_path ) {
if ( $current_autoloader_path !== $autoloader_path ) {
require $autoloader_path;
}
}

// This is the latest autoloader, so generate the classmap and filemap and register the autoloader function.
if ( empty( $jetpack_packages_classmap ) && $loaded_autoloader_version === $latest_autoloader_version ) {
if ( empty( $jetpack_packages_classmap ) && $current_autoloader_version === $latest_autoloader_version ) {
enqueue_files();

spl_autoload_register( __NAMESPACE__ . '\autoloader' );
Expand Down

0 comments on commit 8f040fb

Please sign in to comment.