irstea/composer-require-checker-shim
A lightweight Composer shim for composer-require-checker that lets you run the require checker without pulling the original package into your project's dependencies. Useful for CI pipelines and legacy setups needing compatibility fixes, it exposes the checker binary through a simple drop-in wrapper.
The shim exists to give you maglnet/composer-require-checker when the upstream package can't be installed directly (usually due to Composer 2 dependency conflicts, a pinned PHP version, or the upstream not being allowed by your internal Composer repository). Install it exactly like the real thing:
composer require --dev irstea/composer-require-checker-shim
Then confirm the binary landed:
ls vendor/bin/ | grep composer-require-checker
You should end up with vendor/bin/composer-require-checker. That's the entry point — the shim does not change the CLI contract, it just changes how the package is sourced.
First run, from your project root:
vendor/bin/composer-require-checker check composer.json
This walks your autoload paths, collects every class/function/extension reference it finds, and diffs that against what composer.json declares in require, require-dev, and ext-*. Exit code 0 = clean, non-zero = undeclared dependencies.
Where to look first:
composer.lock — check exactly which upstream version the shim pulled in. That's your real reference point for docs and behavior.vendor/irstea/composer-require-checker-shim/composer.json — look at the replace/provide section and any version pinning. This tells you why the shim exists.The single highest-value first use case: drop it into CI on a repo that has a fat require-dev and confirm you're not accidentally importing dev-only classes into production code paths.
Composer script hook — the lowest-friction integration:
{
"scripts": {
"check-requires": "composer-require-checker check composer.json",
"test": ["@check-requires", "@phpunit"]
}
}
Config file — the checker reads .composer-require-checker.json from CWD. A realistic starting point:
{
"symbol-whitelist": [
"PHP_VERSION_ID",
"Composer\\InstalledVersions"
],
"php-extensions": ["json", "mbstring"],
"scan-files": [
"public/index.php",
"bin/console"
]
}
Use scan-files for entry points that have no autoloadable class behind them (front controllers, CLI binaries). Use symbol-whitelist for things the checker can't trace (magic constants, class_exists string lookups that are legitimately optional).
CI gate (GitLab CI example, since this is the IRSTEA forge):
require-checker:
image: php:8.2-cli
before_script:
- composer install --no-interaction
script:
- vendor/bin/composer-require-checker check --config-file=.composer-require-checker.json composer.json
allow_failure: false
Run it after composer install — it needs the autoloader to resolve classes it can load, and the require-dev tree installed so it knows what's "allowed."
Monorepo / per-package runs — in a Symfony or Laravel skeleton with a packages/ split, invoke per package and pass --composer-json:
for pkg in packages/*/composer.json; do
vendor/bin/composer-require-checker check --composer-json="$pkg" "$pkg"
done
Pre-commit gate — cheap and catches regressions at the source:
# .git/hooks/pre-commit
vendor/bin/composer-require-checker check composer.json || {
echo "Undeclared composer dependencies found."; exit 1;
}
Pre-commit / husky via lint-staged or captainhook works the same way — treat the binary as an opaque exit-code oracle.
Dependency-audit pairing — run composer-require-checker (declares what you use) alongside composer-unused (declares what you don't use). Together they keep composer.json honest. The two are complementary; neither replaces the other.
The shim pins the upstream. Inspect vendor/irstea/composer-require-checker-shim/composer.json for a replace entry like "maglnet/composer-require-checker": "4.8.0". That version may lag several upstream releases. If a fix you need (new PHP version support, bug in extension detection) isn't in the pinned release, you must wait for a shim bump or fork — you cannot simply composer require maglnet/composer-require-checker alongside it.
The binary name is unchanged, but don't assume that by default. Some shims rename the bin to ircrc or similar to avoid collisions if both packages end up installed. If vendor/bin/composer-require-checker is missing, ls vendor/bin/ and adjust your scripts.
ext-* checks are strict but incomplete. The checker detects usage of functions from extensions it knows about, but it cannot know that json_decode implies ext-json unless ext-json is declared and --allow-extensions isn't hiding it. Add missing ext-* to require yourself; the checker flags a usage, not a required extension.
symbol-whitelist is a blunt instrument. Whitelisting a class the checker flagged usually means the class is loaded via spl_autoload_register from a path outside autoload. Prefer fixing autoload paths or adding scan-files; whitelist only for genuinely optional symbols (e.g. Symfony\Component\Console\Command\Command when the CLI is optional).
Composer\InstalledVersions — the checker frequently flags this one. It's provided by Composer itself, not by a package, so it needs to be whitelisted if you reference it directly (common in installed.php probes and runtime version checks).
Dynamic class strings are invisible. $class = 'App\\' . $name; new $class(); will never be traced. Neither will call_user_func('some_fn') where the string is assembled at runtime. Don't rely on the checker to validate factory patterns, container bindings, or service-locator usage.
--no-progress and --verbose matter in CI. --verbose prints every class it loaded and from where — that's your debugging tool when a false positive appears. Run it locally with -vvv and grep for the flagged symbol to see the call site.
Exit codes are the contract. 0 = OK, 1 = findings, 2 = usage/config error. In CI, treat exit 2 as a failure too — it means the config file didn't parse or the autoloader wasn't built, and your pipeline should not go green on that silently.
PHP version drift. The pinned upstream may not support the newest PHP (e.g. PHP 8.4) yet. If composer install throws a platform error, add "config": {"platform": {"php": "8.3"}} to your root composer.json so the checker installs against a compatible target — but be aware you're then lying to the checker about your runtime PHP version, which can mask real ext-* requirements. Use --ignore-platform-reqs sparingly and only in a dedicated audit job.
GitLab-hosted, low-star, single maintainer. There's no Packagist security-advisory feed for a project this size, and no community to catch regressions. Pin it exactly ("irstea/composer-require-checker-shim": "1.2.3") and review the shim's diff on every bump. If IRSTEA's forge goes dark, your CI is one composer update away from breaking — keep the composer.lock committed, and consider a two-weekly mirror job if you depend on this across many repos.
Extension point: fork the shim, not the checker. If you need an upstream version bump or a local patch, fork irstea/... and adjust the replace version. Don't patch vendor/ — it'll vanish on the next composer install.
How can I help you explore Laravel packages today?