A Narrative of Using Composer in a WordPress Plugin

petersuhmThis piece was contributed by guest author Peter Suhm. Peter is a web developer from the Land of the Danes. He is the creator of WP Pusher and a huge travel addict, bringing his work along with him as he goes.
 


The other day I posted a warning about using Composer in WordPress plugins on the WP Pusher blog. This post got a lot of attention and I feel the need to clarify a few points that were not all clear to everyone. The article was also a bit heavy on the technical stuff, so in this post I will try to make my main point more clear by using a simple narrative to illustrate it.

A narrative

photo credit: Doors Open Toronto 2008 - Toronto Archives - (license)
photo credit: Doors Open Toronto 2008 – Toronto Archives(license)

Let’s imagine for a while that you and I are both plugin authors. Both of us have a great idea for a plugin we wish to distribute via WordPress.org. We want to include a few premium features in our plugins that users of the free version can unlock by entering a license key.

We need some code that can handle this process. Both of us realize that this problem have probably already been solved by someone else. None of us are fans of reinventing the wheel, so we head over to Packagist and type in “license manager”. It looks like our assumption was justified. Yoast already has a package that can handle this. We both decide to do a quick composer require yoast/license-manager. Easy peasy. Now we can move on to work on something that really matters- the core features of our respective plugins.

Fast forward, ready to release your plugin, you realize something: Your user doesn’t necessarily have Composer handy when installing your plugin from WordPress.org, so how are they going to get the code for the license manager? This situation is a bit annoying, because the only solution you really see is to just commit the whole Composer generated vendor directory to your plugin and push it to WordPress.org. You know this is not how Composer is supposed to work, but whatever. You don’t really have other options.

Meanwhile, I have come to the same conclusion with my plugin. Just include the license manager code and be done with it.

Fast forward once more, both our plugins now live in the WordPress.org repository and once in a while, someone decides to upgrade to our premium versions. Everything seems to be fine and we are both grateful that we could just use the code that Yoast had generously open sourced, and didn’t have to reinvent the wheel.

One day, you receive a strange e-mail. A customer is experiencing some really strange behavior when trying to unlock your premium features. It makes no sense to you, because no one else ever reported this. After hours of debugging, you finally ask your customer to deactivate everything else, except your plugin, and then: It works! Hmm. Your plugin seems to somehow be incompatible with another plugin. My plugin.

You realize this after hours of going through source code of all the other plugins the customer had installed. When you realize that we both use the license manager, a bell rings. Could this really be it? If so, how come no fatal errors: cannot redeclare class was caused by PHP?

A week earlier, I had bumped the required version of the license manager in my plugin to the latest version, which included some (fictional) breaking changes. After even more debugging and var_dump()’ing, you realize that my version of the license manager is also the version loaded by PHP in your plugin. You find that really strange because you specifically required another version of the license manager with Composer. You don’t really know what to do about this.

Because there really isn’t much you can do about it.

What happened here?

Now that we have all seen the problem, let’s take a moment to go through what actually happened in the narrative. First of all, why didn’t PHP cause a fatal error when two classes obviously had the same name that both of us included the license manager?

The reason for this is that we used an autoloader generated by Composer. This autoloader scans the diretory structure of our dependencies and adds every class to the autoloader. If a class has already been added, Composer will ignore it. Silently. I have written a small code example if you want to see it for yourself. It’s on GitHub.

Why was my version of the license manager included before yours?

This happened because my plugin had a name that caused it to be loaded before yours. Maybe, in the future, we will all name our plugins “Aaaaaa My Plugin” in order to be loaded first!

So to sum up, the main issue here is that we won’t know which version of our dependencies are available to us at which time. It simply depends on factors we can’t fully control as plugin developers.

Is this a Composer specific issue?

No. It really isn’t. WordPress doesn’t have a way of dealing with third party code in plugins or themes. Therein lies the problem. The reason why I’m talking about Composer is that it is gaining a lot of traction these days. If WordPress developers want to use Composer in plugins released via WordPress.org, this needs to be solved somehow. Otherwise, we will see true chaos when all plugins starts to be incompatible with each other because they use different versions. Welcome to debugging hell.

