How to Make Your MU-Plugins Compatible with Composer (And Why)

#
By Iain Poulson, Product Manager

We are big advocates of managing dependencies in our projects with the package manager Composer. It’s essential to make sure the same version of dependencies are used in development environments as on production, along with easy updating of those dependencies.

Composer with WordPress is a complicated issue, but something that works well for whole site management. Both deliciousbrains.com and spinupwp.com are WordPress sites with their core files, plugins, and themes managed by Composer. Managing free plugins and themes from WordPress.org with Composer is easy thanks to the awesome Composer repository WPackagist, and even using premium plugins with Composer is possible, depending on the plugin author.

But what about WordPress must-use plugins? In this post, I’ll guide you through the why and how of using mu-plugins with Composer.

MU-Plugins: A Different Kind of Plugin

WordPress must-use plugins are a special type of WordPress plugin. They are installed in a different directory inside wp-content, and unlike normal plugins that are activated or deactivated by an administrator, these plugins are automatically loaded by WordPress.

Typically must-use plugins are single PHP files that provide some functionality that needs to remain in place regardless of what theme is used or what plugins are activated. Must-use plugins are loaded earlier than traditional plugins and themes during the WordPress bootstrapping, so it’s sometimes a necessary way to hook things before plugins.

It’s common to hear WordPress developers and blogs espousing the need to keep PHP tweaks out of the current theme’s functions.php file (which would get lost on on theme switch) and to put them in an mu-plugin. However, if an mu-plugin isn’t just a single file, meaning you have the plugin files contained in a directory like a traditional plugin, WordPress won’t load that mu-plugin automatically. WordPress only loads individual files in the wp-content/mu-plugins directory (which doesn’t exist by default in vanilla WP installs, so will need to be created), so to actually load a plugin from a directory you need an extra bootstrap file, for example:

wp-content/mu-plugins/my-plugin/my-plugin.php
wp-content/mu-plugins/my-plugin/assets/…
wp-content/mu-plugins/load-my-plugin.php

Where the contents of load-my-plugin.php is something like:

<?php
require_once __DIR__ . ‘/my-plugin/my-plugin.php`

So now your plugin is loaded automatically by WordPress! To be honest, for most mu-plugins this is great and all you need. Especially as most mu-plugin code will be very specific to their site, so they can be stored in version control instead of being managed by Composer.

Why Manage MU-Plugins with Composer?

You can certainly get by without managing with Composer, but I recently came across a scenario where Composer was going to make my life way easier.

I had the need to share code from deliciousbrains.com with the spinuwp.com site. I’ve talked in more detail about the reasons behind but I’ll outline them here.

For site-specific code, I prefer to keep it outside of WordPress plugins or even mu-plugins, sitting in an app directory next to my public directory. I can then benefit from PSR-4 autoloading with Composer, so I can create new classes and instantiate them anywhere without having a bunch of require_once file statements at the start of my code. You can see an example of how the site is structured here, which is our starting point for Composer-based WordPress sites that are primed to be hosted with SpinupWP.

The testimonials that appear throughout deliciousbrains.com (mostly in the form of tweets) are actually managed in the dashboard as a custom post type and their location on the site is controlled using Advanced Custom Fields. We have a similar setup for our alert bars that sometimes appear at the top of the site when we launch something new or have a special promotion going (if you head over to our WP Offload SES page right now, you might see one in action). We needed to replicate the same functionality on spinupwp.com so it made sense to reuse the existing code.

Initially I thought it would be as simple as creating some new libraries of code that I could host on GitHub, link to packagist.org so I could easily require them with Composer. The code would sit in /vendor and I could instantiate any classes from a site plugin or theme.

However, as the packages would need to load JavaScript and CSS on the front-end of the site (eg. loading the alert bars to circumvent the page cache) I couldn’t see how to access these asset files with a URL if they lived in vendor, outside of the public root. I decided to package the code up as mu-plugins, instead of a typical plugin, since they shouldn’t be deactivated and are part of the essential site code.

Making MU-Plugins Composer-Ready

To make any plugin or mu-plugin ready to work with Composer, it needs a composer.json file. This is where we tell Composer what type of package it is. WordPress package types are defined by the Composer Installers package, which allows us to use ‘wordpress-plugin’, ‘wordress-theme’, and ‘wordpress-muplugin’ for mu-plugins:

Our package’s composer.json would look like this:

{
    name: "deliciousbrains/my-mu-plugin",
    description: "WordPress must-use plugin for doing stuff",
    license: "GPL-2.0+",
    type: "wordpress-muplugin",
    require: {
            composer/installers: "~1.0"
    }
}

Once I’ve pushed this package repository to GitHub and tagged version 1.0, I can submit it as a package on packagist.org.

In the site project, we need to make sure Composer knows exactly where to put packages with ‘wordpress-muplugin’ type, which we can do by adding an extra item to the installer-paths config:

"installer-paths": {
    "public/content/mu-plugins/{$name}/": ["type:wordpress-muplugin"],
    …
}

We can then tell Composer to require this package, like any other dependency, in our WordPress site project:

composer require deliciousbrains/my-mu-plugin

No More Manual Bootstrapping

If you’ve been following along, then you might be wondering how WordPress will load this package now residing in wp-content/mu-plugins/my-mu-plugin. As I mentioned before, WordPress will only load single files in the directory so we need to create an extra file to bootstrap it. But this is far too manual a process, and what happens the next time I add an mu-plugin Composer package?

One of the benefits of managing a site with Composer is that it’s very straightforward to get up and running on the project. Clone the repo, set some environment configuration and then run composer install. Messing around creating bootstrap files for mu-plugins isn’t ideal.

Enter the WP Must-Use Plugin Loader, a nifty Composer package from Luke Woodward. It creates a new file inside wp-content/mu-plugins that loads any plugins inside directories from inside the mu-plugins directory.

This could be done with your own file, but having this as a Composer package solution means your own mu-plugin package could require it so you have to do even less inside the project. If your site is based on Bedrock you might have come across their similar mu-plugin autoloader.

The WP Must-Use Plugin Loader is a great solution to get round the limitations of how WordPress loads mu-plugins without having to do anything further than requiring the package.

Wrapping Up

I hope that’s been a good introduction to must-use plugins and how to use them with Composer. Composer really is a great way to manage the dependencies in your project, and I’m glad it’s possible to use it so comprehensively with WordPress. Side note: if you are using our premium plugins with Composer, check out this update on authentication!

Have you used must-use plugins with Composer before? How do you share WordPress code across projects? Let us know in the comments.

About the Author

Iain Poulson Product Manager

Iain is a product manager based in the south of England. He also runs multiple WordPress products. He helps people buy and sell WordPress businesses and writes a monthly newsletter about WordPress trends.