CLI tool
maglnet/ComposerRequireChecker avatar
maglnet/ComposerRequireChecker

ComposerRequireChecker: catching the Guzzle you never asked for

A CLI tool to check whether a specific composer package uses imported symbols that aren't part of its direct composer dependencies

1,011 stars80 forksPHPMIT

At a glance

What is it?
ComposerRequireChecker is a CLI tool that parses your PHP sources and your composer.json to find symbols you use but never declared, so a transitive dependency can be upgraded out from under you without breaking your code. It does not install anything and does not modify your code, and it is a development tool you run against a checkout rather than a dependency of your project.
Who is it for?
ComposerRequireChecker suits a team maintaining a PHP library that others install, since the whole point is that a consumer's install can resolve differently from your own, and the tool tells you which of your imports are only accidentally present. It is the wrong tool for an application, where the lock file pins the resolution anyway, and it is the wrong choice for a project with custom Composer installer plugins, which the README says it cannot understand.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The failure mode it is built for

The README explains soft dependencies through a story, and the story is the whole argument.

You require thatvendor/api-lib and use it. That is a hard dependency. Then you see another remote API is available, no library exists for it, the use case is simple, so you find that guzzlehttp/guzzle is already installed and use it right away to fetch some info. Guzzle just became a soft dependency.

Then someday, when you update your dependencies, your access to the second API breaks. The reason guzzlehttp/guzzle was installed is that it is a dependency of thatvendor/api-lib, and their developers decided to update from an earlier major version to the latest, stating in their changelog that version 3.1.0 uses the latest major version of Guzzle with no breaking changes expected. And you think: what about my broken code?

The tool parses your code and your composer.json file to see whether your code uses symbols that are not declared as a required library. If you rely on components that are already installed but didn't explicitly request them, the tool complains, and you should require them explicitly, making them hard dependencies.

The payoff is stated precisely: in that situation you would not get the latest update of thatvendor/api-lib, but your code would continue to work if you had also required guzzlehttp/guzzle before the update.

It also checks for usage of PHP functions that are only available if an extension is installed, and complains if that extension isn't explicitly required. So the same check covers both the Composer level and the PHP extension level, which is where a lot of the same class of bug lives.

Three install routes, and the one the author prefers

The installation section opens with a rule that matters more than the commands that follow: ComposerRequireChecker is not supposed to be installed as part of your project dependencies.

That is a design decision with a reason behind it. If the tool were a dev dependency of your project, it would be resolved by the same Composer logic it is auditing, and a soft dependency of the tool could itself go missing. Keeping it outside the project avoids that circularity.

The preferred route is the PHAR file. The README points to the releases page for available PHAR files and gives a direct download link for the latest release, then the invocation.

bash
php composer-require-checker.phar check /path/to/your/project/composer.json

If you already use PHIVE to install and manage your project's tooling, then you can install it the same way.

bash
phive install composer-require-checker

And there is a global Composer install.

bash
composer global require maglnet/composer-require-checker
composer-require-checker check composer.json

The composer.json here refers to the root Composer manifest of your project, and the README notes that if you have not already set up Composer to support global requirements, you should refer to the Composer CLI documentation for that.

The PHAR route has a practical advantage beyond isolation, since it needs nothing but a PHP binary on the path. For a CI job that checks many repositories, downloading one PHAR is simpler than managing a global Composer state per runner.

Xdebug will slow you down and break your nesting level

There is a short note in the README that saves a confusing debugging session, and it is specific enough to be worth quoting in full.

If your PHP is including Xdebug when running ComposerRequireChecker, you may experience additional issues like exceeding the Xdebug-related max-nesting-level, and on top, Xdebug slows PHP down. It is recommended to run ComposerRequireChecker without Xdebug.

If you cannot provide a PHP instance without Xdebug yourself, try setting an environment variable like this for just the command.

bash
XDEBUG_MODE=off php composer-require-checker

The nesting level point is the interesting one. The tool parses PHP source files, and parsing is recursive, so a deeply nested file can push the stack past the limit Xdebug imposes. Without Xdebug that limit does not exist, so the tool works, and with Xdebug you get a failure that looks like a bug in the tool.

XDEBUG_MODE=off is the right fix rather than uninstalling the extension, because it is scoped to the one command. In a CI image where Xdebug is installed for profiling, this is the difference between a check that runs and one that fails on the largest file in the repository.

The repository also carries infection.json.dist, which is the configuration for mutation testing, alongside phpunit.xml.dist, psalm.xml, phpstan.neon and phpcs.xml.dist. The presence of all four static analysis and testing tools says something about how the project itself is held to account.

One configuration file, and the whole file has to be copied

The configuration section describes a design decision that looks like a bug until you understand it.

ComposerRequireChecker is configured to whitelist some symbols per default, and there is a config file example at data/config.dist.json showing which options are available. You can adjust the file and tell composer-require-checker to use it. If you want to use the default whitelist, you may remove that section and only adjust the sections you would like to change.

Then the warning. If you want to add something on top of a section, you will have to copy the whole section's content. The tool intentionally only reads one configuration file. If you pass only your new settings, you get error reports about the PHP core extensions and internal symbols like true or false being undefined.

bash
bin/composer-require-checker check --config-file=path/to/config.json /path/to/your/project/composer.json

