Composer, dependencies and coding standards
Version constraints and the lockfile, scripts, autoload optimisation, PSR-12 formatting, PHPStan and Rector in one repeatable workflow.
Constraints that mean what you think
composer require "monolog/monolog:^3.7" # >=3.7.0 <4.0.0
composer require "phpunit/phpunit:~11.2" # >=11.2.0 <11.3.0 (patch-level safety)
composer show monolog/monolog # installed version and its own requirements
composer why-not php 8.3 # which package blocks that downgrade
composer outdated --direct
composer audit # known security advisories
composer validate --strict| Constraint | Accepts |
|---|---|
^3.7 | 3.7.0 and every later 3.x release |
~3.7 | 3.7.x only - patch updates |
>=3.7 <4 | An explicit range, useful when the caret is too loose |
3.7.1 | Exactly that version |
dev-main | A branch: moving, unreviewed code - pin a commit hash instead |
💡
Constraints describe what may be installed;
composer.lock records what was. Deploys run composer install --no-dev --optimize-autoloader so production gets exactly the tested set and none of the tooling.Scripts and autoload performance
{
"scripts": {
"test": "phpunit",
"stan": "phpstan analyse src tests --level=6",
"lint": "php-cs-fixer fix --dry-run --diff",
"check": ["@lint", "@stan", "@test"]
},
"config": {
"optimize-autoloader": true,
"sort-packages": true
}
}composer check # one command a reviewer can trust
composer dump-autoload -o # build a class map: no filesystem lookups at runtime
composer dump-autoload --classmap-authoritative # never fall back to scanning
composer dump-autoload --apcu # cache the map in shared memory when APCu is present- The class map turns autoloading from a series of
file_existscalls into a single array lookup per class. --classmap-authoritativeis for production only: add a new class in development and it will not be found.- Regenerate the map after every deploy that changes the dependency set, or the new classes will not resolve.
PSR-12, PHPStan and Rector
<?php
declare(strict_types=1);
namespace Acme\Blog;
final class Slugger
{
public function slug(string $value): string
{
$slug = preg_replace('/[^a-z0-9]+/i', '-', $value);
return strtolower(trim((string) $slug, '-'));
}
}vendor/bin/php-cs-fixer fix # apply PSR-12 across the tree
vendor/bin/phpstan analyse src --level=8
vendor/bin/rector process src # automated upgrades and safe rewrites
vendor/bin/rector process src --dry-run # always review the diff first- PSR-12 fixes the boring arguments: braces, spacing, imports order. Let the tool decide and stop discussing it in review.
- PHPStan levels are cumulative; raise one level at a time so the baseline stays at zero errors rather than growing a suppression file.
- Rector is best applied in small, dedicated commits - a mixed Rector-plus-feature diff is impossible to review.
FAQ
Should I commit composer.lock?
Yes, including for libraries. Consumers ignore it, but your own CI and every deploy then resolve to the same versions you tested.
How often should I run composer update?
On a schedule, in a branch, with the test suite as the gate. Waiting a year turns a routine patch bump into a migration project.
Related
A modern PHP toolchain with Composer Testing, frameworks and deployment
Last refreshed 2026-09-18.