What can we do about this?

Someone who has been really concerned about this and has worked hard to find a potential solution is Coen Jacobs. I decided to reach out to Coen and ask him if he thinks there is anything we can do about this.

Many developers are already including 3rd party code in their plugins. Is this really a problem?

Yes, this is already a problem in the plugins ecosystem. It will become even worse when more people figure out it’s a good idea to put common functionality in separate packages. These packages can then be bundled with multiple plugins and the issue will appear more and more. I’ve been speaking to a couple developers who have already been through debug hell trying to find out what’s causing this issue.

Moving forward, would you suggest developers stop including 3rd party code in their plugins?

I’m a bit torn on this subject. It makes no sense from a developers point of view to tell people to stop bundling shared packages in their plugins. On the other hand, everybody wants the best possible user experience for their users. It’s a tough decision to make for sure.

At this point, I want to push WordPress related development forward. I want to share libraries and use libraries shared by others. Nobody should be reinventing the wheel over and over again. So I would take the risk of running into issues like this, solving the problems as they show up.

This also means that I’ll be doing my damned best to find a long term solution for this issue. More people will start using Composer, more people will bundle libraries with their plugins. This problem will show up more often, so it’s time to fix it.

What can plugin developers do to prevent this problem?

There is a workaround that I have seen some people use already. It basically comes down to moving your dependency to the namespace of your plugin. Danny van Kooten did this for one of his plugins. This is not ideal however. Every time he updates his dependencies, he has to go through all the files and change the namespaces again. Now this is not such a big task for a relatively small library like Pimple, but a massive undertaking for larger libraries.

This can only be done with namespaces though, so you’ll have to make your plugin require PHP 5.3+ as well. I’m not gonna lie, I think every plugin should start doing that sooner or later, but it’s definitely something you need to consider when you decide to do this.

What would the ideal solution be, if there is any?

The ideal situation would be using some sort of dependency manager. There is of course Composer, the most used dependency manager. Composer is very hard, if not impossible, to use for the vast majority of the WordPress users. It’s a developer’s tool after all.

WordPress should make this easier for its end users, while still enabling developers to utilize pretty much any package they want. On this thought, I have started putting together the WordPress Composer Installer plugin, which does all the hard Composer work while people install plugins as they always have. As soon as I am been able to finish this up, I’ll integrate it properly into the whole plugin installer flow.

Now maybe one day, this can be integrated in core WordPress. It has a long way to go, but the proof of concept already works.

Conclusion

If you have been reading this far, first of all: Thank you. Second of all, I hope you now see how this is something that will eventually become a problem. Our current situation is very frustrating, because we simply don’t have the tools we need. Still, I think it’s important that we keep talking about this and make sure that we all, as WordPress developers, understand the potential issues caused by conflicting third party dependencies in our code.

Finally, I want to mention one more time that this is not a Composer issue. It’s a WordPress issue.

