composer/spdx-licenses
PHP library providing the official SPDX license and exception lists plus validation for SPDX license expressions. Look up licenses by identifier or name, check OSI approval or deprecation status, and validate license strings for Composer and tooling.
Installation:
composer require composer/spdx-licenses
Add to composer.json under require or require-dev depending on use case.
First Use Case:
Validate a license identifier in a composer.json file:
use Composer\Spdx\SpdxLicenses;
$validator = new SpdxLicenses();
$isValid = $validator->validate('MIT'); // Returns true/false
Where to Look First:
Composer\Spdx\SpdxLicenses (main entry point).validate(): Check if a license expression is SPDX-compliant.getLicenseByIdentifier(): Fetch license details by SPDX ID (e.g., 'MIT').isOsiApprovedByIdentifier(): Verify OSI approval status.getResourcesDir() for accessing raw license data.License Validation in Composer Hooks:
Extend Composer’s lifecycle events (e.g., post-autoload-dump) to validate licenses:
use Composer\Spdx\SpdxLicenses;
use Composer\Script\Event;
$composer = $event->getComposer();
$licenses = new SpdxLicenses();
foreach ($composer->getPackage()->getRequires() as $require) {
if (!$licenses->validate($require->getLicense())) {
throw new \RuntimeException("Invalid SPDX license: {$require->getLicense()}");
}
}
Register in composer.json:
"scripts": {
"post-autoload-dump": "php scripts/validate-licenses.php"
}
OSI Compliance Enforcement: Block non-OSI-approved licenses in CI/CD:
$licenses = new SpdxLicenses();
$packageLicense = 'AGPL-3.0-only'; // Example from composer.json
if (!$licenses->isOsiApprovedByIdentifier($packageLicense)) {
throw new \RuntimeException("Non-OSI license detected: $packageLicense");
}
Dynamic License Lookup: Build a license selector UI (e.g., for Laravel admin panels):
$licenses = new SpdxLicenses();
$allLicenses = $licenses->getLicenses(); // Returns associative array of all SPDX licenses
$filtered = array_filter($allLicenses, fn($license) => $licenses->isOsiApprovedByIdentifier($license['licenseId']));
Deprecation Warnings: Log deprecated licenses in audit tools:
$licenses = new SpdxLicenses();
$deprecated = $licenses->isDeprecatedByIdentifier('Old-License-Name');
if ($deprecated) {
Log::warning("Deprecated license found: Old-License-Name");
}
SPDX Expression Parsing:
Validate complex license expressions (e.g., MIT AND Apache-2.0):
$validator = new SpdxLicenses();
$isValid = $validator->validate('MIT AND (Apache-2.0 OR GPL-2.0)');
Laravel Service Provider:
Bind SpdxLicenses to the container for global access:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton(SpdxLicenses::class, function () {
return new SpdxLicenses();
});
}
Use in controllers/middleware:
use Illuminate\Support\Facades\App;
$validator = App::make(SpdxLicenses::class);
Artisan Commands: Create a custom command for license audits:
// app/Console/Commands/AuditLicenses.php
public function handle()
{
$licenses = new SpdxLicenses();
$packages = $this->laravel->getComposer()->getPackages();
foreach ($packages as $package) {
if (!$licenses->validate($package->getLicense())) {
$this->error("Invalid license in {$package->getName()}: {$package->getLicense()}");
}
}
}
Packagist Submissions: Validate licenses before publishing:
$validator = new SpdxLicenses();
if (!$validator->validate($request->input('license'))) {
return response()->json(['error' => 'Invalid SPDX license'], 400);
}
Middleware for License Checks: Restrict routes based on dependency licenses (e.g., block proprietary software):
public function handle($request, Closure $next)
{
$composer = $this->app->get('composer');
$licenses = new SpdxLicenses();
foreach ($composer->getPackage()->getRequires() as $require) {
if ($licenses->isOsiApprovedByIdentifier($require->getLicense())) {
return redirect('/osi-compliant-route');
}
}
return $next($request);
}
Case Sensitivity:
'MIT' and 'mit' are treated as the same), but names are case-sensitive (e.g., 'MIT License' ≠ 'mit license').strtolower() for identifiers but preserve case for names:
$licenses->getLicenseByIdentifier(strtolower($input));
Deprecated Licenses:
'Common-Public-License-1.0') are marked as deprecated in SPDX 3.x but may still appear in legacy composer.json files.isDeprecatedByIdentifier() to handle warnings or block deprecated licenses:
if ($licenses->isDeprecatedByIdentifier($license)) {
throw new \RuntimeException("Deprecated license: $license");
}
Complex Expressions:
MIT AND Apache-2.0 or (GPL-2.0 OR LGPL-2.1) WITH Autoconf-exception-3.0 can fail validation if malformed.$validator->validate('MIT AND (Apache-2.0 OR GPL-2.0)'); // Valid
$validator->validate('MIT AND'); // Invalid (incomplete expression)
Performance with Large Datasets:
getLicenses() returns all SPDX licenses (~200 entries), which may be overkill for simple validation.$license = $licenses->getLicenseByIdentifier('MIT'); // Faster for single lookups
PHP Version Compatibility:
${var} syntax in license texts) may behave differently in older versions.License Exceptions:
'Autoconf-exception-3.0') are separate from licenses and require explicit checks.getExceptionByIdentifier():
$exception = $licenses->getExceptionByIdentifier('Autoconf-exception-3.0');
False Positives in Validation:
$allowedLicenses = ['MIT', 'Apache-2.0', 'GPL-3.0'];
if (!in_array($license, $allowedLicenses) && !$validator->validate($license)) {
throw new \RuntimeException("Invalid or unauthorized license: $license");
}
Inspect Raw Data: Access the underlying JSON data for debugging:
$resourcesDir = SpdxLicenses::getResourcesDir();
$licensesJson = file_get_contents($resourcesDir . '/licenses.json');
Validate SPDX Expressions:
Use the validate() method to debug complex expressions:
$validator = new SpdxLicenses();
$expression = 'MIT AND (Apache-2.0 OR GPL-2.0) WITH Autoconf-exception-3.0';
if (!$validator->validate($expression)) {
echo "Invalid expression: " .
How can I help you explore Laravel packages today?