WordPress for devs: using Git

Jon Torrado
4 min readMay 7, 2018

This is the first story of a saga about how to work with WordPress nowadays. The big problems are: WordPress is not a dependency, no namespacing, no OOP, the WP hierarchy, the deployment… How to work with it as if it was a framework? A lot of people is working in 2018 like in 2000.

In this first story we are going to see how to use WordPress as a dependency, how to create a Git project in order to work with some teammates. To achieve this goal, we will be using the PHP dependency manager Composer.

Using WordPress with Composer

The very first step is to create a folder for your new project. Then, just inside that folder, create a composer.json file with the following content:

{
"name": "wordpress-composer",
"require": {
"johnpbloch/wordpress": "^4.9.0"
},
"repositories": [
{
"type": "composer",
"url": "https://wpackagist.org"
}
],
"extra": {
"wordpress-install-dir": "core"
},
"autoload": {
"files": []
},
"minimum-stability": "dev",
"prefer-stable": true
}

We will be coming back lots of times to this file, so keep it open. As you can see, there exists a website for the WordPress dependencies called https://wpackagist.org. The most important part here is the extra section, where you can tell Composer where to install the WordPress core. Run composer install and you should see something like this:

$ composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 3 installs, 0 updates, 0 removals
- Installing johnpbloch/wordpress-core-installer (1.0.0.2): Downloading (100%)
- Installing johnpbloch/wordpress-core (4.9.5): Downloading (100%)
- Installing johnpbloch/wordpress (4.9.5): Downloading (100%)
Writing lock file
Generating autoload files

It’s also important to tell Composer where to put the plugins, mu-plugins (must use) and the themes. Like in Symfony, we are going to use the src/ folder:

"extra": {
"installer-paths": {
"src/plugins/{$name}/": ["type:wordpress-plugin"],
"src/themes/{$name}/": ["type:wordpress-theme"],
"src/mu-plugins/{$name}/": ["type:wordpress-muplugin"]
},

"wordpress-install-dir": "core"
},

Configuring the installation

I usually like to create two files for the WordPress configuration. One that should not change from the very beginning, called wp-config.php:

require_once(dirname(__FILE__) . '/wp-config-custom.php');

$table_prefix = 'wp_';

if (!defined('ABSPATH')) {
define('ABSPATH', dirname(__FILE__) . '/core/');
}

define('WP_CONTENT_DIR', realpath(ABSPATH . '../src/'));
define('WP_HOME', 'http://' . $_SERVER['HTTP_HOST']);
define('WP_SITEURL', 'http://' . $_SERVER['HTTP_HOST'] . '/core');
define('WP_CONTENT_URL', WP_HOME . '/src');

require_once(ABSPATH . 'wp-settings.php');

And, as you can see in the first line of the code above, another file called wp-config-custom.php that changes from computer to computer. This is a wp-config-custom.php.dist file for the newcomers:

define('WP_MEMORY_LIMIT', '64M');

define('DB_NAME', 'database_name_here');
define('DB_USER', 'username_here');
define('DB_PASSWORD', 'password_here');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');

define('WP_DEBUG', true);
define('AUTOMATIC_UPDATER_DISABLED', true);
define('DISALLOW_FILE_EDIT', true);
define('DISALLOW_FILE_MODS', true);
define('WP_CACHE', false);

// See: https://api.wordpress.org/secret-key/1.1/salt/
define('AUTH_KEY', 'Put your unique phrase here');
define('SECURE_AUTH_KEY', 'Put your unique phrase here');
define('LOGGED_IN_KEY', 'Put your unique phrase here');
define('NONCE_KEY', 'Put your unique phrase here');
define('AUTH_SALT', 'Put your unique phrase here');
define('SECURE_AUTH_SALT', 'Put your unique phrase here');
define('LOGGED_IN_SALT', 'Put your unique phrase here');
define('NONCE_SALT', 'Put your unique phrase here');

