Make WordPress Core

Opened 23 months ago

Closed 22 months ago

Last modified 22 months ago

#57249 closed defect (bug) (invalid)

plugin_folder parameter in get_plugins returns empty array

Reported by: antonynz's profile antonynz Owned by:
Milestone: Priority: normal
Severity: minor Version: 6.2
Component: Plugins Keywords:
Focuses: Cc:

Description

WordPress 2.6 added a caching mechanism for the get_plugins function. However this change would have stopped the plugin_folder parameter returning the desired result for a chosen plugin.

i.e get_plugins('classic-editor/classic-editor.php') returns empty when that plugin is installed.

Looking at the code, the wp_plugins data is added to the cache inside of an additional array, with the key matching the plugin_folder passed to the function:

$cache_plugins[ $plugin_folder ] = $wp_plugins;

When no plugin_folder is passed it will be set to "", and the plugins data will be nested inside of an empty key array.

Which means the parameter check earlier on wouldn't be able to check the nested array and only returns true if empty

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

This means the cache is probably inadvertently working, since leaving the plugin_parameter empty will match the empty parent array key of the cache i.e $cache_plugins[""] returns the cached array of the plugins.

This also means that anytime the parameter changes in the same request the cache will be bypassed, and the full function run as the parent key changes: eg on the same request:
get_plugins() - cached
get_plugins('test') - bypassed
get_plugins() - bypassed
get_plugins() - cached

As a fix I suggest removing the parent array of the cache and replacing:

$cache_plugins['plugin_folder'] = $wp_plugins;

with:

$cache_plugins = $wp_plugins;

And replacing the following the following:

if ( ! $cache_plugins = wp_cache_get('plugins', 'plugins') )
		$cache_plugins = array();

with something similar to return the full cached results if the plugin_parameter is empty:

if ( ! $cache_plugins = wp_cache_get('plugins', 'plugins') ) {
	$cache_plugins = array();
} else if($plugin_folder == ""){
	return $cache_plugins;
}

Change History (3)

#1 follow-up: @petitphp
23 months ago

Hi, Welcome back to WordPress Trac.

WordPress 2.6 added a caching mechanism for the get_plugins function. However this change would have stopped the plugin_folder parameter returning the desired result for a chosen plugin.

i.e get_plugins('classic-editor/classic-editor.php') returns empty when that plugin is installed.

It doesn't seem to be the way the parameter $plugin_folder is intended to be used.

Looking at usage in Core, $plugin_folder expect a relative path to a plugin folder : get_plugins('/classic-editor');.

This will only return plugin data for this plugin.

Looking at the code, the wp_plugins data is added to the cache inside of an additional array, with the key matching the plugin_folder passed to the function:

$cache_plugins[ $plugin_folder ] = $wp_plugins;

When no plugin_folder is passed it will be set to "", and the plugins data will be nested inside of an empty key array.

Which means the parameter check earlier on wouldn't be able to check the nested array and only returns true if empty

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

This means the cache is probably inadvertently working, since leaving the plugin_parameter empty >will match the empty parent array key of the cache i.e $cache_plugins[""] returns the cached array >of the plugins.

As a fix I suggest removing the parent array of the cache and replacing:

$cache_plugins['plugin_folder'] = $wp_plugins;

with:

$cache_plugins = $wp_plugins;

And replacing the following the following:

if ( ! $cache_plugins = wp_cache_get('plugins', 'plugins') )
		$cache_plugins = array();

with something similar to return the full cached results if the plugin_parameter is empty:

if ( ! $cache_plugins = wp_cache_get('plugins', 'plugins') ) {
	$cache_plugins = array();
} else if($plugin_folder == ""){
	return $cache_plugins;
}

There might be ways to optimize further this function by refactoring how the cache is used. However, it should be done in a way that preserve the current array shape when for each case (for all plugins and for a specific plugin).

This also means that anytime the parameter changes in the same request the cache will be bypassed, >and the full function run as the parent key changes: eg on the same request:
get_plugins() - cached
get_plugins('test') - bypassed
get_plugins() - bypassed
get_plugins() - cached

I couldn't reproduce the issue locally.

Doing multiple calls to get_plugins, the cache behaved as expected :

wp_cache_flush();
get_plugins(); //miss cache
get_plugins('/classic-editor'); //miss cache
get_plugins(); //hit cache
get_plugins(); //hit cache
get_plugins('/classic-editor'); //hit cache

#2 in reply to: ↑ 1 ; follow-up: @antonynz
22 months ago

  • Resolution set to invalid
  • Status changed from new to closed

Replying to petitphp:

Looking at usage in Core, $plugin_folder expect a relative path to a plugin folder : get_plugins('/classic-editor');.

You're right. I should have looked further into the function. The documentation didn't have any specific examples and I had assumed it grabbed the cached version of all the plugin details based on the line below, and how the array keys were stored when all plugins were retrieved i.e (classic-editor/classic-editor.php):

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

get_plugins('/classic-editor'); does return the plugin details as expected (with the folder pruned from the array) so the returned array is different when a folder is added to the request, meaning any changes to the function would be difficult to implement.

wp_cache_flush();
get_plugins(); //miss cache
get_plugins('/classic-editor'); //miss cache
get_plugins(); //hit cache
get_plugins(); //hit cache
get_plugins('/classic-editor'); //hit cache

I can also replicate this. I had assumed it would clear the cached array but it just appends the new array each time so future duplicate requests return a cache hit.

This means plugins not in a directory (eg hello.php) would only be retrievable from requesting all plugins get_plugins(); ?

It's an unusual set up with the cache and didn't work how I initially expected but happy to mark this resolved.

Last edited 22 months ago by SergeyBiryukov (previous) (diff)

#3 in reply to: ↑ 2 @SergeyBiryukov
22 months ago

  • Milestone Awaiting Review deleted

Thanks for the follow-up!

Replying to antonynz:

Looking at usage in Core, $plugin_folder expect a relative path to a plugin folder : get_plugins('/classic-editor');.

You're right. I should have looked further into the function. The documentation didn't have any specific examples

Sounds like it might be a good idea to improve the function description and add an example or two, feel free to reopen the ticket if you have any suggestions to make the description more helpful.

Note: See TracTickets for help on using tickets.