Composer will install 10.2.1 for ^10.0. It will not install 11.0.0. Tilde ~8.1 is tighter: 8.1.9 is fine, 8.2.0 is not. People copy npm’s mental model (^ means “compatible”) and then wonder why a lock file moved a minor they wanted frozen.
Also Read: Laravel and PHP
OR ranges and the lock file
A package that claims ^12.0 || ^13.0 is telling Composer either major is acceptable. That is how a library survives a Laravel bump without a new major of its own. It is also how you accidentally install 13 on a 12-only app if the application constraint is too loose. The Composer semver checker runs Composer\Semver\Semver::satisfies(), the same library Composer uses, so the yes/no matches composer why rather than a blog table.
Also Read: Laravel: Can you run
Use it when you are about to write "php": "^8.2" in a package that you still test on 8.1, or when a dependabot PR looks legal and feels wrong. Pair it with the Laravel/PHP version matrix so the framework band and the Composer band do not disagree.
Also Read: 220 billable days: turning a day rate into take-home pay
Stability flags are a third axis
@dev and minimum-stability are not semver. A constraint can satisfy 13.0.0 and still refuse a dev-main branch unless you allow it. Check the version string you actually have in composer.lock, not the one on the packagist badge.
When a constraint fails, the error is the point. Do not widen ^10 to * to silence CI. Fix the number you meant.
