ctw / ctw-composer-plugin-composerlenientplugin
Composer plugin that relaxes the upper bound of the PHP platform, or of any package requirement, for an allowlist of packages, so dependencies capped to an older PHP (e.g. ~8.4.0) or an older dependency major (e.g. laminas/laminas-servicemanager ^3) resolve on a newer one without --ignore-platform-r
Package info
github.com/jonathanmaron/ctw-composer-plugin-composerlenientplugin
Type:composer-plugin
pkg:composer/ctw/ctw-composer-plugin-composerlenientplugin
Requires
- php: ^8.4
- composer-plugin-api: ^2.0
Requires (Dev)
- composer/composer: ^2.0
- ctw/ctw-qa: ^6.0
- phpunit/phpunit: ^12.0 || ^13.0
- symfony/var-dumper: ^7.0 || ^8.0
This package is auto-updated.
Last update: 2026-07-17 10:28:18 UTC
README
Install Composer packages that cap their php requirement on a newer PHP — without
--ignore-platform-req, without forking, and without editing anything in vendor/.
ctw/ctw-composer-plugin-composerlenientplugin is a Composer 2 plugin that relaxes the
upper bound of the php platform requirement for an explicit allowlist of packages, at
dependency-solver pool-build time. It lets a package that is capped at, say, ~8.4.0 install
cleanly on PHP 8.5, 8.6, and later, while every other platform check stays fully enforced
and your real PHP version is never faked.
Since 2.x the same mechanism widens any package requirement, not only php — for
example letting a package that declares laminas/laminas-servicemanager: ^3 also resolve
against ^4 while upstream finishes a not-yet-released major bump. php and package
requirements share one uniform config format; see Configuration below.
Does this fix your problem?
If composer install or composer update fails on a newer PHP with a message like this:
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Root composer.json requires laminas/laminas-tag ^2.11 -> satisfiable by laminas/laminas-tag[2.13.0].
- laminas/laminas-tag 2.13.0 requires php ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 ->
your php version (8.5.7) does not satisfy that requirement.
…then yes. The package's code runs fine on your PHP, but its declared php constraint has an
upper bound (~8.4.0, <8.5, ^8.0 <8.5, etc.) that Composer refuses to cross. The same
failure happens with a ~8.3.0-capped package on PHP 8.4, a ~8.5.0-capped one on PHP 8.6, and
so on for every future PHP release.
This plugin removes that specific blocker for the packages you trust — and only those packages.
Why not just use the usual workarounds?
| Workaround | Why it falls short |
|---|---|
composer install --ignore-platform-req=php+ / --ignore-platform-reqs |
Must be repeated on every install and update, including on deploy and in CI, and it switches the platform check off for every package, not just the ones you trust. |
config.platform.php set to an older version |
Lies about your PHP to the whole dependency tree. Impossible when your own require (or other dependencies) genuinely needs the newer PHP. |
Inline aliasing ("vendor/pkg": "2.13.0 as 2.99.0") |
Per-package, fragile, and pins you to one exact version forever. |
A type: package repository override |
Re-declares the package by hand — you now maintain a pinned version and a dist URL per package, forever. |
Forking the package to edit its composer.json |
Highest-maintenance option; you inherit responsibility for keeping the fork in sync. |
This plugin does the job generically and declaratively: name the packages once, and it widens
their php constraint to original || ^8.5 in memory during dependency resolution.
How it works
The plugin subscribes to Composer's PrePoolCreateEvent. As the solver builds its candidate
package pool, the plugin finds each allowlisted package and replaces its php link with
original || <allow> (default ^8.5), preserving the original lower bound. Aliases are
unwrapped so the change lands on the real package definition.
Because this happens during composer update, the widened constraint is written straight into
composer.lock. Every later composer install (deploys, CI, teammates) then reads the relaxed
constraint from the lock and passes the platform check with no flag at all.
Packages that genuinely require a newer PHP than you have still fail to resolve, exactly as they should — only the names you list are touched.
Requirements
- PHP >= 8.4
- Composer 2 (
composer-plugin-api^2.0)
Installation
composer require ctw/ctw-composer-plugin-composerlenientplugin
A Composer plugin must be allow-listed before Composer will execute it:
{
"config": {
"allow-plugins": {
"ctw/ctw-composer-plugin-composerlenientplugin": true
}
}
}
Configuration
Configuration is a list of rules in your root composer.json extra block, namespaced
under ctw. Every rule has the same shape — php is just another requirement, so there is no
special case and no default; set all three keys explicitly:
{
"extra": {
"ctw": {
"ctw-composer-plugin-composerlenientplugin": [
{
"require": "php",
"allow": "^8.5",
"packages": [
"laminas/laminas-cache-storage-adapter-filesystem",
"laminas/laminas-mail",
"laminas/laminas-mime",
"laminas/laminas-serializer",
"laminas/laminas-tag"
]
},
{
"require": "laminas/laminas-servicemanager",
"allow": "^4.0",
"packages": [
"laminas/laminas-form",
"laminas/laminas-inputfilter",
"laminas/laminas-i18n",
"laminas/laminas-router"
]
}
]
}
}
}
Each rule has three keys, all required:
| Key | Meaning |
|---|---|
require |
The requirement to widen. Use php for the platform requirement, or a package name such as laminas/laminas-servicemanager. |
allow |
The constraint OR-ed onto that requirement, e.g. ^8.5 or ^4.0. ^8.5 permits 8.5 up to (not including) 9.0. |
packages |
Exact package names whose declared constraint on require should be widened. A package that does not declare require is skipped. |
A rule missing any key — or whose packages list is empty — is ignored.
The first rule above widens the php constraint of five packages capped at ~8.4.0 so they
install on PHP 8.5. The second turns laminas/laminas-form's declared
laminas/laminas-servicemanager: ^3.22.1 into ^3.22.1 || ^4.0 in memory, so a
servicemanager-4 stack resolves without forking the package. In both cases the original lower
bound is preserved, the widened constraint is baked into composer.lock, and packages outside a
rule's packages list are untouched.
Tip: list transitive dependencies explicitly. If a package such as
laminas/laminas-mailis pulled in by another dependency rather than by your ownrequire, it must still appear in a rule'spackagesor it will block resolution.
Use with care: widening a non-
phprequirement only changes resolution — it does not make incompatible code work. Use it when you have evidence the package already runs against the newer version (for instance, upstream's own CI exercises it on an unreleased branch), and remove the rule once a properly-tagged release ships.
See also: README_LAMINAS.md — a real-world walkthrough of using this plugin to run five Laminas packages on PHP 8.5, including how it compares to the alternatives.
Bootstrapping (first install only)
A plugin cannot rewrite the solver pool until it is itself installed and active, so the very first update — the one that physically installs this plugin — still needs the flag once:
# 1. Installs and activates the plugin (flag required this one time). composer update -W --ignore-platform-req=php+ # 2. Plugin is active and rewrites the pool, baking the relaxed # constraint into composer.lock. No flag. composer update -W
After that, every composer install and composer update runs without
--ignore-platform-req, because the relaxed php constraint already lives in the lock file.
Verifying
After the second update, the lock carries the widened constraint:
php -r '$l=json_decode(file_get_contents("composer.lock"),true); foreach($l["packages"] as $p){ if($p["name"]==="laminas/laminas-tag"){ echo $p["require"]["php"],"\n"; } }' # ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ^8.5
Run an update with -v to see exactly what was relaxed:
ctw-composer-plugin-composerlenientplugin: relaxed php for laminas/laminas-tag -> ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ^8.5
FAQ
Why does composer install say "your php version does not satisfy that requirement" on a
newer PHP, even though the library works?
Because the library declares an upper bound on php (for example ~8.4.0), and Composer
enforces it literally. The code may be compatible, but Composer will not cross a declared cap.
How do I install a package that does not officially support my PHP version?
Add the package to this plugin's packages allowlist and run composer update. The plugin
widens only that package's php constraint; nothing else changes.
Is this safe? Does it patch files in vendor/?
No files are patched. The change is made in memory during dependency resolution and recorded in
composer.lock. Your real PHP version is never spoofed, and every other platform requirement
(extensions, other packages' php) stays enforced.
Does it affect deploys and CI?
Yes — positively. Because the relaxed constraint is baked into composer.lock, deploy and CI
runs use a plain composer install with no --ignore-platform-req flag.
Can I use it for the next PHP, like 8.6 or 8.7?
Yes. Set "allow": "^8.6" (or whatever you need). The default is ^8.5, which permits 8.5
through the 8.x line but stops before 9.0, since a major release may introduce breaking changes.
Testing
composer install
composer test
The suite builds real Composer\Package objects and asserts the relaxed php constraint with
Composer\Semver\Semver, so it exercises actual Composer semantics rather than mocking the
constraint logic.
Caveats
- The plugin only widens a requirement, and only by an OR (
|| <allow>). It never narrows or removes a constraint. - Widening a non-
phprequirement changes only resolution; it cannot make incompatible code work. Use such a rule only when the package already runs against the newer version, and drop it once a tagged release ships. - Only the packages a rule names are affected; every rule's allowlist is deliberately explicit.
- Rules target tagged upstream releases. Branch/dev aliases are out of scope, as the affected packages resolve to plain tags.
Keywords
Composer plugin · ignore-platform-req alternative · your php version does not satisfy that requirement · install package on PHP 8.5 / 8.6 · relax php platform constraint · php upper
bound · unsupported PHP version · config.platform.php alternative · force composer install on
newer PHP · --ignore-platform-reqs.
License
See LICENSE.md.