By default it uses composer-require-checker.json if the file exists.

The reason for the design is that the default configuration is the source of truth for what the tool considers built in, and merging a partial config into it would mean two places to keep in sync. The cost is that a user who wants to add one whitelisted symbol has to copy a large block of defaults into their own file, and a later upstream change to the defaults will not reach them.

There is a second, smaller feature here. To scan files that are not part of your autoload definition, you add glob patterns to the config file's scan-files section, and the example given would scan the file bin/console and all files with the .php extension within your bin/ folder.

json
{
    "scan-files" : ["bin/console", "bin/*.php"]
}

The README also suggests adding those paths to the Composer autoloading section of your project instead, if you would rather not copy the tool's default settings.

It reads your code, it does not install your code

The usage section is short and its framing is what makes the tool safe to run on a codebase you care about.

ComposerRequireChecker runs on an existing directory structure. It does not change your code and does not even install your Composer dependencies. That is a task that is entirely up to you, allowing you to change or improve things after a scan to see if it fixes the issue.

The usual workflow is three steps. Clone your repo, run composer install for your dependencies, and run composer-require-checker check on your code.

The deliberate separation of the install step from the check step is what makes the tool usable as a gate. Because it never modifies anything, you can run it on a branch, see the report, add the missing requires, and then re-run. There is no state to clean up and no risk of it having half-modified a vendor directory.

The tool also does not need to be a hard dependency of anything. It is a checker for the moment before you publish, which is the moment when the consumer's resolution of your package might differ from yours.

The last piece of the workflow documentation covers custom installer plugins, and it is a genuine limitation rather than a caveat. ComposerRequireChecker only fetches its knowledge of where files are from your project's composer.json. It does not use Composer itself to understand custom directory structures. If your project requires install plugins to put files in directories that are not vendor/ or defined via the vendor-dir config setting, the tool will fail to detect the required code correctly.

The workaround is documented, and it is a little awkward. Install your dependencies without plugins just for the scan, run the check, and then install the dependencies once again in the correct location.

A 4.x tool with an active maintenance branch

The default branch is named 4.25.x, and the release history shows 4.24.0 on 2026-03-20, 4.23.0 on the same day earlier, and 4.22.0 on 2026-03-10. The last push was on 2026-10-16, so work is happening on the branch ahead of the most recent tag.

The 4.x numbering is worth noting, because the branch name and the tags together tell you the project has been through several breaking releases and is now maintaining a stable line. A 4.25.x default branch with 4.24.0 as the newest tag means the tip of the branch is preparing the next minor release.

The two tags on the same day, 4.24.0 and 4.23.0, is the same pattern seen elsewhere in the PHP tooling world, where a release is cut and an immediate follow-up corrects something in it.

The repository layout is a conventional PHP library: src/ and test/ directories, composer.json and composer.lock, a data/ directory that holds the default configuration file, a bin/ directory for the entry point, and a build.xml for the Ant-based build. The contributor credits name Marco Pivetta, known as ocramius, who designed the tool initially, and Matthias Glaub of maglnet.

The licence is MIT, with the LICENSE file at the root, and the tool ships a Renovate configuration for dependency updates.

For an adopter the practical points are short. Run it as a PHAR in CI, disable Xdebug, copy the whole default config if you need to change it, and remember that a project using Composer installer plugins needs a separate install step for the scan to see the right files.

Editorial conclusion

ComposerRequireChecker suits a team maintaining a PHP library that others install, since the whole point is that a consumer's install can resolve differently from your own, and the tool tells you which of your imports are only accidentally present. It is the wrong tool for an application, where the lock file pins the resolution anyway, and it is the wrong choice for a project with custom Composer installer plugins, which the README says it cannot understand. Before you add it to CI, copy the whole default configuration rather than a fragment, because the tool reads only one configuration file and a partial one produces false reports about PHP core symbols like true and false.

Frequently asked questions

What problem does ComposerRequireChecker solve?

It analyses your code and your composer.json to find symbols you use that are not declared as required libraries, so soft dependencies get turned into hard ones. That prevents a transitive dependency being upgraded out from under your code and breaking it.

How do I install ComposerRequireChecker?

Download the PHAR from the releases page and run php composer-require-checker.phar check path/to/composer.json, or use phive install composer-require-checker, or composer global require maglnet/composer-require-checker. The README states it is not supposed to be installed as part of your project dependencies.

Does ComposerRequireChecker install or modify my code?

No. It runs on an existing directory structure, does not change your code and does not even install your Composer dependencies, so you can change things after a scan and re-run it.

Why do I get errors about PHP core symbols like true and false being undefined?

The tool intentionally only reads one configuration file, so if you pass only your new settings instead of copying the whole default configuration, it loses the default whitelist of PHP core extensions and internal symbols.

What happens if my project uses Composer installer plugins?

The tool only fetches its knowledge of where files are from your composer.json and does not use Composer itself to understand custom directory structures, so it will fail to detect required code correctly. The documented workaround is to run composer install --no-plugins for the scan and then install the dependencies again in the correct location.

Official sources

  1. Issues
  2. License: MIT
  3. maglnet/ComposerRequireChecker on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/maglnet-composerrequirechecker.svg)](https://hysenlabs.com/projects/maglnet-composerrequirechecker)

Was this useful?