Using Composer with Drupal 8

Start managing your projects' dependencies with ease- use Composer.

A Quick Introduction to Composer

If you are looking to use Composer with Drupal 8, the first step is to understand how Composer works. Composer is a PHP-specific package manager, where Composer is to PHP as npm is to Node.js. It manages packages, which are stored in repositories. A global repository named “Packagist” is registered by default and others may be added. A composer.json file is placed in your project’s top level directory, which lists the dependencies your project has on other projects (packages). Specific versions and stability levels of packages may be specified in composer.json. Composer’s install command reads the composer.json file and places the specified packages in the project’s vendor directory. Once installed, the exact versions of each package are placed in the composer.lock file, which may then be added into version control, assuring that all project team members, Dev and QA instances, etc., use the exact same package versions. For more information on Composer, see the book at: https://getcomposer.org/doc.
 

Traditional Management of Drupal Code

Historically, there were three methods for managing (installing and updating) a Drupal project’s core and contributed code:

1) use the Drupal administrative UI
2) use the Drush command line interface
3) create a Drush makefile.

These methods relied on automated processes included in Drupal core, which communicate to Drupal.org using information stored in .info files (Drupal 7) and .info.yml files (Drupal 8) to identify dependencies and control updates.

So can we forgo using these traditional Drupal core/Drupal.org code management processes, and instead use Composer to manage all of our Drupal projects’ codes?

The answer is yes, and the following sections explain how. 

Drupal 8 Core and Contributed Module Composer Usage

Drupal 8 core includes a composer.json file which defines the external PHP code libraries on which Drupal core relies. The Drupal core /vendor directory comes pre-populated with these external PHP libraries. Contrib modules and themes may also optionally provide a composer.json file for their external PHP dependencies, with the download dependencies placed in the project’s /vendor directory when the project is installed or updated. Composer uses a two-level naming standard for naming packages; for Drupal core, the package name is drupal/core, and for contrib, the standard is /drupal/<project name>, e.g. /drupal/ctools for the Ctools module. In Composer, packages may be assigned a package type. Drupal-specific package types have been defined, e.g. “drupal-module” for a Drupal module. The available package types are listed at: https://github.com/composer/installers.  

Project Setup Using Composer

In order to manage your Drupal project using Composer, the first step is to verify that you have composer installed globally. From there, we recommend you use Drupal Composer to generate your project files. If you follow along with the readme, you will end up with something like this:

/project-root
   
   /web
   /drush
   /scripts
   /vendor
   .gitignore
   .travis.yml
   LICENSE
   README.md
   composer.json
   composer.lock
   phpunit.xml.dist

Next, update the composer.json file. It should look something like:

{
  "name":    "my-organization/my-project",
  "license": "proprietary",
  "type":    "project",
  "description": "project description",
  "repositories": [
    {
      "type": "composer",
      "url":  "https://packagist.drupal-composer.org"
    }
  ],
  "require": {
    "composer/installers":                        "^1.0.20",
    "cweagans/composer-patches":                  "~1.0",
    "drupal/core":                                "~8",
    "drupal/console":                             "~1.0"
  },
  "require-dev": {
    "behat/behat":                  "3.0.*",
    "behat/mink":                   "1.6@stable",
    "behat/mink-extension":         "*",
    "behat/mink-goutte-driver":     "*",
    "behat/mink-selenium2-driver":  "*",
    "behat/mink-browserkit-driver": "*",
    "drush/drush":                  "^8.0",
    "drupal/drupal-extension":      "~3.0",
    "drupal/coder":                 "~8.2",
    "phpunit/phpunit":              "4.6.*",
    "squizlabs/php_codesniffer":    "2.*",
    "jarnaiz/behat-junit-formatter": "^1.2",
    "drupal/devel":                 "8.1.x-dev"
  },
  "autoload-dev": {
    "psr-4": {
      "Drupal\\Tests\\PHPUnit\\": "tests/phpunit/src/"
    }
  },
  "minimum-stability": "dev",
  "prefer-stable":     true,
  "extra": {
    "installer-paths": {
      "docroot/core":                     ["type:drupal-core"],
      "docroot/modules/contrib/{$name}":  ["type:drupal-module"],
      "docroot/profiles/contrib/{$name}": ["type:drupal-profile"],
      "docroot/themes/contrib/{$name}":   ["type:drupal-theme"],
      "drush/contrib/{$name}":            ["type:drupal-drush"]
    },
    "patches": {
      "drupal/core": {
        "Ignore front end vendor folders to improve directory search performance": "https://www.drupal.org/files/issues/ignore_front_end_vendor-2329453-116.patch"
      }
    }
  },
  "scripts": {
    "install-phantomjs": "PhantomInstaller\\Installer::installPhantomJS",
    "post-install-cmd": [
      "PhantomInstaller\\Installer::installPhantomJS"
    ],
    "post-update-cmd": [
      "PhantomInstaller\\Installer::installPhantomJS"
    ]
  }
}

