If you answered all of these with yes this talk is 100% for you! Even if you haven't, it will still be interesting and might help you sooner or later as a WordPress developer.
The gateway drug for most people developing for WP probably was just messing with existing themes and plugins. For example by adding some simple helper functions into the functions.php of a theme. I sure know it was for me.
So, as the greenhorn I was I just added simple functions like this. Most of your probably already see my mistake which soon led me here…
Or, looking at the error logs to this. If you use very generic names for your functions there is a very high risk that other code will use the same name. And that will then result in this error. So I learned the hard way to better prefix my functions…
…like this. Or, as I learned later, write modern code using PHP namespaces.
So, to summarize: We do this do prevent what the official plugin developer handbook aptly explains like this:
Okay. Easy peasy I thought. Just prefix or namespace functions when I write them. Done. Continuing my journey as a developer sooner or later I was faced with requirements more complex than simple helper functions. So instead of reinventing the wheel I considered including some 3rd party PHP libraries in my plugin. In the olden days by just downloading a zip file, copying its contents into my plugin and then `require`-ing the files. This is probably still not too terrible for very, very simple stuff.
But what if that library itself isn't self-sufficient and again relies on something else? Maybe another library. Or maybe just some other WordPress plugin?
I somehow need to find, download and require those dependencies as well. And these might then have dependencies themselves…
…which again have dependencies…
…and so on. So even starting with a single library I guess you can already see how this can become quite tedious to maintain manually very quick. Lazy as I am I was already suspecting there must be a better way. Still, it worked for now.
I did this for some time. But pretty soon, as I did `require` the same library in two of my plugins, I had the same problem of name collisions again.
And then it dawned on me - this could also happen any time with some 3rd party plugin! If any other plugin used the same library as any other plugin, including mine, we'd run into a naming conflict.
One traditional way of solving this "the WP way" has been "pluggable" functions using the famous `function_exists` pattern. Before defining a function or including a file you check if the function or something in the file to include is already defined. This way functions aren't created again if they already exist. This solved the issue of loading things twice.
But what if there are different library versions at play?
Functions in different versions of a library might both have the same name but still behave different in some way.
Even worse: What if one plugin uses the newest version of the library, but another plugin an old, potentially broken version?
They might even be completely incompatible. Or they might only be compatible if you just pick the right version that isn't too old but also not too new. In any case you'll probably hit some errors with some combinations.
Since both plugins try to load functions with the same name but differing implementations now, which one gets loaded depends on the execution order of the plugins. You might sometimes even end up in a situation where on some requests you get the version from plugin 1, in others from plugin 2. Apart from *obvious* failures this can often lead to the worst kind of so called [Heisenbugs](https://en.wikipedia.org/wiki/Heisenbug) which only appear sometimes under specific circumstances and are a nightmare to debug. Yes, you can try to make sure that all the plugins using the same library always use the same version. But realistically this isn't always an option, especially together with 3rd party plugins.
Unfortunately when it comes to this a lot of plugins out there behave like the proverbial "bull in the china shop". They often do not care much about collisions with other code as long as their plugin works in isolation. Also blame is usually given to "the others". So, if we can't prevent different versions from being used, what else can we do?
Sometimes there are ways to work around this. We can try to detect them and do our best to work around potential conflicts or at least give meaningful error messages - a.k.a. compatibility checks. To do this we first need to be aware of such conflicts and handle them differently for various combinations of plugins. But needing every plugin to do this on its own is very ineffective and still prone to error.
Let's quickly summarize the issues at hand we've encountered so far: … And then, at some point, probably sooner than later, you'll need to do updates. For all the libraries you use. If only to fix security issues. And then this whole, very laborious process basically starts from scratch.
Like many developers I *abhor* repetitive work. I get annoyed and start thinking: There must be a better way!
Maybe you're now thinking: Well, this all sounds very interesting but also a lot of work for something that mostly sounds like a rare edge case. Honestly, how often does this *actually* happen, like, in the real world.
Well, this actually happens waayyy more often than you might think. There have been multiple occasions in the WP ecosystem where this has caused major breakage. …and you can find many more in the support forums
So let's step back for a moment and look at the big picture. This seems to be a very common issue that hasn't appeared for the first time in the context of WordPress and the general PHP ecosystem. Usually when something is a commonplace issue a lot of people have already thought about the issue at hand and have usually already come up with a good solution.
And indeed there is a class of tools that help with and even solve most of these issues with managing dependencies. They are aptly named dependency management tools. The dependency management tool of choice for PHP is Composer.
Funny story: I actually got introduced to Composer at another WordCamp myself, actually the first WordCamp Europe ever back in 2013. https://europe.wordcamp.org/2013/session/better-site-stacks-with-composer/ Just to know who I'm talking to, some quick questions: - Who of you has heard of Composer before? - Who of those has used it before? - Who of those has used it with WP? So, what does Composer do and how does it work?
Well, basically it solves all the issues we've been worrying up until here. And using it actually isn't that hard - unless you're generally scared of the command line. Which I assume won't be very frequent on the developer track. So, how does it work?
- You create a `composer.json` file in your project - Give your project a name - Define what kind of package you're working on - in our case `wordpress-plugin` - And then you define what other packages your code depends on - the package names - and the versions of the other packages that your package needs While there are some more ways to define the versions and some other things you can tell Composer, for starters this is basically it.
If you then run `composer install` Composer will magically find and download everything your package needs.
Up until here we assumed that both our dependencies have no further dependencies themselves. Let's extend this and imagine the first package has dependencies itself. So the `composer.json` for "someone-else/package-name" looks like this:
The resolution will now be different, but still both can be happy.
But this still isn't all Composer can do for you. Up until here we'd have matching versions resolved and installed. But you'd still need to manually add `require` calls to actually load the files in your plugin.
Short detour: Who has heard of auto-loaders? Auto-Loaders are a great invention to free you from manually managing all those `require` calls. By naming your files consistently (ideally according to the [PSR-4 standard](https://www.php-fig.org/psr/psr-4/)) and telling PHP in which folder to look for them PHP can automatically require those files.
So, if libraries properly name their files and define this in their `composer.json` file (which nowadays most do) Composer automatically generates such an auto-loader for you when running `composer install`. You can then just once require this autoloader with a single line of code and all your code and the code of dependencies can then be used without any further `require` calls. I won't have time to further explain the details of autoloading now, but it's actually not all that complicated and pretty well explained in the Composer documentation.
So by just - writing a couple of lines in a `composer.json` file - using dependencies that have done that as well - running `composer install` on the command line all the problems we worried about earlier are solved. Thanks for listening and enjoy your lunch.
Oh, oh. Wait, sorry! There is one thing I forgot to mention. To actually make this work of course *all* your dependencies need to be installed and managed with Composer. Otherwise, if some dependencies might end up in your site through some other way we're almost back to square one with all the issues of name collisions and so on. And by all I mean all, including all plugins, themes and even WP core itself. The problem though is that WP is way older than Composer and by now, as you're probably all painfully aware, is a massively legacy codebase. So it doesn't support Composer and instead has its own mechanism for installing and managing plugins and themes: The well known Plugins and Themes screens in the backend. This leaves us with basically two ways to still use Composer with WP: - you adapt WP to your needs - you adapt to WPs needs
To enable Composer to have control over all dependencies you need to disable managing (installing, deleting and updating(!)) themes and plugins in the backend and base your whole websites stack on Composer. You then need to install all plugins and themes with Composer. But for this to work you need to be in control of the whole site. But WP isn't well-prepared for this out of the box, so this is another complex topic for which I can't go into the details here now.
If this sounds interesting to you, you can have a closer look at stacks like WP Starter or Bedrock. Also feel free to approach me later. Anyway, that isn't an option for plugins or themes to be distributed for installation on random websites.
If you want your plugin to work anywhere all we can do is to at least handle dependencies on a plugin or theme level. This isn't ideal but currently the best we can do with WordPress. Only using Composer on the plugin level solves the whole dependency resolution, downloading and installing issue. But since it only operates on the plugin level it can't save us from naming collisions and version conflicts with other plugins. We still need another solution for that. So looking at it again we can see that the core of both issues is not being able to have multiple versions of the same code in multiple plugins. But what if we could?
Imagine this: When installing your dependencies with Composer you basically copy all files and prefix the namespaces to include your project namespace. This is really not that difficult since it mostly boils down to a Search&Replace of the namespace. The only remaining thing is to then use those renamed namespaces instead of the originals in all of your code. Like this you can then have multiple versions of the same dependencies without any collisions.
And to nobody's surprise there are already tools that do exactly that. My favourite tool, especially to get started since it is very easy to configure, is Strauss/Mozart. Like this, even if multiple plugins use the same dependencies, since they live in different namespaces, they can peacefully coexist. You can now use dependencies in your code without the risk of unpredictable breakage in combination with other peoples code. Still, we do not have a solution for global dependencies between plugins and themes.
As already mentioned due to the massively legacy architecture of WordPress introducing Composer in WP Core probably won't happen soon, if ever. If you want to read about the why (and maybe cry a little bit) read this ticket. But WordPress wouldn't be WordPress if instead of adopting the tried and proven industry standard it wouldn't try to reinvent the wheel on its own.
So, let me present you the "WordPress Feature Project: Plugin Dependencies".
Again this only solves a specific subset of the problem field: - Plugins that depend on other plugins. Not for Themes. Unlike Composer. - You can only define dependencies on a plugin, not on a specific version(-range). Unlike Composer. - WordPress doesn't handle any of the installing, updating and (de-)activating, just informational messages to guide you. Unlike Composer. You've probably noticed my snarky mentioning the technical inferiority to Composer. But on the other hand, very much in line with WordPress, the way this works is very simple and stays fully inside the comfortable realms of the WP backend.
So, how does it work: When writing your plugin you add a simple additional header to your main plugin file. WordPress could then use this information to help in various ways, e.g. If this sound like something you might find useful please have a look at the Feature Project, try it and give the developers working on it some feedback. But please keep in mind that this is still in a very experimental stage and might never or only in a very different way be merged into WP core. Anyway it probably won't harm adding the plugin header since it will just get ignored if this never gets into WP core.
And that's a wrap. I hope you enjoyed the talk! I know I only skimmed some parts: Especially the technical details of how to use Composer, Autoloaders and the tools for automatically namespacing dependencies. But I believe talking about the problems and underlying concepts is more important in the limited time I have. Once you've grasped the usefulness of all this I am confident you can figure out the rest on your own using the excellent documentation for composer and all other tools mentioned.
So, I hope the talk gave you some food for thought and will help you improve your development workflow.