Composer Dependencies and Version Constraints

Understand Composer require, install, update, caret constraints, lockfiles, platform checks, and controlled dependency upgrades.

Lesson 79 of 86Advanced10 min read

Composer manages PHP package dependencies, but understanding its commands matters as much as installing it. composer.json states what your project permits; composer.lock records the exact resolved set. install and update do different jobs, and a constraint such as ^2.3 does not mean one fixed release. This reference explains those relationships, platform requirements, development tools, and reviewed upgrade workflows. You will practice reading constraints and learn why production deployment should usually install the committed lockfile rather than resolve a new dependency set. The lesson describes commands; it does not install packages or execute third-party scripts on your behalf.

Separate requirements from resolved versions

composer.json contains direct package requirements, autoload rules, scripts, and project metadata. Each required package may bring transitive dependencies with its own requirements. Composer resolves a compatible set rather than independently choosing the newest version of every package. composer.lock stores that resolved set, including exact versions and associated metadata. For an application, committing the lockfile makes development, testing, and deployment use the same dependency selection. A library's lockfile workflow has different consumer considerations, so follow the repository's policy. The vendor directory is generated installation output and is commonly excluded from source control. Do not manually edit files inside vendor to fix a package, because a later install can overwrite those changes.

Read semantic version constraints carefully

A caret constraint allows compatible updates within its defined range. For a stable package, ^2.3 generally permits versions from 2.3.0 up to but excluding 3.0.0. Pre-1.0 versions use narrower compatibility ranges, so ^0.3 is not equivalent to allowing every 0.x release. A tilde constraint expresses a different upper boundary depending on the specified segments. Exact versions pin one release but can make intentional updates cumbersome. Wildcards and comparison operators are also available. Constraints express what the project allows; they do not guarantee every permitted release is bug-free or suitable. Choose a range based on package compatibility promises, your test coverage, and the project's upgrade policy. Read the actual documented rule rather than guessing from the symbol.

Use install and update for different purposes

composer install uses the lockfile when present to install the recorded versions. composer update resolves versions under current constraints and writes the resulting lockfile. Running update during production deployment can introduce an unreviewed dependency change at the same time as a release. A controlled upgrade normally happens in development: update a bounded package set, inspect changes, run relevant tests, and commit the new lockfile. composer require adds a direct requirement and performs its documented dependency work. composer remove changes requirements and resolution too. Read command output for conflicts rather than deleting the lockfile as a first response. The conflict may reveal an incompatible PHP version or a package that needs a coordinated upgrade.

Treat PHP and extensions as platform dependencies

Composer models PHP and extensions as platform packages. A package can require a minimum PHP version or an extension such as ext-dom. Your CLI environment is what resolves and runs Composer, while production may have a different PHP version or enabled modules. A config.platform setting can influence dependency resolution, but it does not install the simulated PHP runtime or extension. Run platform checks against the actual target environment. Do not routinely use --ignore-platform-reqs to conceal missing requirements; that can install code the runtime cannot execute. A deployment should verify compatibility before traffic reaches the release. Keep production and test runtimes within the project's supported compatibility matrix.

Separate development tools and production packages

Put tools such as PHPUnit in require-dev when they are not part of runtime application behavior. A production install can use --no-dev under the application deployment policy. Autoload-dev rules likewise serve tests and development helpers. Optimized autoload generation can improve class lookup, but strict classmap settings need testing with any dynamically discovered classes. Composer plugins and scripts can execute code during install or update, so review package trust and allow-plugins policy rather than treating dependency installation as passive data downloading. Credentials for private repositories belong in protected authentication configuration and should not be committed. A security audit is useful evidence, but absence of reported advisories is not a complete code review.

Make upgrades reviewable and recoverable

Upgrade a focused set of dependencies when practical, inspect the lockfile diff, read release notes for breaking changes, and run behavior-focused tests. A passing test suite cannot prove every behavior is unchanged, but it identifies regressions the suite covers. Keep a rollback-ready release artifact and the previous lockfile. Use composer validate to check manifest consistency, and audit according to the project's supported Composer workflow. Record why a constraint changed, especially when allowing a new major version. If resolution fails, use diagnostic commands such as why or why-not to understand the dependency graph. Avoid a broad update merely to fix one conflict without reviewing the additional changes it introduces.

Practice task and worked solution

A project uses a committed lockfile, and one library needs a security fix within its current major version. Plan a bounded update in a development branch, inspect the resolved package changes, and run the tests that exercise that library. The solution commits composer.json if its range changed and the resulting composer.lock, then deploys with install rather than update. Check platform requirements on the real target PHP runtime and distinguish require-dev tools from production dependencies. Explain why deleting the lockfile or ignoring platform requirements would weaken repeatability. Keep private repository credentials outside the committed manifest and logs.

Continue learning

Return to namespaces and Composer, exceptions, and security.

The Composer version-constraint documentation specifies caret and tilde ranges; the command reference distinguishes install and update.

Composer Dependencies and Version Constraints Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Read an application manifest

This JSON example is a manifest fragment using an illustrative package. Do not install it blindly; choose actual packages and supported versions for your project.

The example permits the illustrated stable library within its compatible 2.x range. PHPUnit 12.5 has its own PHP requirements, so the full development resolution must satisfy those too; a lower PHP floor in the root manifest does not override a dependency's stricter floor. The illustrative vendor package is not an installation recommendation.

JSON
{
  "require": {
    "php": "^8.3",
    "ext-json": "*",
    "vendor/example-library": "^2.3"
  },
  "require-dev": {
    "phpunit/phpunit": "^12.5"
  },
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  }
}
Output
Manifest interpretation: requirements describe allowed ranges; composer.lock records the exact resolved versions.

Composer Dependencies and Version Constraints FAQ

Does ^2.3 mean exactly 2.3?

No. The caret allows a compatible range below 3.0: any version from 2.3.0 up to, but not including, 3.0.0. Semantic versioning promises no breaking changes within a major version, so this range accepts fixes and new features while avoiding the next major release.

Should production run update?

No. Normally install the reviewed lockfile with composer install --no-dev, which gives exactly the versions you tested. Run composer update on a development machine, test the result and commit the new composer.lock.

Does config.platform install PHP?

No. It influences resolution: config.platform.php tells Composer which PHP version to assume when choosing package versions, so a newer local PHP doesn't select packages the server can't run. The server still needs that PHP version installed.

Can I patch vendor directly?

Avoid it. The next composer install overwrites the change, and nobody else on the team receives it. Use a reviewed package update, a fork referenced in composer.json, or a documented patch workflow that reapplies the patch automatically.

Composer Dependencies and Version Constraints Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choicemedium

A required extension is absent in production. What should happen?

Composer Dependencies and Version Constraints Quiz

2 questions to check what you learned.

  1. What does composer.lock record?

    • Every possible allowed version
    • The exact resolved dependency set
    • The production password
    • Only the HTML title
  2. Which command normally installs a reviewed locked application release?

    • An unreviewed broad composer update
    • Deleting composer.json
    • composer install
    • Editing vendor manually