shipmonk/composer-dependency-analyser
Fast, zero-dependency tool to analyze Composer dependencies. Detects unused, shadowed, and misplaced packages/extensions by scanning your autoload paths. Works out of the box, highly configurable, and very performant (15k files in ~2s). Compatible with PHP 7.2β8.5.
| Project | Deaddependency | Shadowdependency | Misplacedin require |
Misplaced in require-dev |
Time* |
|---|---|---|---|---|---|
| maglnet/composer-require-checker | β | β | β | β | 124 secs |
| icanhazstring/composer-unused | β | β | β | β | 72 secs |
| shipmonk/composer-dependency-analyser | β | β | β | β | 2 secs |
*Time measured on codebase with ~15 000 files
composer require --dev shipmonk/composer-dependency-analyser
Note that this package itself has zero composer dependencies.
vendor/bin/composer-dependency-analyser
Example output:
Found shadow dependencies!
(those are used, but not listed as dependency in composer.json)
β’ nette/utils
e.g. Nette\Utils\Strings in app/Controller/ProductController.php:24 (+ 6 more)
Found unused dependencies!
(those are listed in composer.json, but no usage was found in scanned paths)
β’ nette/utils
(scanned 13970 files in 2.297 s)
This tool reads your composer.json and scans all paths listed in autoload & autoload-dev sections while analysing you dependencies (both packages and PHP extensions).
composer.json--configcomposer install --no-devrequire-dev to requirerequire to require-dev--composer-json path/to/composer.json for custom path to composer.json--dump-usages symfony/console to show usages of certain package(s), * placeholder is supported--config path/to/config.php for custom path to config file--version display version--help display usage & cli options--verbose to see more example classes & usages--show-all-usages to see all usages--format to use different output format, available are: console (default), junit--disable-ext-analysis to disable php extensions analysis (e.g. ext-xml)--ignore-unknown-classes to globally ignore unknown classes--ignore-unknown-functions to globally ignore unknown functions--ignore-shadow-deps to globally ignore shadow dependencies--ignore-unused-deps to globally ignore unused dependencies--ignore-dev-in-prod-deps to globally ignore dev dependencies in prod code--ignore-prod-only-in-dev-deps to globally ignore prod dependencies used only in dev pathsWhen a file named composer-dependency-analyser.php is located in cwd, it gets loaded automatically.
The file must return ShipMonk\ComposerDependencyAnalyser\Config\Configuration object.
You can use custom path and filename via --config cli option.
Here is example of what you can do:
<?php
use ShipMonk\ComposerDependencyAnalyser\Config\Configuration;
use ShipMonk\ComposerDependencyAnalyser\Config\ErrorType;
$config = new Configuration();
return $config
//// Adjusting scanned paths
->addPathToScan(__DIR__ . '/build', isDev: false)
->addPathToExclude(__DIR__ . '/samples')
->disableComposerAutoloadPathScan() // disable automatic scan of autoload & autoload-dev paths from composer.json
->setFileExtensions(['php']) // applies only to directory scanning, not directly listed files
//// Ignoring errors
->ignoreErrors([ErrorType::DEV_DEPENDENCY_IN_PROD])
->ignoreErrorsOnPath(__DIR__ . '/cache/DIC.php', [ErrorType::SHADOW_DEPENDENCY])
->ignoreErrorsOnPackage('symfony/polyfill-php73', [ErrorType::UNUSED_DEPENDENCY])
->ignoreErrorsOnPackageAndPath('symfony/console', __DIR__ . '/src/OptionalCommand.php', [ErrorType::SHADOW_DEPENDENCY])
->ignoreErrorsOnExtension('ext-intl', [ErrorType::SHADOW_DEPENDENCY])
->ignoreErrorsOnExtensionAndPath('ext-sqlite3', __DIR__ . '/tests', [ErrorType::SHADOW_DEPENDENCY])
//// Ignoring unknown symbols
->ignoreUnknownClasses(['Memcached'])
->ignoreUnknownClassesRegex('~^DDTrace~')
->ignoreUnknownFunctions(['opcache_invalidate'])
->ignoreUnknownFunctionsRegex('~^opcache_~')
//// Adjust analysis
->enableAnalysisOfUnusedDevDependencies() // dev packages are often used only in CI, so this is not enabled by default
->disableReportingUnmatchedIgnores() // do not report ignores that never matched any error
->disableExtensionsAnalysis() // do not analyse ext-* dependencies
//// Use symbols from yaml/xml/neon files
// - designed for DIC config files (see below)
// - beware that those are not validated and do not even trigger unknown class error
->addForceUsedSymbols($classesExtractedFromNeonJsonYamlXmlEtc)
All paths are expected to exist. If you need some glob functionality, you can do it in your config file and pass the expanded list to e.g. ignoreErrorsOnPaths.
Some classes might be used only in your DIC config files. Here is a simple way to extract those:
$classNameRegex = '[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*'; // https://www.php.net/manual/en/language.oop5.basic.php
$dicFileContents = file_get_contents(__DIR__ . '/config/services.yaml');
preg_match_all(
"~$classNameRegex(?:\\\\$classNameRegex)+~", // at least one backslash
$dicFileContents,
$matches
); // or parse the yaml properly
$config->addForceUsedSymbols($matches[1]); // possibly filter by class_exists || interface_exists
Similar approach should help you to avoid false positives in unused dependencies. Another approach for DIC-only usages is to scan the generated php file, but that gave us worse results.
--composer-json to composer.json of the other codebaseNO_COLOR environment variable to disable colored output:NO_COLOR=1 vendor/bin/composer-dependency-analyser
ext-* analysis, your enabled extensions of your php runtime should be superset of those used in the scanned projectcomposer checkcomposer fix:csHow can I help you explore Laravel packages today?