Some web applications are like gated castles, or walled gardens, where there are limited opportunities for the person running the site day to day to install something without the developer. Generally speaking, this is a good thing.
But WordPress, by empowering its users, is more like a community garden, open to many developers, through the concepts of plugins and themes. You are likely not the only developer whose code is going to run on a given WordPress site. And, you are probably not the only plugin developer using Composer as part of your toolkit.
When developing WordPress plugins, it can be tempting to just install one or more PHP packages with Composer, and continue on your merry way.
If you have complete control of the entire application stack, and you are absolutely positive that your site admin will never install a plugin without your say so, then good on you.
If however, you’re like me, and you want to distribute a plugin to more than one site, and you don’t have complete and total control over all the code on one or more of those sites, you may be setting yourself up for a trip to dependency hell.
Although the chances may seem somewhat slim, it is not outside the realm of possibility, that you and another developer, may install completely incompatible versions of the exact same package.
Which one loads first? What will be the result of the conflict?
Welcome to the ninth level of dependency hell.
Enter Mozart, and the Imposter Plugin.
Mozart, and Imposter Plugin, are two additional tools for your toolkit. They both aim to wrap the namespaces of your package dependencies into your own namespace, thus avoiding a trip to dependency hell.
They both require a little extra configuration in your composer.json, and they can take a little getting used to.
Here’s a somewhat contrived example using Imposter Plugin to wrap the league/container package namespace within my own project’s namespace:
{
"name": "richaber/project-name",
"description": "Demo project",
"authors": [
{
"name": "Richard Aber",
"homepage": "https://richaber.com",
"role": "Developer"
}
],
"require": {
"php": ">=7.1",
"league/container": "^3.3",
"psr/container": "^1.0",
"psr/container-implementation": "^1.0",
"typisttech/imposter-plugin": "^0.3.1"
},
"autoload": {
"psr-4": {
"RichAber\\ProjectName\\": "src/"
}
},
"extra": {
"imposter": {
"namespace": "RichAber\\ProjectName\\Vendor",
"excludes": [
"psr/container-implementation"
]
}
}
}
After running composer update
, I now have access to the league/container thusly:
<?php
namespace RichAber\ProjectName;
use RichAber\ProjectName\Vendor\League\Container\Container as Container;
Now, if another plugin decides to implement the league/container package, my own plugin will ignore the version they’ve included, and run the version that is within my own namespace.
You will note in my example, that I’ve put psr/container-implementation
inside of the excludes
block. This is because psr/container-implementation
is a virtual package, there are no actual files on the filesystem for Imposter Plugin to process there, which can cause an error like File does not exist at path /path/to/project/wp-content/plugins/project-name/vendor/psr/container-implementation/composer.json
.
Of course there are some packages these tools simply can’t help you with, extraordinarily complex packages, and packages that do some unusual and/or unexpected things inside of them that the tool authors could not foresee or account for.
But if you are building a plugin, and you don’t have 100% complete control over the entire application stack’s code, these tools are worth knowing about.
Update 07/02/19
Shortly after writing this post I came across another tool which could also be used for this purpose, called PHP-Scoper. Although I have not had a chance to test this PHP-Scoper yet, it looks like another good tool for your toolbox and thought I should mention it here in case one of the other tools doesn’t quite meet your needs. Give this one a look too.
Update 11/09/21
Just throwing another tool in the toolbox, a fork of Mozart, called Strauss… https://github.com/BrianHenryIE/strauss