laravel-package-url maintained by gumslone
laravel-package-url
Package URL (purl) for Laravel — parse, build, normalize and validate purls, plus registry / repository / download URL resolution, vulnerability-database ecosystem mapping, and reverse-parsing from VCS URLs.
The core is a faithful, dependency-light implementation of the purl spec that
passes the entire official package-url/purl-spec conformance suite (base
spec + all 40+ type definitions) — the same suite
packageurl-php and
packageurl-python test
against — and then goes further with the helpers you actually need in an app.
Installation
composer require gumslone/laravel-package-url
The service provider and Purl facade are auto-discovered.
Core: parse, build, normalize, validate
use Gumslone\PackageUrl\PackageUrl;
$purl = PackageUrl::fromString('pkg:composer/laravel/framework@11.55.0');
$purl->type; // "composer"
$purl->namespace; // "laravel"
$purl->name; // "framework"
$purl->version; // "11.55.0"
$purl->qualifiers; // []
$purl->subpath; // null
(string) $purl; // "pkg:composer/laravel/framework@11.55.0"
$purl->toArray(); // ['type' => 'composer', 'namespace' => 'laravel', ...]
// Build from components (with normalization + validation)
PackageUrl::fromArray([
'type' => 'PYPI',
'name' => 'Django_package',
'version' => '1.11.1',
])->toString(); // "pkg:pypi/django-package@1.11.1" (type + name normalized)
// Immutable withers (each returns a new normalized PackageUrl)
$purl->withVersion('12.0.0');
$purl->withNamespace('symfony')->withName('console');
$purl->withType('composer');
$purl->withSubpath('src/Console');
$purl->withQualifier('repository_url', 'https://example.org'); // '' removes it
$purl->withoutQualifier('repository_url');
$purl->withoutVersion();
// Read
$purl->qualifier('repository_url'); // ?string
$purl->equals('pkg:composer/laravel/framework@11.55.0'); // true
// Type checks
$purl->isType('composer'); // true (case-insensitive)
$purl->isGeneric(); // false
Normalization follows the spec: the type is lowercased, qualifier keys are
lowercased/sorted with empty values dropped, subpaths are cleaned, and
type-specific rules apply (e.g. pypi lowercases the name and turns _ into
-; github/bitbucket/golang lowercase namespace and name). Invalid
purls throw PackageUrlException — including per-type rules such as swift
requiring a namespace, vcpkg/otp forbidding one, and chrome-extension
id/version formats.
Extended: the Purl facade
use Purl;
Purl::isValid('pkg:npm/left-pad@1.3.0'); // true
Purl::parse('pkg:npm/left-pad@1.3.0'); // PackageUrl
Purl::parseMany([...]); // Collection<PackageUrl>, invalid skipped
// Find every purl embedded in a block of text (SBOM, manifest, log, changelog)
Purl::extractAll($text); // Collection<PackageUrl>, de-duplicated
Checks
Non-throwing booleans to guard a conversion before you attempt it:
// Type
Purl::isType('pkg:cargo/serde@1.0', 'cargo'); // true
Purl::isGeneric('pkg:generic/tool@1.0'); // true
// Can this purl produce a URL?
Purl::hasRepositoryUrl('pkg:composer/laravel/framework@11.0'); // true
Purl::hasDownloadUrl('pkg:npm/left-pad@1.3.0'); // true
Purl::hasDownloadUrl('pkg:npm/left-pad'); // false (no version)
Purl::hasRegistryApiUrl('pkg:cargo/serde@1.0'); // true
Purl::isConvertibleToUrl('pkg:npm/left-pad'); // true (any of the above)
// Can this URL be turned into a purl?
Purl::isConvertibleDownloadUrl('https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz'); // true
Purl::isConvertibleUrl('git@github.com:laravel/framework.git'); // true (also accepts repo/SSH URLs)
Registry, repository & download URLs
Purl::repositoryUrl('pkg:composer/laravel/framework@11.55.0');
// https://packagist.org/packages/laravel/framework
Purl::repositoryUrl('pkg:pypi/django@5.0');
// https://pypi.org/project/django/5.0/
Purl::registryApiUrl('pkg:npm/left-pad@1.3.0');
// https://registry.npmjs.org/left-pad
Purl::downloadUrl('pkg:npm/@babel/core@7.0.0');
// https://registry.npmjs.org/@babel/core/-/core-7.0.0.tgz
Purl::downloadUrl('pkg:maven/org.apache.commons/commons-lang3@3.14.0');
// https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.14.0/commons-lang3-3.14.0.jar
Coverage matches (and extends) packageurl-python's purl2url:
- repositoryUrl — cargo, bitbucket, github, gitlab, gem, cran, npm, pypi, composer, nuget, hackage, golang, cocoapods, maven (+ hex, pub, conda).
- downloadUrl — cargo, gem, npm, maven, hackage, nuget, github, gitlab, bitbucket, hex, golang, pub, swift, luarocks, conda, alpm, deb, apk.
A download_url qualifier always takes precedence. Distro artifact URLs
(deb/apk/alpm/conda) read the relevant qualifiers (arch, repo, channel, …).
Vulnerability-database ecosystem mapping
Purl::osvEcosystem('pkg:pypi/django@5.0'); // "PyPI" (OSV.dev)
Purl::osvEcosystem('pkg:composer/laravel/framework@11.0'); // "Packagist"
Purl::githubEcosystem('pkg:npm/left-pad@1.3.0'); // "NPM" (GitHub Advisories)
Reverse-parse from a URL
From a VCS repository URL:
Purl::fromRepositoryUrl('https://github.com/laravel/framework');
// pkg:github/laravel/framework
Purl::fromRepositoryUrl('git@github.com:laravel/framework.git');
// pkg:github/laravel/framework
Purl::fromRepositoryUrl('https://github.com/laravel/framework/tree/v11.0.0');
// pkg:github/laravel/framework@v11.0.0
From a download / registry URL — the inverse of downloadUrl():
Purl::fromDownloadUrl('https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz');
// pkg:npm/left-pad@1.3.0
Purl::fromDownloadUrl('https://files.pythonhosted.org/packages/ab/cd/Django-5.0-py3-none-any.whl');
// pkg:pypi/django@5.0
Purl::fromDownloadUrl('https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.14.0/commons-lang3-3.14.0.jar');
// pkg:maven/org.apache.commons/commons-lang3@3.14.0
Purl::fromDownloadUrl('https://github.com/laravel/framework/archive/refs/tags/v11.0.0.tar.gz');
// pkg:github/laravel/framework@v11.0.0
// Try both, whichever matches
Purl::fromUrl($anyPackageOrRepoUrl);
Recognition covers (and extends) packageurl-python's url2purl: npm
(incl. yarnpkg mirrors), PyPI (CDN files, /packages/source, project/JSON
API), RubyGems, crates.io, NuGet, Maven, Go (proxy + pkg.go.dev), Hex,
Composer/Packagist, pub.dev, CocoaPods, Hackage, LuaRocks, CRAN, SourceForge
and Google Code archives (→ pkg:generic), and the full GitHub / GitLab /
Bitbucket surface — web, REST API (api.github.com/repos),
codeload.github.com, archive/release/download, and raw/blob/tree/
commit refs. Returns null for anything unrecognised.
Pairs well with
gumslone/laravel-purl2cpe— resolve a purl to its NVD CPE (vendor/product) from the curated scanoss/purl2cpe database.
Testing
composer install
composer test
The suite runs the bundled official conformance data under tests/data.
Credits
- The purl specification and conformance suite: package-url/purl-spec (MIT).
Support
If this package saves you time, consider supporting its development:
License
MIT. See LICENSE. Bundled test fixtures are from package-url/purl-spec, also MIT.