Agenda
This hands-on guide to using Composer with Drupal was originally created for the DrupalCon Nashville session How to build a Drupal site with Composer AND keep all of your hair, presented by Matthew Grasmick and Jeff Geerling on Wednesday, April 11, 2018.
- Agenda
- Check System Requirements
- Install Composer
- Take Composer for a test drive without Drupal
- Try basic Composer commands
- Create a new Drupal project with Composer
- Require and update Drupal dependencies
- Execute project binaries
- Update Drupal core
- Patch something!
- Check into Git
- Advanced usage
Check System Requirements
To run the example code in this hands-on guide, you need to have PHP 5.6+ installed installed on your computer. Check what version is installed with php -v
.
We strongly suggest that you use macOS or Linux. Windows users, please use these instructions to install PHP and Composer, and also install and use Cmder instead of PowerShell when running commands.
Install Composer
Follow the instructions below, or follow the Composer installation instructions and return here when you’re finished.
If you’re a Homebrew user on macOS, you can use brew install composer
. If you’re on Windows, you can use the Windows installer. Otherwise, execute the following on your command line:
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
chmod 755 composer.phar
This downloaded and installed Composer. Now you need to move it into directory that is part of your PATH
so you can access it globally. For instance:
mv composer.phar /usr/local/bin/composer
If that did not work, follow the more detailed Composer installation instructions and return here when you’re finished.
Take Composer for a test drive without Drupal
Create a new project
mkdir my-new-project
cd my-new-project
touch index.php
Create a PHP script
Initialize Composer
Run the command:
composer init
And you will be prompted for a few settings for your new project:
- Enter a package name, like
yourusername/my-new-project
- Enter a description like ‘Test project at DrupalCon’
- Enter
project
for Package Type. - Use defaults (hit enter when prompted) for everything else.
- When it asks about defining dependencies, answer
no
.
Once you confirm the composer project initialization, you’ll see a new composer.json
file in the folder, containing the project information you just entered.
It’s also a good idea to tell Composer you prefer to use stable versions of projects, instead of whatever’s latest. So let’s explicitly tell Composer:
composer config prefer-stable true
There are many other Composer config options you could set, but for the rest of them, the defaults are fine.
Require a new dependency
Your project doesn’t do anything yet, so let’s add a dependency so we can start doing something productive! Use composer require
to add a new dependency.
composer require monolog/monolog
Monolog is a logging library for PHP that uses the standard PSR-3 logging interface. Basically, it makes logging things easy!
Implement the dependency
Using Monolog is easy; add the following lines of code to the index.php
file you created earlier:
<?php
// Require Composer's autoloader.
require __DIR__ . "/vendor/autoload.php";
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// Create a logger.
$log = new Logger('example-log');
$log->pushHandler(new StreamHandler(__DIR__ . "/example.log", Logger::WARNING));
// Log a message!
$log->error('Goodbye world!');
Save the file, and run it on the command line:
php -f index.php
The script should execute very quickly, and afterwards, there should be a new example.log
file in your project’s directory. Let’s look at what’s inside:
$ cat example.log
[2018-03-25 12:05:00] example-log.ERROR: Goodbye world! [] []
Once you’re done with this simple project, clean up your project if you don’t want it for future reference:
cd ..
rm -rf my-new-project
That’s it! You’ve already started using Composer to make features in your PHP applications easier to implement!
Try basic Composer commands
Now that you have your feet wet, let’s explore a few more commands that can helpful when learning or exploring Composer.
composer
composer list
These two commands list out Composer’s help, along with a list of all the available Composer commands on your system. Note if you have extra composer scripts
defined in your project, they will be listed alongside the other available commands.
You can use other commands to explore Composer dependencies:
composer info monolog/monolog
For any dependency used in your current project, composer info
lists out all the information, including metadata, requirements, and suggestions.
composer why monolog/monolog
composer why
(an alias for composer depends
) gives a concise description of why a certain dependency is used. For example, in our test project, we can see the project directly requires monolog:
$ composer why monolog/monolog
myusername/my-new-project - requires monolog/monolog (^1.23)
When you want to start using a library, you might want to look up documentation, or get support. To do that, you could go search on Google for documentation, but it’s even faster to go straight to the library’s website using composer browse
:
composer browse monolog/monolog
This will open up a browser window to the project’s home page.
composer outdated
Finally, this handy command will look at all the packages used in your project, and check if any updates are available. If there are none, it won’t output anything.
Create a new Drupal project with Composer
Drupal requires a bit of extra scaffolding and setup to be able to more easily manage your Drupal project with Composer, so we’re going to use the most popular method of building a new Drupal codebase, the Composer Template for Drupal projects.
Following the directions from that project’s README, create a new Drupal project using Composer:
composer create-project drupal-composer/drupal-project:8.x-dev my-project --stability dev --no-interaction
Then change directories into the project folder that was just created:
cd my-project
This process will take a while; you’ll notice it installs a large number of dependencies, most of which are required by Drupal itself, but a few others will help you in managing your Drupal codebase with Composer (like cweagans/composer-patches, a library that lets you easily manage patches to Drupal core and contrib projects!).
Once your codebase is complete, it’s a good idea to install some of the Drupal modules, themes, and tools that will complete your site.
Want to test this Drupal project locally?
If you want to run the Drupal project you just created locally, follow these steps:
- Install Docker CE.
- Add a copy of Drupal VM to your project:
composer require --dev geerlingguy/drupal-vm-docker
- Run the local environment:
docker-compose up -d
- Access http://localhost/, and you should be able to install your new Drupal site (use database name, username, password of
drupal
)!Note: On Windows, if you get an error like
(Client.Timeout exceeded while awaiting headers)
when runningdocker-compose up -d
, go to the Docker Settings, then the Network section, and under ‘DNS Server’, select ‘Fixed’ with the8.8.8.8
server, then click Apply. Once Docker restarts, try running the command again.
Require and update Drupal dependencies
Require a module
Almost every Drupal site requires a few additional modules to provide much-needed functionality. One of the most popular Drupal modules is the Token module, which provides many tokens you can use around your Drupal site.
You can install the latest version with:
composer require drupal/token
If you do this, you’ll see an entry added to the require
section of your composer.json
file like:
"drupal/token": "^1.1",
Basically, Composer looked up the Token project on Drupal.org’s Packagist, found that the latest stable version is 1.1
(corresponding to 8.x-1.1
), and then automatically set the version constraint for the module to ^1.1
. Composer also downloaded the Token module, and placed it inside the modules
directory in your site’s document root (the web/modules
folder) so it can be used by Drupal.
Version constraints
The version constraint ^1.1
here tells Composer: Install any version of Token 8.x-1.0 or later, up to but not including Token 8.x-2.0. Refer to our earlier discussion about versions and Composer.
Note: You could also specify an exact version, e.g.
drupal/token:1.1
, and Composer will keep the project pinned to that version no matter what. This isn’t necessarily the best practice for Composer, but many projects and teams prefer to manage Drupal dependencies this way, especially if they rely on Drupal’s own update mechanisms (instead of usingcomposer outdated
) to warn when there are new releases.
Require a theme
Themes work much the same as modules; you just require the theme by name like you would a module:
composer require drupal/bootstrap
If you’re running the Drupal site locally, you can log in and go to the ‘Appearance’ section in the Drupal admin, and click on ‘Install and set as default’ under the Bootstrap theme to start using it.
Require a dev dependency
There are some things you might want to add which are helpful for developing your website, but aren’t necessary (and shouldn’t even be installed!) on your production website. For example, many people use PHP CodeSniffer to help check their PHP code against a set of common coding standards.
To install PHP CodeSniffer—but only in your local development copy of the codebase—require the library and pass the --dev
flag, like so:
composer require squizlabs/php_codesniffer --dev
After Composer is finished adding PHP CodeSniffer, look inside composer.json
; you’ll see that it appears under the require-dev
section, and not under require
like most of the other dependencies:
"require-dev": {
"squizlabs/php_codesniffer": "^2.8"
},
Warning: You might have gotten the message
Your requirements could not be resolved to an installable set of packages.
when you run this command, and Composer might have errored and revertedcomposer.json
to its original content. If this happened, it’s likely due to some other package (usuallydrupal/coder
) causing a dependency conflict. Congratulations! You can troubleshoot this dependency conflict using the guide later if you’d like—usually you just have to provide a version constraint that fits within the conflict’s restrictions, e.g.composer require squizlabs/php_codesniffer:^2.8.1 --dev
.
Note: If you added Drupal VM to your project earlier, you’ll notice it is also in the
require-dev
section, since it was required with the--dev
flag.
At this point, you can use PHP CodeSniffer to test some code against the Drupal coding standards:
-
Register the Drupal coder module’s Drupal coding standards with
phpcs
:./vendor/bin/phpcs --config-set installed_paths ../../drupal/coder/coder_sniffer
-
Sniff the Token module code, which we added to our codebase earlier:
./vendor/bin/phpcs --standard=Drupal --extensions=php,module,inc,install,test,profile,theme,css,info,txt,md ./web/modules/contrib/token
Note: On Windows, use the path
vendor\bin\phpcs
(note the backslashes) instead, otherwise you’ll get an error message like'vendor' is not recognized as an internal or external command, operable program or batch file.
Once PHP CodeSniffer is finished, you should see a report of all the errors found throughout the module. Development tools like PHP CodeSniffer can be very useful in evaluating code quality and fixing problems before you commit them to your codebase!
Execute project binaries
Many development tools, like PHP CodeSniffer, add ‘binaries’ to your project. Binaries are small executable programs you can run to do certain things. By default, these binaries are stored inside vendor/bin
inside your project.
This project already includes a few popular Drupal development binaries, for example:
# Run Drush commands with the `drush` binary:
./vendor/bin/drush status
# Run Drupal Console commands with the `drupal` binary:
./vendor/bin/drupal about
# Run PHP CodeSniffer with the `phpcs` binary:
./vendor/bin/phpcs -h
Note: On Windows, use the path
vendor\bin\[name of binary]
(note the backslashes) instead, otherwise you’ll get an error message like'vendor' is not recognized as an internal or external command, operable program or batch file.
Note: To make it easier to run these binaries, you can add the
vendor/bin
directory to your system path. Read more about Vendor binaries in the Composer documentation.
Update Drupal core
If you built your project with the Drupal Composer Project, you can update Drupal core to the latest stable version with the following command, taken directly from the Drupal Composer Project documentation on Updating Drupal Core:
composer update drupal/core webflo/drupal-core-require-dev symfony/* --with-dependencies
But in our case, we might want to update our codebase to the latest Drupal core development release, so we can test a patch at a code sprint, or work on some code to test it with Drupal’s latest version.
To do this with your project, you’ll first need to remove the webflo/drupal-core-require-dev
package, because it locks Drupal core into a stable release version.
So first, remove the package with:
composer remove webflo/drupal-core-require-dev --dev
(Note that this will also remove any other dependencies that package required which are not required by any other project dependencies.)
Then we can update drupal/core
to the latest development release:
composer require drupal/core:8.6.x-dev
After a minute or two, your codebase should be running Drupal 8.6.x-dev. You can verify this in one of two ways:
- Run
composer info drupal/core
, and check theversions
that Composer lists. It should list* 8.6.x-dev
. - Go to the Status report on the Drupal site in your browser (under Admin > Reports), and verify the ‘Drupal Version’ under General System Information. It should show
8.6.0-dev
.
Patch something!
Have you ever heard the Golden Rule of Drupal Development, “Every time you hack core God kills a kitten”?
Well, with Composer, we can actually bend that rule a little, because we can manage Drupal core and contributed project patches very safely and easily, with the help of the cweagans/composer-patches
library. Gone are the days of keeping a folder of random patch files that have to be manually applied with the patch
command line utility every time you update something—and accidentally forgetting to apply a patch, thus breaking your production website!
You can add patches in the extra
section of your project’s composer.json
file, like so:
"extra" {
"patches": {
"drupal/core": {
"Clear Twig caches on deploys": "https://www.drupal.org/files/issues/2752961-130.patch"
}
}
}
Whenever you install or update drupal/core
, the patch from comment #130 in Drupal.org issue #2752961 will automatically be applied. And if you’re updating Drupal, and the patch no longer applies, the composer-patches
library will warn you so you can go back to that Drupal.org issue and look for a newer, compatible version of the patch.
Warning: Even though the
composer-patches
library makes patching easier, you should still use approach patches with caution; a general rule of thumb is to only patch something if it’s mission-critical, if you understand everything the patch is doing, and if there’s a good chance the patch will be maintained and eventually included in the project so you don’t have to use the patch anymore!
Check into Git
Once you create your Drupal project’s codebase, it’s a good idea to start tracking the codebase in version control using Git, so you can have a history of changes to your site, and also so you can go back to a safe state if you accidentally break something!
Composer’s documentation recommends storing your composer.lock
file in Git, but not storing the vendor
directory. Luckily, the Drupal Composer Project creates a .gitignore
file that uses all the best defaults for your project, so to get started with managing your codebase in Git, create a repository, then commit everything as it is in an initial commit:
git init
git add -A
git commit -m "Initial commit of my-project."
At this point, you should have all the files that are necessary to replicate your Drupal site codebase in a new Git repository, stored locally on your computer, in the master
branch.
Create a ‘build’ and deploy to cloud hosting
Now, what happens when you want to put your Drupal site on cloud hosting somewhere, like Pantheon, Acquia, or some other provider? If you look at what’s actually stored in Git, there’s no vendor
folder. There also aren’t any contributed modules or other bits and pieces that Composer installs when you run composer install
.
Your Drupal project won’t run very well without these things!
So, when you want to deploy your project to a production environment, you need to ‘build’ the codebase specifically for production. One of the best advantages to doing this (as opposed to just committing everything you have locally) is that you can:
- Exclude
require-dev
dependencies (which can introduce security and performance risks if available in production). - Remove pesky metadata that’s not needed in production (e.g.
.git
folders for dependencies). - Add other build commands to do things like optimize images, compile CSS, and more! (That’s a more advanced topic for another day).
Before we can deploy the project, we need to build it for production use, specifically. And to do that, we’ll create a separate but parallel master-build
branch:
git checkout -b master-build
Now that we’re on this branch, we can use Composer and other commands to clean up the ‘build artifact’ that we want to eventually deploy to our production environment:
composer install --no-dev --optimize-autoloader --prefer-dist
When you run this command, you might notice Composer removes a bunch of dependencies. This is a good thing—all of those dependencies are only needed when doing local development. Once you switch back to your master
branch and want to do development work again, you can go ahead and run composer install
without all these options and it will reinstall your require-dev
dependencies.
Next up, we should remove all the unneeded Git repositories that may exist in some dependencies which are installed directly from GitHub:
find 'vendor' -type d | grep '\.git' | xargs rm -rf
find 'web' -type d | grep '\.git' | xargs rm -rf
Now, we will forcefully add the directories and files required to run our Drupal site, even though they are in the .gitignore
file, since we will need them in the production environment on the master-build
branch:
git add -f 'web/core'
git add -f 'web/modules/contrib'
git add -f 'web/themes/contrib'
git add -f 'web/profiles/contrib'
git add -f 'web/libraries'
git add -f 'vendor'
At this point, if you run git status
, you’ll see a large number of new files ready to be added. Go ahead and add them, and then make a commit to the master-build
branch:
git add -A
git commit -m "Production build artifact for 1.0.0 release."
At this point, the master-build
branch can be safely deployed to any cloud hosting environment.
Note: For some web hosts, e.g. Pantheon, you might need to add some extra configuration (e.g. add
web_docroot: true
to yourpantheon.yml
file) to tell the web host that the Drupal codebase files are inside theweb/
subdirectory.
Advanced usage
There are other advanced ways to use Composer to manage your codebase you can attempt if you have time:
- Adding a third party Node.js library using Asset Packagist. See a helpful guide here.
- Speed up Composer package installation by requiring the
hirak/prestissimo
Composer plugin.