Composer Scripts: Automating Your PHP Workflow Beyond Install
Composer's script feature lets you attach PHP or shell commands to lifecycle events like post-install-cmd. Learn how to define, trigger, and safely verify them.
13 Sept 2025, 14:16 UTC

The Problem: Manual Steps in Composer Workflows
When you run composer install, you expect your dependencies to be in place—but what about clearing caches, warming up opcache, or running database migrations? These tasks often get handled manually or through fragile shell scripts that don't integrate with your dependency management.
Thesis: Composer Scripts Are Lifecycle Hooks, Not Just Shell Commands
Composer's scripts feature lets you attach PHP or shell commands to specific events in the Composer lifecycle. Think of them as event listeners that fire automatically when certain actions occur—no extra tooling required.
Available Script Events
Scripts can be attached to these built-in events:
pre-install-cmdandpost-install-cmd—fire before and aftercomposer installpre-update-cmdandpost-update-cmd—fire aroundcomposer update- Custom named scripts—triggered manually via
composer run-script
Script Types: Shell vs PHP Callables
In your composer.json, scripts are defined under the scripts key. You have two options:
- Shell commands prefixed with
@, which Composer executes in the project root - PHP callables as arrays:
[ClassName, methodName]orClassName::methodName
Worked Example: A Production-Ready Install Hook
Let's add a script that clears the cache and applies migrations after every install:
{
"scripts": {
"post-install-cmd": [
"@php bin/console cache:clear --env=prod",
"@php bin/console doctrine:migrations:migrate --no-interaction"
],
"post-update-cmd": [
"@php bin/console cache:clear --env=prod"
]
}
}
This ensures that every time dependencies are installed or updated, your application cache is cleared and database migrations are applied—without any manual intervention. Multiple handlers for the same event run in the order listed.
Accessing Script Context
When a script runs, Composer provides useful context:
- The current working directory is the project root
- The script inherits the environment of the Composer process
COMPOSER_SCRIPT_ARGSenvironment variable contains any extra arguments passed after--
Trade-offs and Limitations
Security risk: Scripts execute with the same privileges as the Composer process. A malicious package could run arbitrary code, so always review scripts in dependencies before running composer install.
CI considerations: Many CI pipelines invoke Composer with the --no-scripts flag during dependency-only steps, which silently skips all defined scripts. If your project relies on scripts for setup, document this requirement separately or use a two-step process: install dependencies, then trigger scripts explicitly.
Failure behavior: A failing script (non-zero exit code) aborts the entire Composer command. This is usually desirable for catching errors early, but it means a flaky script can block all installs.
Practical Verification
Test your scripts with these commands, run from the project root:
composer install—verify your post-install script output appears in the consolecomposer run-script post-install-cmd—manually trigger a script to check its behavior and exit codecomposer run-script post-install-cmd -- --verbose—pass extra arguments and confirm they reach the script viaCOMPOSER_SCRIPT_ARGS
For a quick failure test, temporarily add "exit 1" as a script handler and confirm Composer aborts with an error—then remove it.
Conclusion: Scripts Keep Your Workflow Consistent
By defining scripts in composer.json, you make your build pipeline self-documenting and reproducible. Whether it's clearing caches, running migrations, or generating IDE metadata, Composer scripts ensure these steps happen automatically and consistently across all environments—just remember to audit dependency scripts and account for --no-scripts in CI.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.