Roots Discourse

Privately hosted plugins which have composer deps


I have followed the guide here and the follow-up discussion, but I am unclear how best to handle the case where the private custom plugin has its own composer dependencies.

When specifying the plugin’s dependencies in the plugin’s own composer.json file, and adding the custom plugin as a dependency to the main roots project composer.json, composer installs the plugin’s dependencies in the roots vendor directory. Is this recommended? How should the plugin reference the autoloader, or should it assume it’s already available?

I had been including the plugin’s own vendor directory in the plugin directory itself (renamed “libs” to avoid .gitignore issues).

Any pointers greatly received!


I usually add the dependencies for custom plugins in both the root composer.json and in the plugin’s composer.json (but I never run composer install inside the plugin, I just keep it there for future reference). This way you only need to run composer update in your project root, instead of twice.

If you use the plugin in non-bedrock installs, you could conditionally require vendor/autoload.php from your plugin. You can run composer install from your plugin directory, and composer will autoload your dependencies from the plugin directory.

if (file_exists(__DIR__ . '/vendor/autoload.php')) {
    require 'vendor/autoload.php';
1 Like

This is the desired behavior. Generally composer dependencies should not be concerned with loading their dependencies: they should act as though their dependencies will be available when they run. In this context, a plug-in is a dependency.

In practice, it’s going to depend on how the plug-in is written. Using composer to manage WordPress stuff is extremely useful, but sometimes it doesn’t line up as well as we’d like because WordPress has its own paradigm for dependency management (which is: do it all yourself). If a plug-in is written as a WordPress plug-in first and a composer package second, then it probably has a hardcoded call to load its own autoload.php, which will likely break if its dependencies get installed to bedrock’s vendor. On the other hand, if the plug-in is written as a true composer package, you should have nothing to worry about.


On our private plugins to customize things we looked into imposter-plugin and Mozart which both do some tricky rewrites to namespace all your private plugin dependencies and isolate them from the global autoloader.

The issue I’ve run into with the root level composer myself is when dependencies conflict but if you manage the private plugin yourself then it might be easy to resolve those.

I have run into trouble with all three options and wish there was more out there, but hopefully these other two optinos help.


Except if my plugin assumes its composer dependency is available via the root auto loader the plugin won’t work in any non-roots/composer based setup.

That’s correct: Something written as a composer package is meant to behave as part of that composer ecosystem, not some other ecosystem (i.e. WordPress). If you’re writing the plugin yourself, you need to think about how you want to approach that, which will probably be influenced by where the library is going to be used. My own personal experience was that the “plugins” I was writing were just plugins by virtue of the fact that I wanted their logic separate from the theme–they would never be distributed outside of our agency environment, which was exclusively sage/bedrock-based. If you want to distribute it, the math is different: WordPress’s plugin paradigm is fairly opinionated about plugins being things unto themselves: It has no concept of interdependence and therefor interacts poorly with composer in certain situations. Bedrock’s ability to install plugins from wpackagist is in a sense dependent on that: It can install plugins without interdependencies easily by just shuttling them off to plugins or wp-plugins on install. It’s once you start installing “plugins” that make use of composer’s dependency resolution that you start running into the rough edges. As @EHLOVader said, there isn’t a simple, catchall solution to these problems: They’re going to depend on what the plugin you need is, how it’s being used, who’s writing it, etc.

1 Like

This is the situation I’m in too, so I’ll go with the global composer route.

Really appreciate everybody’s time discussing the different options.


This topic was automatically closed after 42 days. New replies are no longer allowed.