drupol/composer-packages
Retrieve the list of packages installed by Composer in your project through a simple, object-oriented API, including names, versions, and metadata—no manual parsing of composer.lock or installed.json required.
Install as a dev dependency (it's a Composer plugin, not a runtime library):
composer require --dev drupol/composer-packages
Allow the plugin. On Composer 2.2+ plugins are blocked unless whitelisted. Add to composer.json:
{
"config": {
"allow-plugins": {
"drupol/composer-packages": true
}
}
}
Then composer install again. If you skip this, the package installs but its hooks never fire — a silent no-op.
Where to look first. Open the package's own composer.json and confirm "type": "composer-plugin" and the extra.class entry (the plugin class Composer boots). Then read that class plus everything in src/ — the public surface is small, and the tests are the best documentation of intended usage. Skim the README for the one or two advertised examples; treat them as the canonical entry point rather than guessing at method names.
First use case. The lowest-friction win is a diagnostic command. Wire a small artisan/console command (or a standalone PHP script in bin/) that asks the package for the installed package list and prints name + version, optionally filtered by vendor or type. Run it once, confirm it matches composer show, and you've validated the integration end-to-end. Everything else (audits, feature gating, reports) builds on that same call.
Reality check before you invest: the last release was 2020-10-24. Verify it installs and behaves on your current PHP/Composer versions in a throwaway branch before designing anything around it, and be prepared to fork or pin.
Wrap it behind one service. Don't call the plugin API from controllers, commands, and build scripts independently. Create a single PackageInventory (or similar) class that wraps the package, returns a normalized array of ['name' => ..., 'version' => ..., 'type' => ...], and is bound in your container. When the underlying package inevitably needs a fork or replacement, you change one file.
CLI diagnostics / about command. The most natural day-to-day use: a command that prints environment info — PHP version, Laravel/Symfony version, and the resolved package inventory. Great for bug reports and support tickets, because you get the exact dependency set in one paste.
Feature gating on optional packages. If your app has pluggable integrations (e.g., an optional spatie/laravel-medialibrary or a specific queue driver), check presence rather than hard-requiring it:
if ($inventory->has('some/optional-package')) {
// register the integration's service provider / routes
}
Keep this to a handful of checks; it's a deployment convenience, not a DI container replacement.
Audit and build artifacts. Generate a JSON/CSV manifest of installed packages + versions in CI, attach it to releases, or diff it between deploys to see exactly what shipped. Pairs well with a license inventory if the package exposes license metadata — verify it does before promising that to compliance.
Guardrails in deploy scripts. Assert expected packages are present (and, for production, that dev-only packages are not). Fail the deploy loudly rather than discovering a missing dependency at runtime.
Testing support. In test bootstrap or a custom assertion, snapshot the inventory so a dependency swap or accidental downgrade breaks a test instead of production.
Long-running processes. Compute the inventory once at boot (service provider register/boot) and cache it in memory or config. It's static data for the life of the deploy — no reason to recompute per request.
vendor/composer/installed.json / lock file). It is not a live lookup. Anything computed and cached will go stale after a composer update — bust the cache on deploy.--no-dev changes the answer. Production installs omit dev packages entirely. Any logic that "checks for a package" must be tested in both composer install and composer install --no-dev modes, or you'll ship a feature that silently disables itself in prod.allow-plugins is the #1 support issue. No whitelist entry → plugin doesn't run → confusing empty results. Check this before debugging anything else.1.2.3 vs v1.2.3.0, branch aliases like dev-main / 1.x-dev). Pick one, normalize it at the boundary, and don't write version comparisons against raw strings. Use Composer\Semver\Semver for constraint checks rather than string logic.psr/log-implementation, composer-plugin-api) and replace/provide entries behave differently from real installs. If you gate on a package name, confirm how this package reports virtuals before relying on has().composer install/update, not runtime failures — so test the install path specifically. If broken, vendor a patched fork via a VCS repository rather than pinning an ancient Composer.require — and if you do, understand you're shipping a Composer plugin into prod for what is usually a build-time concern.--env=local.How can I help you explore Laravel packages today?