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!