Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge plugins and mu-plugins when get_plugins function is called #2

Open
radeno opened this issue Jan 19, 2021 · 1 comment
Open

Merge plugins and mu-plugins when get_plugins function is called #2

radeno opened this issue Jan 19, 2021 · 1 comment

Comments

@radeno
Copy link

radeno commented Jan 19, 2021

This is follow up ticket for #1

Motivation?

Lot of plugins interacts with each others and they checks if plugin is installed and what is more important when different plugin is active.
For example plugins extensions or plugins which uses API of different plugins (dependencies).

By my experience they are using get_plugins function to check if specific plugin is installed and active.

In the current situation mu-plugins are loaded automatically, but WP does know nothing about them.

Implementation?

Curiously it could be very easy.
get_plugins checks every sub-folder in the plugins folder. And this plugin is used heavily. This function doesn't have any filter what is sad, but happily it uses wp_get_cache, so there is a workaround.

Step 1

Copy code from get_plugins (https://developer.wordpress.org/reference/functions/get_plugins/) and change just wp_cache_get and wp_cache_set namespace to mu_plugins and $plugin_root to WPMU_PLUGIN_DIR. Name this function for example as get_mu_autoloader_plugins()

Step 2

Merge both lists together. Eg:

\add_action('init', function() {
    $plugins = \get_plugins();
    $muPlugins = get_mu_autoloader_plugins();

    \wp_cache_set( 'plugins', ['' => array_merge($plugins, $muPlugins)], 'plugins' );
});

This code is not optimized, so cache is rewrite on every request, which is not good.

Drawbacks?

Listed all mu-plugins as regular plugins. Which is not good.

Drawbacks fixes

Append specific array key which will say that that plugin is MU plugin.
Then in the all_plugins filter, just skip this plugins list.

Proof of concept, workable code

// almost 1:1 with https://developer.wordpress.org/reference/functions/get_plugins/
function get_mu_autoloader_plugins( $plugin_folder = '' ) {

	$cache_plugins = wp_cache_get( 'mu_plugins', 'mu_plugins' );
	if ( ! $cache_plugins ) {
		$cache_plugins = array();
	}

	if ( isset( $cache_plugins[ $plugin_folder ] ) ) {
		return $cache_plugins[ $plugin_folder ];
	}

	$wp_plugins  = array();
	$plugin_root = WPMU_PLUGIN_DIR;
	if ( ! empty( $plugin_folder ) ) {
		$plugin_root .= $plugin_folder;
	}

	// Files in wp-content/plugins directory.
	$plugins_dir  = @ opendir( $plugin_root );
	$plugin_files = array();

	if ( $plugins_dir ) {
		while ( ( $file = readdir( $plugins_dir ) ) !== false ) {
			if ( '.' === substr( $file, 0, 1 ) ) {
				continue;
			}

			if ( is_dir( $plugin_root . '/' . $file ) ) {
				$plugins_subdir = @ opendir( $plugin_root . '/' . $file );

				if ( $plugins_subdir ) {
					while ( ( $subfile = readdir( $plugins_subdir ) ) !== false ) {
						if ( '.' === substr( $subfile, 0, 1 ) ) {
							continue;
						}

						if ( '.php' === substr( $subfile, -4 ) ) {
							$plugin_files[] = "$file/$subfile";
						}
					}

					closedir( $plugins_subdir );
				}
			} else {
				if ( '.php' === substr( $file, -4 ) ) {
					$plugin_files[] = $file;
				}
			}
		}

		closedir( $plugins_dir );
	}

	if ( empty( $plugin_files ) ) {
		return $wp_plugins;
	}

	foreach ( $plugin_files as $plugin_file ) {
		if ( ! is_readable( "$plugin_root/$plugin_file" ) ) {
			continue;
		}

		// Do not apply markup/translate as it will be cached.
		$plugin_data = get_plugin_data( "$plugin_root/$plugin_file", false, false );

		if ( empty( $plugin_data['Name'] ) ) {
			continue;
		}

		$plugin_data['IsMuPlugin'] = true;

		$wp_plugins[ plugin_basename( $plugin_file ) ] = $plugin_data;
	}

	uasort( $wp_plugins, '_sort_uname_callback' );

	$cache_plugins[ $plugin_folder ] = $wp_plugins;
	wp_cache_set( 'mu_plugins', $cache_plugins, 'mu_plugins' );

	return $wp_plugins;
}

// this hook is used in the plugins list WP admin page
add_filter('all_plugins', function($get_plugins) {
    return array_filter($get_plugins, function($plugin) { return !isset($plugin['IsMuPlugin']); });
});

// this action merge both plugin list, unoptimized
\add_action('init', function() {
    $plugins = \get_plugins();
    $muPlugins = get_mu_autoloader_plugins();

    \wp_cache_set( 'plugins', ['' => array_merge($plugins, $muPlugins)], 'plugins' );
});
@radeno
Copy link
Author

radeno commented Jan 21, 2021

We can fix when plugins requesting assets with plugins_url function.

Within add_action init we can replace URLs

$muPluginList = array_filter(array_map(function($pluginKey) { return substr($pluginKey, 0, strrpos( $pluginKey, '/') ?: 0); }, array_keys(get_mu_autoloader_plugins())));
\add_filter('plugins_url', function($url, $path, $plugin) use($muPluginList) {
    if (strpos($url, WP_PLUGIN_URL . '/') === false) {
        return $url;
    }

    foreach ($muPluginList as $plugin) {
        $pluginUrl = WP_PLUGIN_URL . '/' . $plugin;
        $muPluginUrl = WPMU_PLUGIN_URL . '/' . $plugin;

        if (strpos($url, $pluginUrl) !== false) {
            return str_replace($pluginUrl, $muPluginUrl, $url);
        }
    }
}, 10, 3);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant