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
ConstraintAccepts
^3.73.7.0 and every later 3.x release
~3.73.7.x only - patch updates
>=3.7 <4An explicit range, useful when the caret is too loose
3.7.1Exactly that version
dev-mainA 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_exists calls into a single array lookup per class.
  • --classmap-authoritative is 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.

A modern PHP toolchain with Composer Testing, frameworks and deployment

Last refreshed 2026-09-18.