Recommended Free Tools
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Composer scripts are a simple way to give a PHP project one consistent set of commands for tests, code checks, and small build steps. Define them in the root composer.json, then run them locally or from CI with composer test or composer run-script test. They are useful as a lightweight task runner—not a replacement for a CI platform or a deployment system.
The idea dates back to SitePoint’s 2012 article, Build Automation with Composer Scripts. The core approach remains useful, but current Composer syntax and lifecycle-event APIs have evolved. The examples below follow the current Composer scripts documentation.
Start with a small, named command
A Composer script is a command, PHP callback, or sequence of handlers declared under the root package’s scripts key. Here is a practical setup for common checks:
{
"scripts": {
"test": "phpunit",
"analyse": "phpstan analyse",
"cs-check": "php-cs-fixer check",
"ci": [
"@cs-check",
"@analyse",
"@test"
]
}
}
Install the tools as development dependencies:
composer require --dev phpunit/phpunit
composer require --dev phpstan/phpstan
composer require --dev friendsofphp/php-cs-fixer
Choose tool versions that support the PHP versions your project promises to support; compatibility requirements change. Composer installs development dependencies for local work by default, but composer install --no-dev omits them, so test and analysis scripts may not be available in a production installation.
#1 Best Overall
Composer temporarily adds the project’s configured binary directory—commonly vendor/bin—to PATH while scripts run. That lets the script call phpunit or phpstan without hard-coding a platform-specific path.
Run scripts and combine them
Call a named script with its short form or the explicit command:
composer test
composer run-script test
composer ci
A string defines one handler. An array runs handlers in the order listed, making ci a convenient, discoverable entry point for local checks and automation. If a command fails, Composer reports the failure; a successful overall check depends on each underlying tool returning a nonzero exit status when it finds a problem.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →The @ prefix reuses another named script. For example, "ci": ["@cs-check", "@analyse", "@test"] avoids duplicating command definitions. You can also forward arguments to a referenced script, such as "tests-verbose": "@test -vvv".
To pass arguments from your terminal to the underlying command, use -- as the separator:
Rank #2
composer test -- --filter UserTest
composer run-script test -- --filter UserTest
The separator keeps Composer’s own options distinct from the test runner’s options. If arguments appear to be ignored, check that the separator is present and that the target tool accepts the option.
Named scripts are different from lifecycle hooks
A named script runs when someone explicitly calls it. Lifecycle hooks run because Composer is performing an operation. For example, a project might warm a cache after the autoloader is generated:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minute{
"scripts": {
"post-autoload-dump": [
"php bin/cache-warm.php"
]
}
}
Other documented command events include pre-install-cmd, post-install-cmd, pre-update-cmd, post-update-cmd, pre-status-cmd, post-status-cmd, pre-archive-cmd, post-archive-cmd, pre-autoload-dump, post-root-package-install, and post-create-project-cmd. Composer also documents package-operation and plugin events; consult its event reference for the relevant event and callback type.
Be cautious with automatic hooks: they can make composer install or composer update do more than contributors expect. Keep checks such as tests and static analysis in explicit commands like composer ci unless they genuinely need to run automatically.
Ordering matters. During pre-install-cmd and pre-update-cmd, dependencies may not yet be installed or autoloadable. Keep early hooks self-contained in the root project. Use later hooks such as post-install-cmd, post-update-cmd, or post-autoload-dump when the task needs installed dependency binaries or generated autoload files. Composer runs scripts declared by the root package; scripts declared by dependencies are not automatically executed.
Rank #3
Use PHP callbacks when a command needs project logic
A callback can be clearer than a long shell command. The callback class must be loadable using Composer’s autoload configuration. For example:
{
"autoload": {
"psr-4": {
"App\": "src/"
}
},
"scripts": {
"build": "App\Build::run"
}
}
<?php
namespace App;
use ComposerScriptEvent;
final class Build
{
public static function run(Event $event): void
{
$io = $event->getIO();
$io->write('Build started');
// Put project-specific build logic here.
}
}
After adding the autoload definition, regenerate Composer’s autoloader and run the callback:
composer dump-autoload
composer build
Current callback signatures use namespaced Composer classes. Command events use ComposerScriptEvent; other event types have their own classes. For instance, package-operation callbacks use ComposerInstallerPackageEvent, and the package is obtained from the event’s operation. Do not copy older examples that use outdated event names or callback types.
Composer 2.5 and Symfony Console commands
Composer 2.5 and later can run Symfony Console command classes as scripts. A script may point to a command class such as AppConsoleMyCommand; the class must extend Symfony’s Command class and end in Command for Composer to detect it as a native command.
This can make structured arguments and options more comfortable than forwarding raw shell arguments. There is an important version caveat: the command runs using Composer’s built-in Symfony Console version, which may differ from the version required by your project and can change between Composer minor releases. If version consistency matters, create a project-owned executable that uses the project’s own Symfony Console dependency instead.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Timeouts, shells, and portability
Composer’s default process timeout is 300 seconds. A long integration test or asset build can hit that limit. Prefer investigating a process that unexpectedly takes five minutes; if a particular command legitimately needs longer, disable the timeout narrowly:
{
"scripts": {
"long-test": [
"Composer\Config::disableProcessTimeout",
"phpunit"
]
}
}
Other options include setting "process-timeout": 0 in the project’s config, setting COMPOSER_PROCESS_TIMEOUT=0 in the environment, or running composer run-script --timeout=0 long-test. Avoid disabling the timeout globally by default: Composer is not intended to manage long-running servers, watchers, or other persistent processes.
Shell commands are not automatically portable. Utilities such as rm -rf, cp, and mkdir -p, pipelines, quoting, and environment-variable syntax vary across Windows and Unix-like shells. Keep shell snippets short. For substantial logic, use a PHP script or a cross-platform package binary, and verify the command on the operating systems and CI runners your contributors use.
Keep scripts safe
Composer scripts execute commands, including during dependency operations when attached to lifecycle events. Review changes to composer.json like other executable code. Be especially cautious with Composer plugins, which are a separate extension mechanism with broader capabilities and distinct trust implications. Avoid hooks that download and immediately execute arbitrary remote code, and do not put production secrets in composer.json, command arguments, or logs. Run deployment commands only with the permissions they need, and ensure CI output does not expose credentials.
Let CI orchestrate; let Composer define project commands
Composer scripts make a useful interface between local development and CI. A workflow can install dependencies and invoke the same project-level check developers run locally:
Best Value
composer install --no-interaction --prefer-dist
composer ci
For example, GitHub Actions workflows are YAML files under .github/workflows; they define triggers, jobs, and steps that run on hosted or self-hosted runners. An illustrative workflow is:
name: CI
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathai/setup-php@v2
with:
php-version: '8.3'
tools: composer
- run: composer install --no-interaction --prefer-dist
- run: composer ci
Treat this as a starting point, not a universal or fully tested workflow: select and maintain action versions and PHP versions according to your project’s policy. The CI provider should handle triggers, runners, caching, artifacts, permissions, secrets, approvals, and deployment orchestration. The Composer script should describe the project-specific checks to run. GitLab CI/CD, Jenkins, CircleCI, and other established platforms can use the same approach.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.When Composer scripts stop being enough
Composer is a good fit for a handful of deterministic commands: tests, static analysis, formatting checks, documentation generation, cache operations, or preparing an archive. It is less suitable as the sole system for parallel pipelines, infrastructure provisioning, approval gates, artifact management, deployment rollback, or health checks.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →- Composer scripts: low-friction commands that use the project’s PHP tools and work locally as well as in CI.
- Make: useful when a team already uses Make and can provide a consistent Unix-like shell; Windows support may require an agreed environment.
- Phing: a PHP-oriented build tool to consider when build logic needs more structure than a small Composer script collection provides.
- CI/CD platforms: handle repository triggers, runner allocation, parallel jobs, artifacts, access controls, and deployment workflows.
Composer can invoke a deployment command, but it does not itself provide deployment infrastructure, secret management, approvals, rollback, or health checks. Keep those responsibilities in the platform or tool designed to manage them.
Troubleshooting common failures
command not foundor a missing tool: check that the package is installed in this project, that development dependencies were not omitted with--no-dev, and that the script name matches the binary provided by the package.- A hook fails before install or update completes: move dependency-dependent work out of
pre-install-cmdorpre-update-cmd, or make the early hook self-contained. - A command stops at about five minutes: check for Composer’s 300-second process timeout. Fix a slow or stuck process where possible; otherwise apply a targeted timeout override.
- The script works on one OS but not another: inspect shell syntax and external utilities. Replace nonportable shell logic with PHP or a cross-platform tool.
- A callback class cannot be found: verify its namespace and PSR-4 or classmap configuration, then run
composer dump-autoload. - Arguments do not reach the test runner: use
--after the Composer command and confirm the tool supports the forwarded option. - An operation runs unexpectedly during update: inspect lifecycle hooks in the root
composer.json; use explicit named scripts for operations that should happen only on request. - Production cannot run a developer command: check whether it depends on a development tool omitted by
composer install --no-dev. Do not install development dependencies in production merely to conceal a misplaced task.
For install hooks that need to distinguish production from development mode, Composer exposes COMPOSER_DEV_MODE during relevant install, update, and autoload-dump operations: it is 0 with --no-dev and 1 otherwise. Prefer keeping checks explicit rather than using that variable to make routine installations perform extensive build work.
Make the command list discoverable
For a project with several scripts, add descriptions under scripts-descriptions; Composer can show them through composer list or composer run -l. Keep names and descriptions action-oriented so a new contributor can find the canonical test or CI command without searching shell history.
A strong default is a small command surface—such as test, analyse, format-check, and ci—with Composer handling project-level tasks and the CI platform handling orchestration. That keeps local development repeatable without turning composer.json into an opaque build or deployment system.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

