3Rd Party Dependency Conflict in Developing Wordpress Plugin

3rd party dependency conflict in developing Wordpress Plugin

Welcome to WordPress hell. We have 2018 and WordPress still does not have any dependency management and still didn't notice Composer existence.

WordPress ecosystem simply relies on assumption, that functions/classes names of plugins/themes should be unique. Obviously distributing popular 3rd-part Composer libraries with your plugin/theme is asking for trouble - it is easy to get names collision when other plugin is doing the same. There is no good way out of this situation.

If you want a bulletproof solution for standalone plugins, you should prefix namespaces of every package in your vendor directory with plugin prefix, for example myplygin\vendor. Then GuzzleHttp\Client becomes myplugin\vendors\GuzzleHttp\Client, so there is no risk of name collisions. This will require some work to write script for this (or you may use some existing solutions, like humbug/php-scoper), and you may get many duplicated dependencies (10 plugins may bring the same library 10 times, but with different namespaces), but this is the cost of integrating modern tools and patterns into outdated software.

If you're writing this plugin for yourself and you're controlling final installation, you may try to use Composer for installing WordPress and its plugins. You still may need to fix 3rd-party plugins (via forking) if they're have bundled some composer library, but in the long run it should simplify many things and you may avoid duplicating libraries for every plugin.

Class conflicts with plugin frameworks - PHP / Wordpress

The solution I ended up choosing was to create a bootstrap.php file, have each instance of the framework register (in a global variable) what version it is and its file path. Then I register an action to run after all plugins/theme are loaded which compares all versions and only loads the classes associated with the most recent version.

The only downside I see to this is that I'd have to make sure my framework is backwards compatible, but I planned on doing that anyway.

Here is the code I used in my bootstrap file. Obviously the version number in each instance of the framework would need to correspond to the version number of the framework included.

// register this version of the framework
$GLOBALS['pw_framework_meta']['0.1.2'] = __FILE__;

if ( !function_exists('pw_framework_init') ) {

/**
* Sort the $GLOBALS['pw_framework_meta'] variable for the latest registered
* version of the framework. Include the PW_Framework.php file and then call init()
*/
function pw_framework_init()
{
// get all the different versions registered with $GLOBALS['pw_framework_meta']
$versions = $GLOBALS['pw_framework_meta'];

// sort the versions
uksort($versions, 'version_compare');

// get the latest versions (the last in the array)
$latest = end($versions);

if ( !class_exists('PW_Framework') ) {
require_once( dirname($latest) . '/PW_Framework.php' );
}
PW_Framework::init();
}

// register pw_framework_init() with the 'after_setup_theme' hook
// This way we can ensure that all plugins or themes that might be using PW_Framework
// have had a chance to register with the $GLOBALS['pw_framework_meta'] variable
add_action( 'after_setup_theme', 'pw_framework_init' );
}

Wordpress plugin development jquery conflict

Before using $j you should declare it using .noConflict().

You can also use it for your plugin, Wordpress require it most of the time because it use different libraries.

So at the to of your plugin write:

var $j = jQuery.noConflict(); //$j could be also j, or $r, everything you want, is a variable.

Then replace all your $ with your new variable used in the no conflict.

Hope it help.

How to prevent PHP namespace conflicts (pre-bundled packages)

There is no solution without changing the code. If two versions of ´\Vendor\AnyClass´ do exist in the filesystem, and code is executed to use them both, either an error appears because redeclaring that class is not allowed, or because the expected class is incompatible. It will only work if the interface of the class is implemented the same, i.e. the two codes are compatible. The problem of compatibility is complicated if it isn't only that one class, but an entire tree of objects that may react badly to mixing classes from different versions, even though they offer a compatible interface.

Changing the namespace is changing the code. Who's responsible for that? I can think of some automatic code parser that would be able to add a specific namespace prefix for each plugin, but that task hasn't been done to my knowledge in PHP. The Java guys in my company made some remarks that such a problem has been solved there, but I have no details.

Also, it doubles your code base, and the duplicated code has to share only the one opcode cache you have.

I know that the core developers of Wordpress are still struggling with this problem. There are some coded suggestions of how to use Composer for dependency management (i.e. plugins and their dependencies), but I don't think they made enough progress for now.

Essentially you have two choices: 1. Create a code namespace prefixer, parse all the files belonging to a plugin (so the plugin author has to include his dependencies somehow), change the code, live with the code duplication, and see what awaits you when it comes to debugging. The downside is that no code outside of that plugin will be easily able to use the plugin code directly because that would mean to know the created prefix. 2. Implement some form of dependency management, preferably using Composer, and don't change the namespaces.

Update: I consulted my Java co-workers again, and they basically made the same statement about Java that I made about PHP: You cannot have two different versions of a class under the same class name, and there is no "magic" even for Java but renaming the class to a different namespace.

How to require a composer package inside a plugin?

You can't. Not at the moment at least, if you distribute your plugin using zips. If you plan on distributing solely through composer, you can use the 'static-plugins' folder to install your plugin in your project through composer, which will also install the deps.

Now, in case you want to distribute through the shopware store, you will have to supply the vendor folder in your plugin zip. You'll also have to actively include the autoloader in your plugin class.

The following code snippet is placed above the class declaration of your plugin class, and loads the composer autoloader of the plugin - if it exists. This means you can still also distribute through composer, but also through a zip-file.

if (file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
$loader = require_once dirname(__DIR__) . '/vendor/autoload.php';
if ($loader !== true) {
spl_autoload_unregister([$loader, 'loadClass']);
$loader->register(false);
}
}

Example/source: https://github.com/runelaenen/shopware6-two-factor-auth/blob/master/src/RuneLaenenTwoFactorAuth.php

On packaging your zip, don't forget to include the vendor folder, and you're ready to distribute your plugin.

Do keep in mind though, that you cannot use a package twice in one system, class names have to be unique. So only include packages (and dependencies of them) that you are fairly certainly not used by core Shopware or other packages. Or use a tool like PHP Scoper to scope your plugin vendor folder so everything is always unique, even if it's already used in a system.


Roadmap

Shopware has composer integration on the roadmap, so this might soon be out of date. At time of writing (feb 2021) the above method is however the way to go.



Related Topics



Leave a reply



Submit