Updated on 27th August with different methods for getting WordPress packages
This is a guide on how to incorporate a modern Composer workflow into your WordPress builds. This aimed at people who run WordPress websites as a service – typically bespoke theme developers who want to get a bit more out of WordPress as a tool.
This article prerequisites an understanding of Composer; Composer is a great PHP package manager so if you haven’t used it before give it a look. I won’t go into the specifics of how to use it or set it up as there are plenty of great guides on how to do that.
Composer by default installs it’s packages in
/vendor/ and adds the contents to the PHP autoloader. When we want to install WordPress, that’s not so much what we want. This article should be useful for anyone that wants to install PHP packages that don’t necessarily follow the composer format.
fancyguy has done a great job creating a composer plugin that installs specific composer packages into a predefined webroot, and then install packages of a defined type into specific locations – say for example WordPress plugins.
In the extra section of our
webroot-installer allows us to install WordPress in a “webroot”. In actual fact, our webroot is going to be
/web, but composer doesn’t need to know this – we want WordPress to be installed in
We can also define types of composer package that are installed into specific places. In this example we take every package with the type
wordpress-plugin, and install them into
This means that if the only code being put into the plugins directory is from composer, we can gitignore the entire directory. And we can also gitignore WordPress itself. We can build ourselves into a situation where we commit nothing third party into our repository, other than the auto-generated code composer outputs to composer.lock.
WordPress core and private/paid plugins
Unfortunately there is no official WordPress package in Packagist yet, but there are efforts to get a composer.json into the codebase. There are a few options for this:
If we host our own, it’s easy with the aid of Satis. With Satis, we define our packages as if we were setting up custom repositories in a
composer.json, and then Satis creates a fully fledged packages.json structure that we can point a
composer.json to. It needs hosting somewhere, and you may want to pop HTTP Basic Auth on the top of it if you want to store private or paid plugins here too. This is what I do for including paid for plugins such as Gravity Forms or ACF-Repeater. Another option for a Satis repository for plugins is SatisPress, which is a very simple WordPress install, which allows for the self-updating features of private plugins.
The other alternative to setting up Satis is to point to a very simple raw
packages.json maintained by yourself. At Carbon we have one of these on GitHub, however I wouldn’t recommend you use that one as there’s no guarantee it’ll be updated.
Another great tool in the arsenal to set this up is WordPress Packagist. WordPress Packagist is a Composer repository which mirrors
themes.svn.wordpress.org. This means that every plugin and theme hosted on WordPress.org is available as a composer package. Take the slug of a plugin from the URL of it’s page (e.g.
https://wordpress.org/plugins/advanced-custom-fields/) and use a * as the version number in our composer.json to get the latest stable version.
Project set up
Now we can treat WordPress’s internal code like a black box, but now we need to communicate with it to instantiate WordPress.
This starts with our frontend controller that delegates requests to the WordPress code. This is exactly the same as WordPress’s frontend controller:
* Application bootstrapper. Edit this file to route requests through WordPress
* or another means as you see fit
require(__DIR__ . '/wordpress/wp-blog-header.php');
Note, we have full control over this; if we want to write our own PHP application next to WordPress, we can implement a router inside this file that only delegates certain requests to WordPress.
Thankfully, this sort of setup is not uncommon and WordPress knows this. By default,
wp-load.php looks in the directory above itself if it cannot find a
wp-config.php in the same directory. This means that we can place our
/web/wordpress, and have our install point to a separate
Take your normal
wp-config.php file and replace the following defines:
/** Absolute path to the WordPress directory. */
if ( !defined('ABSPATH') )
define('ABSPATH', __DIR__ . '/wordpress/');
define('WP_HOME', 'http://' . $_SERVER['HTTP_HOST']);
define('WP_SITEURL', WP_HOME . '/wordpress');
define('WP_CONTENT_DIR', realpath(__DIR__ . '/wp-content/'));
define('WP_CONTENT_URL', WP_HOME . '/wp-content');
This will let WordPress know where to find stuff in our new directory structure.
One final file you may need to set up is a
wp-load.php file. Sometimes a plugin or theme may rely on the fact that it can include
wp-load.php by saying
require __DIR__ . '/../../../wp-load.php'; , so you may need to set up something to bounce the requests back to
/web/wordpress/wp-load.php, like so:
* Bounce user to Wordpress WP-load, so theme files can find it relative to themselves.
require_once __DIR__ . '/wordpress/wp-load.php';
So that’s all there is to it. It should all function as a completely normal WordPress install, with the only caveat being that the admin is now loaded from
http://yoursite.com/wordpress/wp-admin/. It’s useful to set up a 301 redirect from
/wordpress/wp-admin/ if any of your clients/users are quite used to WordPress.
Next I’ll write about how to incorporate PSR standard autoloading into your WordPress builds, and what advantages that can bring.