15 Comments


  1. I raised this issue months ago, maybe nearly a year ago, calling for a dependency/third part asset manager to be built into core. The dependency/asset should be loaded in the same way we add drop ins. These should be maintained by the author of same. Each one has a universal ID which should only be allocated by a WP team, managing a dependency/asset repository from which they can be loaded/updated. Each theme/plugin that uses a dependency can then flag that it needs a particular dep/asset and will not activate until they are installed/activated (possibly with the option to install/activate). WP uses this data to load the dep/asset only once.

    And what did the WP team say? Well, I guess you can guess.

    Report


    1. It’s a dillemma. Stuff like this can end up becoming very complicated.

      For now, my main agenda is to make people aware of it! :-)

      Report


      1. TGM Plugin activation is just for making sure other plugins are installed and activated. It’s a form of being able to include third-party code, and does use WP to handle it. However, it doesn’t cover any third-party PHP packages the same way Composer does, or third-party JavaScript like npm or Bower does. So there is still a lot of work needed to be done to create a developer-friendly ecosystem that allows for third-party code.

        Report


    2. Thanks for trying Trevor. Alas, we’ve run into similar attitude when trying to help improve core (for example running multisite in a directory).

      Dependencies are extremely trying. With our video player FV Player we are highly dependent on jQuery. It’s amazing how many plugins break even jQuery by reloading their own version (we count on the latest version, which comes loaded by default in WordPress itself). I can only imagine how hard it is to get clean performance when reusing parts of other people’s plugins within your own plugin.

      Conflict resolution (naming conventions) should be built into these bits and pieces so you can’t end up fighting over data.

      It sounds like the namespaces solution is the only safe one. We’ll all be on PHP 7 very soon, so requiring PHP 5.3+ seems okay to me.

      Report


  2. I would love to see a shared Composer-like resource for WordPress assets. A really common would would be Stripe – imagine being able to use either stripe-php-1.18.0 if you want to provide 5.2 support, or the latest if not – or simply just version locking your dependencies :)

    I think the the major reason that this should be a priority for the Core team is how much potential it adds to the ecosystem.

    It does seem unlikely that it will even be a possibility until the minimum version of PHP gets bumped though :(

    Report


  3. I have read you initial article and now this one. I completely agree with the point you make and having an “official” way to manage dependencies would be awesome.

    However, even though I saw you mentioned in the article that it is not a Composer issue, the beginning basically says the opposite. Many people won’t read the article entirely and will just remember “don’t use Composer for plugins development” which is not right.

    Report


  4. Had anybody addressed how adding in things like “libraries” and such introduce issues, including security issues, to that faction that already doesn’t update in the first place?

    We’re still trying to get people to keep their themes and plugins and core up to date in an effective manner. Adding in third party libraries seems like a long-term recipe for a security disaster.

    Witness the large number of themes using outdated third-party sliders. How do you solve that problem, exactly? What is your solution to DLL-HELL?

    Report


    1. If the 3rd-party libraries are depended upon by plugins, then updating those libraries would be the responsibility of those plugins. Users wouldn’t have to deal with them. Dealing with this in core wouldn’t add any mental overhead for end-users.

      Report


    2. That’s true @Otto, but isn’t it restrictive to only say that about 3rd party libraries? I mean, if someone releases a plugin on WP.org and never updates it, when a security issue arises, like say an XSS vulnerability with add_query_arg(), then the security issue remains…

      Updating plugins / themes is the responsibility of the author, and this includes updating 3rd party dependencies. Not updating a plugin is just like not updating a dependency in terms of risks IMO.

      Report


    3. Otto, with all due respect, that’s completely unrelated to this issue. People will bundle libraries anyway, wether they do it via Composer or manually. Composer will just make it easier and following a proven standard, to bundle libraries. Security issues will always be there and need to be dealt with.

      Actually, using Composer will make it easier for plugin developers to keep their dependencies updated. It’s as simple as running ‘composer update’, compared to having to manually replace all the files of the bundled libraries in your plugin. I just realised that if we would install those dependencies on a server level (based on the dependencies of all active plugins, as my proof of concept assumes), we could update them without having plugins to push updates!

      Report


  5. I’m toying with the idea of writing conflict scanner plugin (perhaps “mu” for early binding). Would scanning all plugins’ respective composer.json files for dependencies, gather their version numbers, and the load position/priority of the plugin help point out? The plugin could then notify WordPress users of compatibility issues of shared dependencies. Even better would be to notify the plugin author if they are in conflict (when the site is running the latest version of their plugin and it’s a victim of latent loading that conflicts with another latest version plugin). Perhaps that would encourage authors to synchronize?

    Report


  6. This article was quite useful. I am actually running into this trap more often than not lately.

    Links and comments here have been greatly helpful. I do not know if a solution for this issue will be added to the core anytime soon. However an easier solution to this dependency management hell could be to pay closer attention to how the ClassLoader gets implemented.

    Yes it is not ideal to add our dependencies to the plugin package, however since there’s no better availability do date this is a necessary evil.

    Report


  7. I have thought about this same issue. I think a solution would be to run a php parser that autoprefixes namespaces when your composer requires a new dependency. It may be possible that composer already has functionality like this but I doubt it.

    Report

Comments are closed.