A Warning About Using Composer With WordPress

NB. I wrote a follow-up piece about this topic on the WP Tavern.

Yesterday, I was watching the WP Sessions stream, where Josh Pollock talked about developing WordPress plugins using Composer. Josh did a great job introducing Composer basics, however, I still feel a need to comment on one specific point that was missing in the presentation: Loading 3rd party dependencies with Composer doesn’t change the fact that WordPress isn’t designed to handle 3rd party dependencies in plugins.

Composer and WordPress

In the presentation, Josh goes over the basics of extracting parts of a plugin to a reusable Composer package. This is basically why Composer exists. In a private (or at least controlled) WordPress project, this is not a problem. This will work. Just require the dependencies and load them from the /vendor directory. If you plan on using Composer and WordPress in a controlled environment, no problems. Check out Rarst’s site.

As Josh pointed out, the /vendor directory should be ignored by Git, so 3rd party code won’t be committed. Except for one case: If you are distributing your plugin via WordPress.org. In that case, even though it kinda sucks and goes against the spirit of Composer, you will have to commit everything – including the 3rd party dependencies in /vendor. But is that really a good idea?

Welcome to debugging hell

What happens when 2 plugins on WordPress.org commits the same dependency, but in 2 different versions, and both of them are installed on your WordPress site? Take a guess. It’s one of the following:

  1. Nothing – they just use their own included versions
  2. PHP will throw a fatal error, because class names will collide
  3. PHP will not throw an error – the autoloader will silently just include the dependency from whichever plugin is loaded first

The right answer is 3. Can you see how this can result in some pretty frustrating debugging situations?

Why does this happen?

The short answer to why this happens is that WordPress has no way of handling 3rd party dependencies. Well, it has 2: Plugins and themes. But it has no way of handling 3rd party dependencies recursively, which is what Composer relies on, i.e. dependencies of dependencies. It doesn’t matter if you use Composer or not. This is why I wrote on the Tavern that this needs to be solved in the near future.

Is it really a problem?

So, for this to be a problem, what would need to happen? All of this at the same time:

  • 2 plugins needs to commit the same dependency, in 2 different versions
  • I need to install both plugins on my WordPress site
  • The plugin which is loaded last relies on functionality, in the other version of the dependency, that is not available or have changed enough to result in errors

Pretty unlikely. In most cases.

But what if a plugin that everyone has installed, say WordPress SEO by Yoast, included an old version of a 3rd party dependency? (they don’t, but play along). Your little plugin simply can’t be incompatible with a giant like SEO by Yoast, so there is only one option for you. You need to rely on the same old version of that dependency, since including another version could result in really strange errors.

Speaking of Yoast, in the presentation, Josh uses them as an example of someone who includes a composer package in a WordPress.org plugin. And truly enough, if you download a zipball of their plugin from WordPress.org, you will find the Composer /vendor directory. But, there is one important point to make here. They only include their own packages (yoast/i18n-module and yoast/license-manager).

I’m not saying that you can’t commit 3rd party dependencies to WordPress.org, but I ask you to consider these implications before your do it. Thanks for reading along.

