Dependency Conflict between Plugin and Theme

Viewed 30

I have installed a custom theme and an SMTP plugin on my website and they both include Google API PHP Client for different purposes. Unfortunately, the dependency used by the plugin and theme are of different versions, and they could not be upgraded or downgraded easily.

This results in a conflict, where the plugin loads the package from the Theme instead of its own, and throws errors.

Here is the Composer for the theme.

{
    "require": {
        "google/apiclient": "^2.2.1"
    },
    "scripts": {
        "pre-autoload-dump": "Google\\Task\\Composer::cleanup"
    },
    "autoload": {
        "psr-4": {
            "BH\\": "includes/"
        }
    },
    "extra": {
        "google/apiclient-services": [
            "Sheets"
        ]
    }
}

And the theme makes use of the namespace BH

Is there a way to limit using Composer to load the Google API PHP Client files only for the code executed by the theme (with namespace BH) and not to the plugin that makes use of a different namespace (say ABC).

Please note: I have attempted scoping and it makes the entire situation even more complex.

1 Answers

Is there a way to limit using Composer to load the Google API PHP Client files only for the code executed by the theme (with namespace BH) and not to the plugin that makes use of a different namespace (say ABC).

In short no, no such option exists in Composer. Composer dumps the autoloader based on the project configuration (composer.json and all those below vendor/*/composer.json).

The rest is then how autoloading in PHP works: You use a class (or Interface/Trait/Enum etc.) and if not yet loaded the autoloader is consulted to resolve the class name to the class definition, most often a file on disk. That file then is loaded and parsed by PHP (require etc.).

So there is not much magic here. You can also see this depends on the order of execution which namespace "loads" what. The (default) autoloader btw. can't see (whithout further introspection which is not done due to efficiency reasons) which namespace actually requires what.

If you'd like to inspect the details - especially as you already looked into scoping - Composer has something to offer here on the runtime level.

By default composers autoloader is prefixed. That means it adds itself on top of the autoloader queue. In PHP you can have multiple autoloader implementations. You can configure Composer in your project to not add itself on top of the queue but at the end.

This allows you to add your own autoloader in front, which you can use to diagnose the loading for example. And it would also allow you to load something different, but only every with the same class name.

This is why forking and rewriting namespaces (a.k.a. "scoping") normally works better as the class names are different then - the conflict is gone.

I have attempted scoping and it makes the entire situation even more complex.

Yes it does, however keep in mind, at the end of the day this is a dependency conflict. Not resolving it at its core will just force you to maintain work-arounds. These kind of conflicts don't easily go away until resolved.

And yes, dependency management is hard. We already say programming is hard, but the moment you run into your first dependency conflict, there is another lesson to learn the hard way. Those don't magically disappear and they require maintenance (I don't mean those dependency conflicts that are easy to resolve by lifting requirements or similar, but those dependencies you rely on and then they break).

Related