Both Drupal core and contributed modules must be specified in the require section. Not the require-dev section, which is for code that is used only in development. Each package is specified by the package name and version required. Versions may be specified explicitly, e.g. “1.0.3”, using the range operators (>, >=, <, <=), e.g. “>=1.0 <1.1 || >=1.2”; using the hyphen range operator, e.g. “2.0 – 3.0”; using wildcards, e.g. “*” (latest release) or “1.0.*”; using the next significant release operator signified by a tilde, e.g. ~1.1 is equivalent to >=1.1 <2.0.0, ”; and using the caret operator, which is preferred with library code for maximum interoperability, e.g. ^1.2.3, is equivalent to >=1.2.3 <2.0.0.

The repositories key specifies the repositories that are to be searched for the specific packages. Currently, there are two Drupal repositories, a new repository maintained by the Drupal association (drupal.org), and another maintained externally (packagist.drupal-composer.org). The officially maintained drupal.org repository is currently in alpha status and should not be used yet. The external repository is scheduled for deprecation in January 2017.

Autoload mappings may be provided to autoload PHP classes using the PHP autoload capability. PSR-0 and PSR-4 autoloading are both supported, although PSR-4 autoloading is preferred, as it eliminates the need to regenerate the autoloader when new classes are added. In the example above, classes for PHPUnit testing are autoloaded for the development environment.

The value for minimum-stability is set to dev, overriding the default setting of stable, which means that dev versions of packages (modules) may be used. The prefer-stable setting is set to true, indicating that stable versions should be used when available, unless a dev version was specified in the require mapping.    

Once the composer.json file has been created for your project, to download and install Drupal and all of the dependencies, go to your project root directory and execute the command:

composer update

(This will probably be the first and last time running that command with specifying a specific module, library, profile, or core)

That’s it! Your project and all of its dependencies will be installed.

Composer’s build process is similar to Drush’s make process, where Drupal core and contrib code is not stored in the repository, but rather a record is kept of the modules and versions a project uses with the code pulled in dynamically.

Adding a Module using Composer

To add a new module to an existing Composer-managed Drupal project:

  1. Add the new module in the require section of composer.json (not recommended), or execute the command Composer requires: drupal/<module_name> (specify version w/ limits/contraints).
  2. Enable the module (UI or drush)
  3. Export the config (drush config-export or drush cex. Note that the config entity that contains the list of enabled modules is core.extension).
  4. Deploy
  5. Sync the config (either through the UI or using Drush)

Updating Drupal Core and Contrib using Composer

To update core and contrib code, use the following commands:

composer update
drush updatedb --entity-updates -y

This would result in new versions of all project dependencies (core, contrib, custom, etc.) being downloaded into the project /vendor directory. This may not be preferable, as the project’s core dependencies would no longer match the versions shipped (and tested) with core. For this reason, it is preferable to only update a project’s non-core dependencies by specifying each dependency using the command:

composer update drupal/my_module

If you ever run into a case where a pre-existing module’s lock file is requiring a specific module, but that module has a security patch out, you can explicitly overwrite that version using an inline alias inside your composer.json file like so:

drupal/panels: "8.3.0-beta5 as 8.3.0-beta4"

Then run:

composer update drupal/panels

Now you have the version of the panels module that you want.

 

Special thanks to Dennis Evert and Dave Connerth for contributing to the content of this article.