Tobias T–

Get back to Howtos I create to explain things

Installing Wordpress with Composer

»It’s easy to use«, »Everyone knows how it works«, »We can easily extend it with a plugin« and »Did you see this gorgeous theme« are most likely words you all have heard when the discussion came up which blog Software to use. Over at What About Leo it was no different. We needed a blog. I hesitantly agreed to use Wordpress under the requirement that we install and manage it my way.

The most obvious way is to download the latest archive file from Wordpress.org, copy it to the server and unpack it. Create the database, open the folder in a browser and install it. Now you install some plugins and pick a theme, change a little bit of a template here, an image there and apply some more CSS rules. This is how millions of websites are installed.

This is not what I did with our blog. Installing software is easy. Updating, backing it up and restoring it when the disaster strikes is the hard part. What we needed was a good backup plan, a plan to update Wordpress (or a plugin) and make sure we would be able to deploy it to a new server in minutes. The idea was to create the installation somewhere else and then put the copy on the server. Best case scenario would be that Wordpress is not even allowed to change itself.

Long story short, this is what I did:

Installing Wordpress

I use Composer for almost any PHP project I have to work on. They describe themselves like this:

Composer is a tool for dependency management in PHP. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you.

This is exactly what I want. After all Wordpress is a dependency for the repository that contains our blog. The problem is: Wordpress does not support Composer. They seem to like the unpacking archive workflow and that’s fine. It’s their project so they make the decisions. I still needed a solution.

There is a fork of the Wordpress source on Github that added the required file and updates the source automatically every 15 minutes. This would have been a valid solution, but I don’t quite like the idea of installing a software that could change things without me noticing this. It’s not the official Wordpress code after all and I guess with Wordpress one is a little paranoid.

Composer can install packages from different version control systems. Wordpress itself is hosted in a Subversion repository. This is not an ideal setup, but it works:

{
    "type": "package",
    "package": {
        "name": "wordpress/wordpress",
        "version": "4.7.0",
        "url": "https://wordpress.org/download/",
        "source": {
            "type": "svn",
            "url": "https://core.svn.wordpress.org/",
            "reference": "tags/4.7"
        }
    }
},

This tells composer where to find Wordpress on the original Wordpress servers. No middleman in between. All I needed to do now was to add this package to the requirements:

"require": {
    "wordpress/wordpress": "*"
}

This would install Wordpress into the Composer directory vendor/wordpress/wordpress. Unfortunately this is not where we want it to be. Sure, we could configure this directory as the document root for our server, but that would not be very intuitive.

composer-custom-directory-installer to the rescue:

A composer plugin, to install differenty types of composer packages in custom directories outside the default composer default installation path which is in the vendor folder.

This does exactly what we want and it’s configured in the composer.json file like this:

"extra": {
    "installer-paths": {
        "public/": ["wordpress/wordpress"],
    }
}

That’s it. Composer will now do the downloading of Wordpress for us. When we want to update it, we change the version in this file and push the changes to the CI server.

Configuring Wordpress

This was actually the easiest part. I previously did not know that Wordpress is able to read a wp-config.php file that is above the document root. So I created our file inside the project root directory and it just worked.

Installing Plugins

We did pick some plugins that we wanted to use and now I had to add them to this setup. Obviously none of these plugins did support Composer. This is understandable. If Wordpress does not support it, why should plugins for Wordpress go the extra mile?

I did end up with the same setup as with the Wordpress installation. I defined the source of the plugin (again directly using the official Wordpress servers):

{
    "type": "package",
    "package": {
        "name": "plugin/mailchimp-for-wp",
        "version": "4.0.11",
        "url": "https://wordpress.org/plugins-wp/mailchimp-for-wp/",
        "source": {
            "type": "svn",
            "url": "https://plugins.svn.wordpress.org/mailchimp-for-wp",
            "reference": "tags/4.0.11"
        }
    }
},

and added it to the dependencies:

"require": {
    "wordpress/wordpress": „*“,
    "mnsami/composer-custom-directory-installer": "1.0.*",
    "plugin/mailchimp-for-wp": "*"
}

As this would install this plugin into vendor/plugin/mailchimp-for-wp  Wordpress would never know about it. Good that we can use the custom directory installer again and point it to a directory we want. The question is, where do we want it to be? We could put it directly into the Wordpress plugins folder public/wp-content/plugins/mailchimp-for-wp  but this is again not very intuitive. When we open the project in our favourite editor, we want to see the plugins easily. I decided to put it into a separate root plugins  directory in this project:

"extra": {
    "installer-paths": {
        "public/": ["wordpress/wordpress"],
        "plugins/mailchimp-for-wp": ["plugin/mailchimp-for-wp"],
    }
}

How would Wordpress now find my plugin? It wouldn’t. I did try different solutions to add this directory to the valid plugin directory but I found none. Most likely there is none, as plugins need to offer files to download for the visitors (CSS, JavaScript, …) and this directory would be outside of the document root anyway. The easiest way at this point was to create a symlink. Again, I’m not a fan of manually doing this, so I did create a simple class that we run every time composer changes packages:

"scripts": {
    "post-install-cmd": [ "WhatAboutLeo\\Blog\\ComposerScript::symlinks" ],
    "post-update-cmd": [ "WhatAboutLeo\\Blog\\ComposerScript::symlinks" ],
}

I've updated the related script file here. It checks if the target is already the correct symlink or if we need to delete the original Wordpress plugin directory first and create the links after that.

Installing a Theme

After we have the basic Wordpress installation automated and we know how to add new plugins, we need to install our theme. Themes are almost never in some kind of version control system as they are sold to you. You give them money and they provide you a zip file. This makes my automation life quite hard. I did have to add the theme, and all its files, into our repository and commit it. It still hurts a little, but there seems to be no other way.

Of course I used the Child Theme functionality to create our variation of the theme. Now only this theme contains our changes to the template and the CSS files and we most likely never have to touch the original theme folder anymore. I don’t think we will ever receive an update for it.

All this now shows this structure when you did freshly clone the repository:

After composer install

After performing the composer install step it looks like this:

Before composer install

For the crazy ones

Yes, I have to admit it might look like too many tools and automation power to install Wordpress. For me it is worth the effort.

With this approach we are enforcing that changes, no matter which ones, need to be done inside the repository. If you change a file that is not part of the repository you know immediately that you will loose this change. The fewer files a repository has, the better you can see which code is written by us, and more importantly, has to be maintained by us.

When I now commit a change to the repository, our continuous integration provider will build the customised Wordpress installation for us and notify the server that there is an update available. The server then downloads the archive, extracts it and replaces the existing version with the new one. Wordpress itself is not even allowed to change or create any file outside the uploads directory.

You most likely will never get the absolute protection against hackers or security issues, but the idea is to make it as hard as possible and try to prevent as much as possible. This is not a perfect setup, but it is a start for us and we will improve it step by step in the future.


Yesterday morning I did read this Tweet from Jesse Skinner:

Developers love to innovate. But when management decides what to build, all we have left to innovate is our software architecture and tools.

Maybe that’s the case here. Installing Wordpress is a boring task and constantly updating it manually is no fun either. Changing one file and installing Wordpress through an automated continuous integration workflow is.

Also available at

This section try to explain what I do for a living. If you think I could help you with a project, you can hire me though my little company succont.