Hand tools organized on a wooden wall.

Script Management with Composer

Codebases often have deployment scripts, code style checking scripts, theme building scripts, test running scripts, and specific ways to call them. Remembering where they all live, what arguments they require, and how to use them is hard to keep track of. Wouldn’t it be nice if there was some sort of script manager to help keep these things all straight? Well, there is one, and (if you’re reading our blog) chances are you probably already have it in your codebase.

Composer already manages our PHP dependencies, so why not let it manage our utility scripts too? The scripts section of a Composer file offers a great place to consolidate your scripts and build an easy to use canonical library of useful tools for a project.

The official Composer documentation says it best:

A script, in Composer's terms, can either be a PHP callback (defined as a static method) or any command-line executable command. Scripts are useful for executing a package's custom code or package-specific commands during the Composer execution process.

Note: The Composer documentation is full of much more great information on the details of how the scripts section can be used.

If you used a Composer template to build your composer.json file, you likely found entries under scripts such as pre-install-cmd, post-install-cmd, etc. These “magic” keys are event names that correspond to events during the composer execution process.

"scripts": {
        "drupal-scaffold": "DrupalComposer\\DrupalScaffold\\Plugin::scaffold",
        "pre-install-cmd": [
            "DrupalProject\\composer\\ScriptHandler::checkComposerVersion"
        ],
        "pre-update-cmd": [
            "DrupalProject\\composer\\ScriptHandler::checkComposerVersion"
        ],
        "post-install-cmd": [
            "DrupalProject\\composer\\ScriptHandler::createRequiredFiles"
        ],
        "post-update-cmd": [
            "DrupalProject\\composer\\ScriptHandler::createRequiredFiles"
        ]
    },

Creating Custom Commands

Composer also allows for custom events and provides great details in their documentation. Custom events can be used for just about anything and can be called easily with composer run-script my-event or simply composer my-event.

Shell Scripts/Commands

Composer scripts can be utilized to run anything that is available via the command line. In the example below, we simplify the execution of our code style checks and unit test execution.

"robo": "robo --ansi --load-from $(pwd)/RoboFile.php",
"cs-check": "composer robo job:check-coding-standards",
"phpunit": "composer robo job:run-unit-tests",
"test": [
    "@cs-check",
    "@phpunit"
],
"code-coverage": "scripts/phpunit/code-coverage"

Now we can call our tests simply using composer test.

Note that you can also define events that simply call other events by using the @ syntax as seen in our test event.

No longer do you need to remember the correct directory, command name, and mile long list of arguments needed to call your script, just store that all in a composer event and call it with ease.

PHP Commands

Composer scripts can also call PHP functionality with a tiny bit of additional setup.

First you must inform the autoloader where your class lives.

"autoload": {
    "classmap": [
        "scripts/composer/SiteGenerator.php"
    ]
},

Then create a new event and point it to a fully name-spaced static method, and you are set.

"generate-site": "ExampleModule\\composer\\SiteGenerator::generate",

Listing Available Commands

If you ever forget what scripts are available, composer list displays a handy list of all the available commands.

Available commands:
  about                Shows the short information about Composer.
  archive              Creates an archive of this composer package.
  browse               Opens the package's repository URL or homepage in your browser.
  check-platform-reqs  Check that platform requirements are satisfied.
  clear-cache          Clears composer's internal package cache.
  clearcache           Clears composer's internal package cache.
  code-coverage        Runs the code-coverage script as defined in composer.json.
  config               Sets config options.
  create-project       Creates new project from a package into given directory.
  cs-check             Runs the cs-check script as defined in composer.json.
  ...

Examples Use Cases

Each event can execute multiple commands as well. So for example, if you want to put your deployment script directly into composer.json you can.

This is not a complete deployment script, but it shows the flexibility Composer offers.

"deploy": [
  "drush config-import -y",
  "drush cc drush",
  "drush status",
  "drush updatedb -y",
  "drush cr"
],

Now code deploys to a local environment or a production environment can all use composer deploy and you can have confidence that the same code is running everywhere.

This can be integrated with front-end commands as well. Back-end developers no longer need to find the theme, navigate to it, find the build commands and run them. They can simply run something akin to composer example-build-theme-event where all theme building steps are handled for them.

Summary

Of course none of this is revolutionary, there are many other ways to achieve similar results. We are simply calling shell scripts or PHP methods in a fancy way. However, a tool does not need to be revolutionary to be useful. Composer scripts are a great way for a team to better document their scripts and increase their visibility and ease of use. Especially if said team is already committed to Composer as part of the project’s tooling.

If knowing is half the battle, then hopefully this helps your team remember where they put their tools and how to use them more easily.

Related Posts & Presentations