3balloons

So far I have decided I need a new version of this site (day 1) and worked out roughly how I’d like to structure my components (day 2). I still haven’t worked with a single directory or file. Time to put that right. Here’s another decision – rather than download WordPress, unpack it and jump in to the famous five minute set up, I will take some time to create a Composer file which I will use to deploy the WordPress core and any themes and plugins I want to manage.

Objective

Why do it this way? Why complicated a simple set up process? The answer lies in repeatability. There are two senses of repetition at play here. I would like to be able to run an install in seconds or, at least, minutes and do it over and over again in different places (for staging servers, test instances, development environments). That sense covers ease and frequency. I’d also like to pin down my WordPress core version, and the themes and plugins so that these relationships don’t change from install to install (unless I want them to). So that’s the sense of repetition that covers duplication. To repeat something is to do the same thing. I want this stability so that when I test the site or create a new feature I am working with a near exact duplicate of the production environment – preventing unexpected last minute incompatibilities. So, to sum up, I want:

  • Automated fast deployment of WordPress files and directories
  • Control over versions of core, themes, and plugins

Composer will go some way to winning both of those.

Composer

If you are not already familiar with Composer you may like to spend a little time on the Composer site – set up and installation are fast and easy (I also cover Composer in PHP Objects, Patterns and Practice, Ed 5 – just saying).

In essence, though, Composer is a dependency management tool. It uses a central json file, composer.json to manage the deployment of library code, usually into a local directory named vendor. Crucially, for my purposes, it allows you to pin down the versions of any libraries you deploy.

I want to treat core, themes and plugins as just another set of libraries, so Composer seems perfect. Not so fast, though! Composer wants to install library code tucked away in the vendor/ directory, and WordPress needs to be Web facing. Luckily, there is a solution!

Installing Core

The fancyguy/webroot-installer package allows you to specify a directory outside of vendor for your package. In this case, I’m going to use site/wp. I’ll start by focussing on core alone – here is my composer.json file:

{
    "name": "getinstance/getinstance-site",
    "description": "core for http://getinstance.com",
    "authors": [
        {
            "name": "Matt Zandstra",
            "email": "matt@getinstance.com"
        }
    ],
    "repositories": [
        {
            "type": "package",
            "package": {
                "name": "wordpress/wordpress",
                "type": "webroot",
                "version": "4.6.1",
                "dist": {
                    "type": "zip",
                    "url": "https://github.com/WordPress/WordPress/archive/4.6.1.zip"
                }
            }
        }
    ],
    "require": {
        "wordpress/wordpress": "4.6.1",
        "fancyguy/webroot-installer": "1.0.0"
    },
    "extra": {
        "webroot-dir": "site/wp",
        "webroot-package": "wordpress/wordpress"
    }
}

Let’s break this down. The name, description, and authors elements are standard and self explanatory. repositories is slightly less often used. By default Composer searches a repository at packagist.org for its packages. We want it to look on github for a zip archive. Luckily we can add our own package type. By specifying the type as webroot I ensure that this repository will be managed by the fancyguy/webroot-installer package.

Next comes require, another very frequently used element. This is where we specify the packages that Composer will install and the dependencies it will enforce. In this case, we need WordPress, of course, and webroot-installer to actually perform the installation in its custom location.

Finally the fields in the extra element are required by webroot-installer to specify the package it will install and the required install location.

So let’s give it a try. I begin a directory empty of everything except for the composer.json file.

$ composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing fancyguy/webroot-installer (1.0.0)
    Loading from cache

  - Installing wordpress/wordpress (4.6.1)
    Loading from cache

Writing lock file
Generating autoload files

And I’m done. Let’s see what I have. I’m going to use the find command to list my directory structure:

$ find . -maxdepth 3 -type d
.
./site
./site/wp
./site/wp/wp-includes
./site/wp/wp-content
./site/wp/wp-admin
./vendor
./vendor/fancyguy
./vendor/fancyguy/webroot-installer
./vendor/composer

From a single file and no directories I have now generated a full WordPress install under site/wp.

What about themes and plugins, though? Let’s do it.

Installing Themes and Plugins

I already mentioned that by default Composer uses packagist.org to acquire packages. Well, it happens that the WordPress community provide their own own repository at wpackagist.org. This provides a complete mirror of the WordPress theme and plugin directories in a Composer-friendly form.

Once again I need to define a repository in the repositories element. Also, in order to specify a custom location for themes and plugins, I need to add a new field installer-paths to the extra element.

Here is my amended composer file with the additions highlighted:

{
    "name": "getinstance/getinstance-site",
    "description": "core for http://getinstance.com",
    "authors": [
        {
            "name": "Matt Zandstra",
            "email": "matt@getinstance.com"
        }
    ],
    "repositories": [
        {
            "type": "package",
            "package": {
                "name": "wordpress/wordpress",
                "type": "webroot",
                "version": "4.6.1",
                "dist": {
                    "type": "zip",
                    "url": "https://github.com/WordPress/WordPress/archive/4.6.1.zip"
                }
            }
        },
        {
            "type":"composer",
            "url":"https://wpackagist.org"
        }
    ],
    "require": {
        "wordpress/wordpress": "4.6.1",
        "fancyguy/webroot-installer": "1.0.0",
	    "wpackagist-plugin/akismet":"dev-trunk",
	    "wpackagist-theme/twentyfifteen": "1.6"
    },
    "extra": {
        "webroot-dir": "site/wp",
        "webroot-package": "wordpress/wordpress",
        "installer-paths": {
            "site/wp-content/plugins/{$name}/": ["type:wordpress-plugin"], 
            "site/wp-content/themes/{$name}/": ["type:wordpress-theme"]
        }

    }
}

I have added a repository of type composer to the repositories element. I have added the themes and plugins I care about (considerably fewer plugins than I will end up with, no doubt) to the requires element. Finally I add a step that is only necessary because I am using a non-standard location for themes and plugins. I add the installer-paths field to extra and this specifies where my plugins/themes are to go and what they are to be named. You can read more about custom locations at the composer/installers homepage.

Now to clear down everything I’ve installed so far and re-run the intstallation.

$ rm -rf site/ vendor/ composer.lock
$ composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing fancyguy/webroot-installer (1.0.0)
    Loading from cache

  - Installing composer/installers (v1.2.0)
    Loading from cache

  - Installing wordpress/wordpress (4.6.1)
    Loading from cache

  - Installing wpackagist-plugin/akismet (dev-trunk)
    Checking out trunk

  - Installing wpackagist-theme/twentyfifteen (1.6)
    Loading from cache

Writing lock file
Generating autoload files

Seems promising. What has happened to the filesystem? Let’s take another look (edited for brevity).

$ find . -maxdepth 4 -type d
.
./site
./site/wp
./site/wp/wp-includes
./site/wp/wp-content
./site/wp/wp-admin
./site/wp-content
./site/wp-content/themes
./site/wp-content/themes/twentyfifteen
./site/wp-content/plugins
./site/wp-content/plugins/akismet
./vendor
./vendor/fancyguy
./vendor/fancyguy/webroot-installer
./vendor/composer
./vendor/composer/installers

Woo! Not only has Composer installed WordPress, it has fetched my theme and plugins and put them under site/wp-content/themes and site/wp-content/plugins respectively. Things are beginning to shape up just as I planned all the way back in Day 2 when I thought about my directory structure.

Of course, WordPress is not yet runnable – there’s no database or configuration – and by default it would use the wp-content directory at site/wp/wp-content rather than my new Composer-managed directory at site/wp-content. So there’s much to do yet before we open the big window!

Onward tomorrow!

photo credit: jaumescar via photopin (license)