Documenting Optional Features in Laravel Packages
People who maintain Laravel packages need to show which setup steps every user must complete and which apply only to a feature,…
When you upgrade a Laravel package, Composer must choose versions that meet the requirements in your project and in the other packages it needs. A conflict means Composer cannot find versions that meet all those requirements while accounting for the lock file. To fix it, find the requirement that blocks the target version, then change only what the package’s documented and tested compatibility supports.
A package’s constraint is the range of versions its composer.json allows Composer to install. For example, ^10.0 allows versions from 10.0 up to, but not including, 11.0. Composer checks these ranges as well as requirements for PHP and PHP extensions. The commands below show how to trace the requirements, find the blocker, and choose a focused fix.
Composer’s conflict output often names several packages because one package’s requirements lead to others. Follow those requirements to find where the requested version ranges no longer overlap.
For example, this illustrative output describes a project that requires Laravel 11 and a widget package whose release requires illuminate/support 10:
Root project requires acme/laravel-widget ^3.0
acme/laravel-widget 3.0 requires illuminate/support ^10.0
Root project requires laravel/framework ^11.0
Check whether the widget’s requirement fits the project’s Laravel version. Here, the widget release requires the Laravel 10 generation of illuminate/support, while the project requires Laravel 11. If Composer cannot meet both requirements, changing an unrelated package will not fix the conflict.
In the output, identify direct requirements listed in the project’s composer.json and transitive requirements introduced by another package. Check each requires or conflicts line for a version range that rules out the version you want. Composer also checks php and ext-* requirements, which describe the PHP version and extensions available to run the code. A PHP version mismatch or missing extension can block an upgrade even when the package versions fit. The Composer platform-dependency documentation describes these requirements.
Use Composer’s prohibits command, also called why-not, to identify packages whose requirements prevent Composer from installing the target version:
composer prohibits laravel/framework 13 -t
Replace the package and version with your intended target. The -t option prints a dependency tree, which helps trace the blocker through packages that depend on one another. Use composer why when you want to see which packages require a particular package:
composer why illuminate/support -t
Composer documents these commands and their tree options in its command-line reference.
Check the lock file as well. The lock file records the exact package versions Composer selected. When the file exists, composer install installs those versions. composer update chooses versions allowed by the declared constraints and writes the result to the lock file. Run these commands to inspect the selected versions and check the project’s dependency files:
composer show --locked
composer validate
An older version in the lock file is a different problem from a declared constraint that rules out the target. If the declared ranges allow a compatible version but the lock file selects an older one, a targeted update may be enough. If a package’s declared range excludes the target, choosing a different locked version cannot fix the conflict.
The package Composer names in the output may differ from the package whose constraint needs changing. Before editing composer.json, check which releases the package supports and what requirements its metadata lists.
| What the conflict shows | Repair to consider |
|---|---|
| The package has no release compatible with the target Laravel generation. | Stay on a supported release, choose a compatible alternative, or update the package code and publish a compatible release. |
| A direct project requirement pins a dependency to a conflicting range. | Confirm that the pin is intentional, then change that specific requirement if a compatible release exists. |
| The lock file selects an older version, but the declared ranges permit a compatible newer one. | Update the named package; add -W if Composer also needs to update dependencies that the project requires directly. |
| A PHP version or extension requirement blocks the resolution. | Check the PHP runtime and extensions used by Composer and the deployment environment. Upgrade the platform or select package versions that support it. |
composer.json and composer.lock disagree. |
Run composer validate, resolve the lock file deliberately, and review the resulting diff. |
A targeted update limits which packages Composer considers for updates:
composer update vendor/package --dry-run
The --dry-run option shows the proposed changes without applying them. If Composer also needs to update dependencies listed directly in the project’s composer.json, retry with -W:
composer update vendor/package -W --dry-run
Composer defines -W as allowing updates to the named package’s dependencies, including dependencies that are root requirements. Review the proposed changes before running the command without --dry-run.
Ranges such as * or >=3.4 can admit versions beyond the package’s established compatibility, so do not widen a constraint just to make Composer accept the upgrade. Composer’s guidance on unbounded constraints recommends constraints that reflect supported versions. For a Laravel library, that means checking the code and tests against the Laravel or illuminate/* versions the package claims to support.
After Composer resolves the update, inspect composer.lock and confirm that only the intended packages changed, then run composer validate and the project’s tests. Composer’s successful resolution shows that the declared constraints fit together, while the tests check whether the updated code works with the selected versions.
Use the upgrade guide for the Laravel version you are targeting to check framework-specific dependency changes and breaking changes. For example, the Laravel 12 upgrade guide includes Laravel 12-specific requirements. Do not apply those requirements to other Laravel versions without checking their own guidance.
Package maintainers should also test the version ranges they publish. A package’s development lock file helps reproduce its test environment, but it does not determine which versions consumers install. Consumers choose versions from the package’s declared constraints. Composer recommends testing library constraints with both composer update and composer update --prefer-lowest, as described in its library guidance.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com