How to Use Composer with WordPress

José Debuchy

Jun 1, 2023 | 6 min to read |

Tooling

Note. This post draws heavily from this excellent article and combines those concepts with other experiences we’ve had using Composer.

What is Composer

Composer is a dependency manager for PHP. In the same way that npm exists for JavaScript, RubyGems for Ruby, or pip for Python, these dependency managers are responsible for handling all the code that surrounds your project.

If our project were the development of a custom WordPress theme, Composer would be responsible for managing:

  • The installation of WordPress
  • The installation of its plugins
  • The installation of packages that can help improve your code, both at the theme level and globally.

Composer works with packages. A package can be a file or local folder, a remote zip, a git repository, etc.

At the same time, each of these dependency managers usually has a global package registry. In the case of Composer, this registry is called Packagist.

Advantages

  • Everything that is not specifically related to your project is declared in a single place (composer.json).
  • Composer takes care of installing and updating packages.
  • You can fine-tune and lock your project to specific versions of each package.
  • You don’t need to keep any third-party code that is not specific to your project inside your repository.

How to install it

We recommend following the instructions on the official Composer website according to the operating system you are using.

How it works

Composer is based on a JSON file called composer.json, where you list the packages you want to work with and where you can also define certain features or behaviors you want the process to have.

This is a simple example.

{
    "require": {
        "monolog/monolog": "2.0.*"
    }
}

If we run composer install in the directory where the composer.json file is located, we’ll see that a folder called vendor is created, where the installed package will live, in this case the Monolog package from Packagist.

In addition, for libraries that specify an autoload method, Composer will generate a vendor/autoload.php file that allows the library to be loaded into our project with just a few steps.

How WordPress works with Bedrock

Composer allows us to think of WordPress as a project dependency. Even though it is the core of the project, and in 99.99% of cases the absence of this package would make no sense for a project like this, it still meets all the characteristics of a dependency.

The original WordPress structure often causes confusion:

index.php
license.txt
readme.html
wp-activate.php
wp-admin
wp-blog-header.php
wp-comments-post.php
wp-config-sample.php
wp-content
wp-cron.php
wp-includes
wp-links-opml.php
wp-load.php
wp-login.php
wp-mail.php
wp-settings.php
wp-signup.php
wp-trackback.php
xmlrpc.php

In this structure, all core WordPress files live in the root directory, and the wp-content folder usually contains the project-specific files.

Bedrock redesigns this structure and adapts it to a modern setup:

.env
.gitignore
config
composer.json
web
-- app (replacement for wp-content)
-- index.php
-- wp
-- wp-config

In this structure, the web folder contains the files that make the project work, split into two folders. app, which replaces wp-content, and wp, which contains the rest of the essential WordPress files and directories.

WordPress and Composer

All Composer-based projects require a composer.json file. Unfortunately, the official WordPress project does not include one, and it seems it won’t for now.

Because of this, the Roots team created their own Composer package that connects to the original WordPress repository and generates a composer.json adapted to these requirements.

https://roots.io/announcing-the-roots-wordpress-composer-package/

Custom installation directories

Once WordPress is installed using this new structure, we can install the plugins required by our project. Earlier, we mentioned that Composer installs packages in the vendor directory by default.

Thanks to the composer-installers feature, we can define a package type and specify a different installation path instead of vendor.

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

This way, when installing a plugin, we can be sure that if the plugin’s composer.json defines its type as wordpress-plugin, it will be installed in the web/app/plugins directory.

Public plugin repository

For public plugins, a service called WordPress Packagist was created. It mirrors the public WordPress plugin and theme repositories, including their versions, and allows these plugins to be managed as Composer packages.

With this in place, if we want to add a plugin to our project, the composer.json file would look like this:

{
    "require": {
        "wpackagist-plugin/akismet":"*",
        "wpackagist-plugin/contact-form-7":"^5.4",
        "wpackagist-plugin/wordpress-seo":">=16.8"
    }
}

By running composer update, the plugins listed above will be installed.

As we can see, Composer allows us to lock a plugin to a specific version, restrict it to a major release, or ensure that it always updates to the latest version when running composer update.

Private plugins

For premium or private plugins, we need a way to pass credentials without exposing them publicly, while still allowing Composer to interpret them. Let’s look at some common approaches.

ACF

ACF offers a download option via a URL that accepts a license key to download the PRO version. There is a Composer plugin called private-composer-installer that allows passing .env variables into Composer.

This way, we define the ACF_PRO_KEY constant in the .env file and can download the desired ACF version.

"repositories": [
  ...
  {
    "type": "package",
    "package": {
      "name": "advanced-custom-fields/advanced-custom-fields-pro",
      "version": "5.9.0",
      "type": "wordpress-plugin",
      "dist": {
        "type": "zip",
        "url": "https://connect.advancedcustomfields.com/index.php?a=download&p=pro&k={%ACF_PRO_KEY}"
      },
      "require": {
        "composer/installers": "^1.4",
        "ffraenz/private-composer-installer": "^5.0.1"
      }
    }
  }
],
"require": {
  ...
  "advanced-custom-fields/advanced-custom-fields-pro": "5.9"
}

WP Migrate Pro

This plugin allows credentials to be passed through an auth.json file, which can be placed in the project root for local use, or in ~/.composer/ on macOS for global installation.

The plugin provides a username and password, used as follows:

## auth.json
{
    "http-basic": {
        "composer.deliciousbrains.com": {
            "username": "{COMPOSER_API_USERNAME}",
            "password": "{COMPOSER_API_PASSWORD}"
        }
    }
}
## composer.json
"repositories": [
  {
    "type":"composer",
    "url":"https://composer.deliciousbrains.com"
  }
]
"require": {
  "deliciousbrains-plugin/wp-migrate-db-pro": "1.8.1"
}

Satispress

Satispress is a WordPress plugin that attempts to solve the premium plugin problem by identifying premium plugins in a project and storing them on the project server. It exposes a composer.json file that grants access to new versions.

A step-by-step installation guide can be found at this link.

Summary

Composer is an essential tool today for working on large projects with multiple team members. It allows us to manage dependencies in an organized, simple, and portable way, while we focus on writing project-specific code. We strongly recommend learning how to use Composer and taking full advantage of its capabilities.

If you use Composer in a project with Bedrock or another WordPress framework and would like to share your experience, we’d love to hear how you use it and what benefits it brings to your daily work as a developer.

Author

José Debuchy