If you are using Apache as webserver, this is the .htaccess file needed on the root of your project:

# BEGIN - Basic WordPress (non Multisite)
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /

# Securize some files
RewriteRule ^.git - [F,L]
RewriteRule ^composer\.(lock|json)$ - [F,L]
RewriteRule ^vendor/.*$ - [F,L]

# Make the admin still accessible from /wp-admin
RewriteCond %{REQUEST_URI} ^/wp-admin/?(.*)
RewriteRule ^wp-admin/(.*) core/wp-admin/$1 [R=301,L]

# Default WordPress rules
RewriteRule ^core/index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . core/index.php [L]
RewriteRule ^$ core/index.php [L]
</IfModule>
# END

# BEGIN WordPress
# END WordPress

That’s all you need to get your project working with Composer. What’s next?

Themes and plugins

By default, you won’t have any theme installed. Add the default theme to the require section of your composer.json file:

"require": {
"johnpbloch/wordpress": "^4.9.0",
"wpackagist-theme/twentyseventeen": "*"
},

You can also use something like https://github.com/marceldillen/wordpress-headless-theme to start with a blank theme. After installing it, don’t forget to activate the theme!

Installing a plugin is just the same: select it from WPackagist, add it to the require section and install. As you can see, it’s mandatory to use Composer in order to install default Themes and Plugins. If you use the admin, your collegues, production server, etc. will have to do the same steps manually! If the theme or plugin is yours, just commit-push it to Git. Here you have an example installing WordPress SEO plugin:

"require": {
"johnpbloch/wordpress": "^4.9.0",
"wpackagist-plugin/wordpress-seo": "^7.3",
"wpackagist-theme/twentyseventeen": "*"
},

Languages

The last step to get everything as a dependency is the locale selector. Language packs are also available as Composer dependencies. You just have to use https://wp-languages.github.io/ and that’s it! First of all, add the repository to the composer.json file:

"repositories": [
{
"type": "composer",
"url": "https://wpackagist.org"
},
{
"type": "composer",
"url": "https://wp-languages.github.io"
}

],

Then , add the extra needed configuration:

"extra": {
"dropin-paths": {
"src/languages/": ["vendor:koodimonni-language"],
"src/languages/plugins/": ["vendor:koodimonni-plugin-language"],
"src/languages/themes/": ["vendor:koodimonni-theme-language"]
}
,
"installer-paths": {
"src/plugins/{$name}/": ["type:wordpress-plugin"],
"src/themes/{$name}/": ["type:wordpress-theme"],
"src/mu-plugins/{$name}/": ["type:wordpress-muplugin"]
},
"wordpress-install-dir": "core"
},

Last step: add the languages you need.

"require": {
"johnpbloch/wordpress": "^4.9.0",
"koodimonni-language/es_es": "^4.9",
"wpackagist-plugin/wordpress-seo": "^7.3",
"wpackagist-theme/twentyseventeen": "*"
},

Install and then, just log in to the admin, navigate to Settings > General and find the Site Language selector. You should see the recently installed language there.

Using Git and working in a team

So, everything is prepared for the first commit. Create a .gitignore file with the following content:

/core
/src/languages
/src/plugins/*
/src/themes/*
/src/uploads
/vendor
/wp-config-custom.php

If you add your own plugin, just insert a line like !/src/themes/MyTheme. And we are done! When a collegue joins the project, he or she just have to clone the project, composer install and he/she will have the same WordPress version, themes and plugins. Also in a testing stage, also in a production stage.

In future readings we will see how to deploy a WordPress project without FileZilla (automatically), how to create routes like in Laravel or Symfony, how to use Twig as a template engine, how to create post types with classes (namespacing and OOP) and also, how to create default installation and content for newcomers using wp-cli. Comment here if you have any ideas!

--

--

Jon Torrado

IT Manager at Demium. Former CTO of different companies and startups. Father of one. Health learning lover.