Working on a classifieds CMS, heavily inspired by Wordpress (osclass.org not to name it), I stumbled upon your thread. We are in the exact same situation, having to deal with multiple plugins using the same dependency.
To explain the solution we've pulled out, let's we've got two plugins :
- P1 uses dependency D1 in version 1.20
- P2 uses dependency D1 in version 1.30
We have to make sure that version 1.30 is loaded for both plugins to work properly.
1. Plugins loading order
Wordpress (and Osclass) have a active_plugins
option (in wp_options
table) that defines active plugins and more importantly in our case : plugin loading order.
This is a simple array.
2. Version check
At the start of our two plugins, we check the version of D1 to be sure that the version is what we need (or superior).
// Custom autoloader, we'll check it in a moment.
require __DIR__ . "/vendor/composer_components/madhouse/autoloader/autoload.php";
if(!function_exists("mdh_utils") || (function_exists("mdh_utils") && strnatcmp(mdh_utils(), "1.30") === -1)) {
// Dependency version is lower than 1.30 (which we need), let's bump.
mdh_bump_me();
} else {
// Rest of the plugin code.
}
Simple case: what happens when P2 is loaded before P1 ?
- P2 is loaded and D1 with it. Note: loaded version of D1 is 1.30.
- P1 is loaded next, checks if D1 is loaded and version is 1.20 or higher and find the version 1.30 then loads normally.
Everything's good.
Interesting case: What happens when P1 is loaded before P2 ?
- P1 is loaded before P2, our dependency and our plugin P1 are loaded normally. Note: loaded version of D1 is 1.20.
- P2 is loaded next, it checks D1 version and find out that D1 has been loaded but version is not high enough (1.20 instead of the required 1.30).
Here comes the bump version.
3. Bump !
Remember, plugins are loaded in the order defined in active_plugins
.
The bump function – in this case mdh_bump_me() – will bump the current plugin to the top of the active_plugins
(first position of the array).
Looks like this.
This is Osclass-related code but it can easily be modified to work with Wordpress :
function mdh_bump_me()
{
// Sanitize & get the {PLUGIN_NAME}/index.php.
$path = str_replace(osc_plugins_path(), '', osc_plugin_path(__FILE__));
if(osc_plugin_is_installed($path)) {
// Get the active plugins.
$plugins_list = unserialize(osc_active_plugins()); // This is the active_plugins fields unserialized.
if(!is_array($plugins_list)) {
return false;
}
// Remove $path from the active plugins list
foreach($plugins_list as $k => $v) {
if($v == $path) {
unset($plugins_list[$k]);
}
}
// Re-add the $path at the beginning of the active plugins.
array_unshift($plugins_list, $path);
// Serialize the new active_plugins list.
osc_set_preference('active_plugins', serialize($plugins_list));
if(Params::getParam("page") === "plugins" && Params::getParam("action") === "enable" && Params::getParam("plugin") === $path) {
//osc_redirect_to(osc_admin_base_url(true) . "?page=plugins");
} else {
osc_redirect_to(osc_admin_base_url(true) . "?" . http_build_query(Params::getParamsAsArray("get")));
}
}
}
In a nutshell,
- Get current active_plugins list.
- Remove the current plugin from it.
- Add the current plugin at the beginning of the array.
- Save the option to the database.
- Redirect to the same page.
5. Custom Autoloader
A word on our custom autoloader.
Composer autoloader loads every class (even if it has been already defined), the new replacing the old one.
Our custom autoloader only loads a class if does not exist yet. Loads the first instance of each class and ignore every other ones.
Hope it's clear enough.
Far-far-far from an ideal solution but works for us and it feels like a step forward.