lendable/composer-license-checker
A dev tool that checks Composer dependency licenses against configurable rules to ensure open source compliance and reduce legal risk. Define allowed licenses, vendors, and packages in a PHP config file, then run it in CI. Uses installed.json or composer licenses as its data source.
This is a framework-agnostic Composer CLI tool, not a Laravel package — there's no service provider, no publishable config, and no php artisan command. Expect to interact with it exclusively through vendor/bin and CI.
1.4.x requires PHP 8.4, 1.3.x requires 8.3, 1.2.x requires 8.2. On a PHP 8.2 Laravel app, composer require --dev lendable/composer-license-checker will silently resolve to ^1.2 — fine, but be aware you may not have newer flags.composer require --dev lendable/composer-license-checker
.allowed-licenses.php in the project root (not config/ — see Gotchas):<?php
declare(strict_types=1);
use Lendable\ComposerLicenseChecker\LicenseConfigurationBuilder;
return (new LicenseConfigurationBuilder())
->addLicenses('MIT', 'BSD-2-Clause', 'BSD-3-Clause', 'Apache-2.0', 'ISC')
->addAllowedVendor('your-company')
->build();
./vendor/bin/composer-license-checker
The first run on a typical Laravel app will flag a handful of dependencies (often packages declaring proprietary, no license, or an unusual SPDX expression). Add licenses deliberately, and prefer addAllowedPackage() over addLicenses() for one-offs so you don't widen the policy for the whole dependency tree.
First real use case: run it once locally, commit the resulting config, then treat it as a policy file under code review.
Wire it into the fast-feedback CI job. Put it alongside Pint/PHPStan so license failures surface in the same minute as style failures, not after your test matrix:
- run: composer install --no-interaction --prefer-dist
- run: ./vendor/bin/composer-license-checker --format=json > licenses.json
--format (human-readable / JSON) landed in 1.0.3; use JSON if you want to upload the report as an artifact or convert violations into PR annotations.
Expose it as a Composer script so devs and CI run the same thing:
"scripts": {
"check:licenses": "composer-license-checker"
}
Pick your provider deliberately. The default --provider-id=json parses vendor/composer/installed.json and needs no Composer binary in the environment — ideal for slim CI images and containers (Sail, custom Dockerfiles). --provider-id=licenses shells out to composer licenses, which is a more stable contract but requires Composer and adds process overhead. Use the default unless you hit a schema problem.
Decide which dependency set you audit. The checker reports on what is actually installed. A full composer install includes packages-dev; a --no-dev install does not — and also removes the checker itself. If you want to audit the production set only, use a dedicated CI job that installs fully, runs the check, and only then prunes.
Use the escape hatches with intent. addAllowedVendor('your-company') for first-party packages pulled in via path/VCS repos (they frequently omit a license field). addAllowedPackage('vendor/thing') for a single known exception — keep these grouped and commented in the config file, since the builder has no built-in expiry or reason metadata.
If you maintain a Laravel package, the same config file works there; use -a / --allow-file to place your allowlist anywhere you like, e.g. .config/allowed-licenses.php in a monorepo.
Wrap it if you want an Artisan entry point. A thin command that shells out to the binary via Symfony\Component\Process\Process gives php artisan licenses:check for devs who live in Artisan, without forking the tool.
config/. Laravel's config loader globs config/*.php and expects each file to return an array; a file returning a LicenseConfiguration object will break config:cache. A dotfile (.allowed-licenses.php) in the project root is skipped by glob() and is safe.1.1.1), but a parse error surfaces as a confusing failure. Sanity-check with php .allowed-licenses.php in isolation.installed.json is Composer-internal. The README says so explicitly. A Composer major upgrade can change the schema; if you see parse/validation errors after a Composer bump, switch to --provider-id=licenses first, then report upstream.installed.json. If you edit composer.lock without reinstalling, you're auditing the old tree. Always run the check immediately after composer install in the same job.0.1.1), which is the right behaviour but means private/path packages will fail the build until you allowlist their vendor or package.MIT OR Apache-2.0) were mishandled until 1.0.4. On older pins you'll get false positives; upgrade rather than allowlisting the whole vendor.--format support before using it. It arrived in 1.0.3; if Composer resolved you to ^1.2 because of PHP 8.2, verify the flag exists rather than discovering it in CI. Treat the JSON shape as reporting output, not a stable contract — parse defensively.-v/-vvv for the Symfony Console side. Verbosity of the composer licenses subprocess is controlled by the SHELL_VERBOSITY environment variable (moved there in 1.0.3), so set it in the job env if subprocess output is missing or noisy.LICENSE files, doesn't generate NOTICE/attribution files required by Apache-2.0, and doesn't evaluate compatibility between licenses. It's a gate, not a compliance program.composer licenses plus your own parser with an hour's work. The only real extension point is LicenseConfigurationBuilder; the event system is internal and undocumented, so a custom provider means forking.How can I help you explore Laravel packages today?