P.s. If you are attending WordCamp Europe next week, I would love to chat about WordPress, Git and Composer!

  • Thanks for watching my WPSession yesterday. I do want to address what you are claiming here. Brian and I discussed it at the end of the event, but maybe I wasn’t clear.

    I’d like to respectfully disagree with you here. It might be because I’m misunderstanding your point, and how you want Composer to work. But I do want to clear up this confusion.

    Yes, Composer can not check dependencies between two different plugins and yes its lack of recursive update is annoying (I have a grunt script for that if anyone wants it BTW.) Also, since Composer is not fundamental to WordPress core architecture, like it is in other frameworks, it is never going to be ideal. But when done right, it works just fine.

    That said, two plugins, with the same dependent packages will not cause a fatal error when done right. I’m sure there are edge cases and cases of people doing things wrong. For example, while you could use a plugin as a dependency of a plugin, 99% of the time that is a bad idea and is going to break. CMB2 is actually properly architected for that thanks to the way they initialize their main class.

    Having a plugin on WordPress.org ship dependencies is not a problem when done right. For example, two Yoast plugins with their license manager does not cause a problem. Nor does installing two of my plugins, most of which share at least two dependencies.

    Here is a good experiment for you. I have posted the composer.json and the main plugin file for the example plugin I made for my talk: https://gist.github.com/Shelob9/efbd767cc6e566b669be If you make two plugins out of these files, with different file paths/plugin names obviously, and activated both, there will be no errors.

    • Peter Suhm

      Hey Josh! Thanks for taking your time to read the article and reply to me. I appreciate it!

      I think you might be misunderstanding one of my main points in the article, so maybe wasn’t not clear enough. Let me try to clarify:

      No, there will be no errors, because that’s how autoloading works in PHP. Actually, it’s problematic that there will be now errors. What will happen is that the autoloader will silently ignore the classes the second time a plugin tries to load them.

      As I wrote, this will only be an issue if and when 2 plugins includes the same dependency, but in 2 different versions. If I commit your `twitter_core` package in version 1.0 and you commit version 2.0, if your plugin is loaded before mine, we will both be relying on version 2.0. So no, this might not cause errors – but it certainly can.

      So unfortunately, I don’t believe there is a “right” way to commit `/vendor` directories to a WordPress.org repository. Remember, though, that this is not a Composer issue!

      • Yes, I was misunderstanding your main point. That’s about multiple dependency versions.

        On one hand, I don’t see why this has anything to do with Composer though. I make a plugin and copypasta in v1.1 of a dependency, you make a plugin and copypasta in v1.2 of the same dependency. Someone installs both plugins, one one hand the end users has exactly the same issue that you mention.

        On the other hand, if we both used Composer for the dependency, the end user has an easier way of seeing where the dependency is sourced from, and what version, and they might have a one line fix, presuming backwards compat.

        So yes, Composer in WordPress plugins can introduce this issue, but I don’t think that has anything to do with Composer or WordPress, it is a PHP issue. When Composer is involved the issue is either the same level of bad or less bad, as its easier to fix.

        • Peter Suhm

          It’s definitely not a Composer issue (which I’ve mentioned several times).

          I think it’s important that WordPress developers understands that the more plugins commit a `/vendor` directory to WordPress.org, the likelier conflicts will happen. Yoast said yesterday that they will probably put 3rd party code in their _own_ namespace, instead of taking any chances: https://twitter.com/jdevalk/status/611597966126747648

          I don’t see how this is not problematic. When you say “I’m sure there are edge cases and cases of people doing things wrong”, I think doing it wrong is doing it the way you recommend doing it, which unfortunately is probably the only way to do it… Sadly.

          It would be great if WordPress.org had guidelines about committing 3rd party code! (I don’t know if they do)

          • I know you said it’s not a Composer issue, but that’s the title of the post… My point is that as long as plugins have third-party dependencies via Composer, git submodule, copypasta, etc, this is an issue.

            This is a fundamental PHP issue that happens when we have dependencies in packages. So in no way does this make using Composer a bad idea. In fact, I think it helps make the case for Composer, as it provides us with an easier way to diagnose, fix or move on when errors occur.

            Do you have a better solution that doesn’t require non-backwards compatible changes to how plugins work in WordPress or everyone to add a new file to their plugin?

          • Peter Suhm

            You’re definitely right, and I think it’s important to educate people about this when we teach Composer :).

            I don’t have a better solution. It’s a hard problem to solve… But I think we need to discuss it before anything will ever happen. For starters, we could alarm users if there is potential for trouble. This shouldn’t be too hard to implement in our plugins.

    • Peter Suhm

      Sorry, just one more thing: The 2 Yoast plugins doesn’t cause any errors, because they rely on the same version. Only 1 of them will ever be loaded.

    • Coen Jacobs

      First things first: I’ve been talking a lot about this exact issue with Peter in the past and would like to clarify a couple things that you both have missed in this article and comments. I haven’t seen the WP Session, but understand the issue you’re discussing here.

      Imagine the following situation (which is a bit exaggerated to make to problem more clear):
      – plugin-a bundles version 1.0.0 of the Pimple library
      – plugin-b bundles version 3.0.0 of the same Pimple library

      Now both plugins are activated. Because WordPress loads plugins in alphabetical order and let’s assume here that both plugins load their autoloader at the same hook (or at first line of their main plugin file, as long as they load it at the same time). This means that the autoloader of plugin-a will be loaded first, autoloading all files of Pimple 1.0.0. Next up, is the autoloader of plugin-b, which tries to autoload Pimple 3.0.0. All classes with a name that has already been loaded by the plugin-a autoloader, will not load to prevent collisions (Composer does that).

      Since Pimple 1.0.0 and 3.0.0 are two major versions apart from each other, the implementation are most likely not compatible with each other and you’ll get errors.

      Plugin-a might be compatible with Pimple 3.0.0, but that library never gets loaded correctly, because 1.0.0 was already loaded. If the composer.json of both plugins would have been taken into account and Composer had resolved this dependency, they might have ended up with a version of Pimple that’s compatible with both plugins. That’s what Composer is for.

      Now as I said, this is a bit of an extreme example, but you can understand how this can become a problem right?

      So imagine both of us loading library x in our plugins, both requiring a version that’s incompatible with each other. No matter how well tested your plugin is, if I get a chance to load my autoloader before yours, I can load a version of the library that is not compatible with yours and your plugin will break (at least, the end user will see it like that and the horror of finding the culprit begins)…

      There are some clever people who have found a way around this though. Danny has bundled the Pimple library in his own namespace for example, for the Scroll Triggered Boxes plugin. This is a manual (tedious!) task every time he updates that library, but at least he can make sure that the version he tested his plugin on, gets loaded for his plugin.

      CMB2 has also found a way to work around this issue, but they can’t share their dependency management with other plugins, or the rest of WordPress. Let us please find a way to start using Composer for this. It is designed to do this and it does it very well. All these manual solutions for this problem have flaws, all of which can be done way better by Composer.

      If you have a couple minutes, could you please look at my WordPress Composer Installer plugin proof of concept? This is my vision on how we can solve it, most definitely close to how your Grunt script works, only my plugin works from inside a WordPress installation, any installation actually (PHP 5.4+ though).

      Pimple is actually a library which uses semantic versioning, but you and I both know that the WordPress world isn’t very fond of that yet. Lots of plugins have trouble enough making sure that their own updates are backwards compatible, let alone some of their shared packages are. The WordPress ecosystem needs to grow up, embrace these proven standards (they are standards for a reason) and we’ll have a bright future ahead of us.

      I’d like stress one more time that this is not a Composer issue, but an issue that lies deep within the WordPress Plugins API and the fact that there is no native support for dependency management.

      • Peter Suhm

        Thanks for clarifying @coen_jacobs:disqus!

        I have seen how Danny goes around this. It’s both a great fix, but also sad that he needs to do this…

        Your proof of concept is the best thing I’ve seen (and tried) so far, but there is still a long way to go before this is something “normal” WP users will be able to use. (but you know this of course)

        For me, it’s just important that people understand that there is a reason why committing 3rd party dependencies is not recommended. So once again, thanks for clarifying this! And also, I actually thought that it was the PHP autoloader that worked this way, but it makes sense that it’s a composer thing.

        In the short term, a solution could be to scan composer.json files and warn users about conflicting versions, but I don’t even know how helpful that would be.

        • Yes, I always thought namespacing would be the solution to this; will be interested in learning more about what you all are doing here.

          How is composer currently avoiding this problem with dependencies? If I load two separate packages, and each uses it’s own version of Pimple, how is this avoiding the troubles outlined?

          • Peter Suhm

            It will simply give you a warning if it can’t find 2 compatible versions. Problem is when Composer isn’t used for resolving, such as if dependencies are committed to the WordPress.org repository.

  • Pingback: Explaining the WordPress plugins dependencies problem()

  • WOW, plenty of criticism… I think I’ll try to keep it brief as you obviously are theoretically correct, and I think that is important to recognize, but my two points would be.

    Without composer (back pre-common-namespacing), this was a bigger issue, because every class had to prefix a vendor name in the class. (Do you remember this specific brand of hell? I do! I can even remember thinking Composer was lazy… It is, and that is why it is superior).

    Also assuming we accept that this is a common and valid problem (A big leap IMHO), this speaks a lot more to the issue of using so many Envato’ish $5-$55 plugins on a WP site, which will no doubt slow the site down, and introduce far more problems, so I think it focuses on the thin edge of the wedge for lazy or ignorant site-owners that encounter many worse problems than this, due to chronic under-investment in their digital marketing and try to do everything on a shoe-string budget.

    Thanks for writing about this though, it was a good read and obviously provoked a lot of strong opinions

  • Keron Rowskey

    Well ! I’m saying to thanks for being shared this info on internet with us. I gonna write here that, there is many Plugins for wp service. It’s all depend on you which kind of plugin you used. May be there is 2 or more plugins relevant to Yoast. So just try it with full knowledge, while installing.

  • jajouak

    My problem with using composer with wordpress is that some 3rd party libraries that are dependencies of my custom plugins cause errors. The problem is that these libraries contain wp functions and when I use autoload from the root level it loads these dependencies before loading wordpress! Any ideas? I’m using bedrock btw

  • Pingback: Developing WordPress plugins without limiting yourself to the minimum PHP requirements | keesiemeijer()

  • Pingback: Developing plugins without limiting yourself to the minimum WordPress requirements | keesiemeijer()

  • Pingback: Developing plugins without limiting yourself by the minimum WordPress requirements | keesiemeijer()

  • Pingback: Developing plugins without restricting yourself to the minimum WordPress requirements | keesiemeijer()