As PHP developers, we all agree that code quality matters.
Whether it’s running tests, static analysis, linting, or refactoring, we want these checks to be consistent and easy to execute, both locally and in CI/CD pipelines.
But how many times have you joined a project and had to guess:
- “How do I run the tests here?”
- “What’s the command for PHPStan?”
- “Which formatter does this team use, Pint or PHP-CS-Fixer?”
Let’s try to fix that.
In this article, we’ll see how to centralize all code quality commands in one place, ensuring consistency and simplicity. We’ll cover two popular options:
- Composer scripts: built-in, simple, zero extra dependencies.
- Makefile: more flexible and powerful for advanced automation.
Why centralize code quality commands?
Centralizing your commands has several benefits:
- Consistency: everyone on the team runs the same exact commands.
- Ease of use: new developers don’t need to memorize or look up long CLI invocations.
- CI/CD alignment: the same scripts used locally can be reused in your CI pipeline.
- Discoverability: a single, documented entry point for quality tasks.
In short: one command to rule them all.
The common set of tasks
Before diving into the examples, here’s a unified list of the tasks we’ll define in both approaches:
Task | Description |
---|---|
csfix |
Fix code style using Pint |
csfix-review |
Check code style without fixing (Pint --test) |
refactor |
Apply code transformations using Rector |
refactor-review |
Run Rector in dry-run mode |
code-analysis |
Run static analysis with PHPStan |
test |
Run the test suite (Pest or PHPUnit) |
all-check |
Run all checks (Pint, Rector, PHPStan, Tests) |
Option 1: centralizing with composer scripts
Composer allows you to define custom scripts right inside your composer.json
.
It’s lightweight and doesn’t require any external tools, making it perfect for small or pure PHP projects.
What is a composer script?
Composer is not only a dependency manager, it can also run scripts that you define inside your composer.json
.
Each script is a shortcut to a command (or a sequence of commands) that you can run with:
composer run <script-name>
For example, instead of typing:
vendor/bin/pint --test
With this script in composer.json:
{
"scripts": {
"csfix-review": [
"vendor/bin/pint --test"
],
}
you can just run:
composer csfix-review
Example: composer script setup
Here’s a complete example for centralizing all code-quality tasks in composer.json
:
{
"scripts": {
"csfix": [
"vendor/bin/pint"
],
"csfix-review": [
"vendor/bin/pint --test"
],
"refactor": [
"vendor/bin/rector process src"
],
"refactor-review": [
"vendor/bin/rector process src --dry-run"
],
"code-analysis": [
"vendor/bin/phpstan analyse"
],
"test": [
"vendor/bin/pest"
],
"all-check": [
"@csfix-review",
"@refactor-review",
"@code-analysis",
"@test"
]
},
"scripts-descriptions": {
"csfix": "Fix code style using Pint.",
"csfix-review": "Check code style without fixing (Pint --test).",
"refactor": "Apply code refactoring using Rector.",
"refactor-review": "Check possible refactors using Rector in dry-run mode.",
"code-analysis": "Run static analysis with PHPStan.",
"test": "Run test suite using Pest.",
"all-check": "Run all code quality checks (Pint, Rector, PHPStan, Tests)."
}
}
Now you can simply run:
composer all-check
And it will execute all your quality tools in sequence.
Understanding the Example
Let’s break down the key parts:
- The
scripts
section defines custom commands that Composer can run. Each entry is just a command (or a list of commands) you could normally run in your terminal. For example:-
composer csfix
runsvendor/bin/pint
-
composer test
runs your test suite
-
- Nested calls with
@
: the"all-check"
script shows how you can chain existing commands.- The
@
prefix tells Composer to run another defined script. - So
"@test"
means “run thetest
script.” - This allows you to combine multiple tasks into a single, unified command.
- The
-
scripts-descriptions
section: provides human-readable descriptions for each script. Runcomposer list --scripts
to display them nicely in the terminal. It’s great documentation for your team.
Together, these features enable you to build small, reusable building blocks and then combine them into composite commands, such as all-check
.
Pros of composer scripts
- Built into Composer, no extra dependencies.
- Easy to understand and share.
- Works identically across environments (local, Docker, CI).
- Great for small or medium-sized projects.
Cons of composer scripts
- Limited logic (no conditions, variables, or dependencies).
- Harder to extend for multi-step workflows (e.g. JS, Docker, etc.).
If your workflow is relatively simple, composer scripts are perfect.
Option 2: centralizing with a Makefile
What is a Makefile?
A Makefile
is a simple automation file that defines tasks (called “targets”) and how to run them.
It’s managed by the command-line tool make
, available on most Unix systems, macOS, and on Windows (WSL).
Each target defines a command or sequence of commands, similar to composer scripts, but more flexible.
You can also define dependencies between tasks and reuse variables.
For example, this target:
csfix-review:
vendor/bin/pint --test
It can be executed by typing:
make csfix-review
For more flexibility, especially in multi-language or CI-heavy projects, make
is a great choice.
It’s a small tool available on most systems (Linux, macOS, WSL, Git Bash on Windows).
Here’s a Makefile implementing the same tasks:
.PHONY: help csfix csfix-review refactor refactor-review code-analysis test all-check
.DEFAULT_GOAL := help
help: ## Show this help.
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
csfix: ## Fix code style using Pint
vendor/bin/pint
csfix-review: ## Check code style without fixing (Pint --test)
vendor/bin/pint --test
refactor: ## Apply code refactoring using Rector
vendor/bin/rector process src
refactor-review: ## Check possible refactors using Rector in dry-run mode
vendor/bin/rector process src --dry-run
code-analysis: ## Run static analysis with PHPStan
vendor/bin/phpstan analyse
test: ## Run test suite using Pest
vendor/bin/pest
all-check: csfix-review refactor-review code-analysis test ## Run all code quality checks
@echo "✅ All checks completed successfully!"
Run it with:
make all-check
…and you’ll get the same results as composer all-check
.
Understanding the example
Let’s unpack what’s happening here:
.PHONY
: lists targets that don’t correspond to actual files.
This ensuresmake
always runs the command, even if a file with the same name exists..DEFAULT_GOAL := help
: sets the default target when you just runmake
.
In this case, it shows the help message.help
target: dynamically prints all available commands and their short descriptions.
The##
comments next to each command are parsed to generate help output automatically.
Try it out withmake help
.Dependencies between tasks: the
all-check
target lists other targets after the colon:
all-check: csfix-review refactor-review code-analysis test
This means it will "run all those tasks in sequence", just like composer all-check
.
- Comments as documentation: lines like
## Fix code style using Pint
serve as documentation for thehelp
target.
This pattern makes your Makefile self-documenting, readable, and extensible, ideal for larger projects.
Pros of Makefile
- Very flexible: supports logic, dependencies, variables, and complex commands.
- Perfect for mixed stacks (PHP + JS + Docker + CI/CD).
- Easily extendable (e.g.
make push
,make deploy
,make init
). - Self-documenting when combined with a
help
target.
Cons of Makefile
- Requires
make
(Windows users need WSL or Git Bash). - Syntax can be finicky (tabs really matter!.
- Adds another file to maintain.
When to use which
Scenario | Recommended Option |
---|---|
Simple PHP-only project | Composer scripts |
Shared package or open source library | Composer scripts |
Project involving multiple tools (PHP + JS + Docker) | Makefile |
CI/CD pipelines with multiple steps | Makefile |
Teams seeking minimal setup | Composer scripts |
Many teams start with composer scripts, then move to a Makefile as their needs grow.
Conclusion
By defining all your code quality commands in one place, you make life easier for everyone on your team, including developers, reviewers, and CI pipelines.
Whether you go with composer scripts or a Makefile, the principle is the same:
Make it easy to do the right thing.
You’ll never again wonder, “What’s the command for that?”, it’ll be right there, documented and ready to go.
Top comments (0)