1
0
Fork 0

Merge pull request #3885 from jakoch/patch-spdx

updated spdx-license handling to include metadata
pull/3939/head
Jordi Boggiano 2015-04-14 23:37:03 +01:00
commit 3a06e6f036
9 changed files with 1616 additions and 345 deletions

View File

@ -1,85 +0,0 @@
#!/usr/bin/env php
<?php
$identifiers = new SPDXLicenseIdentifiersOnline;
$printer = new JsonPrinter;
$printer->printStringArray($identifiers->getStrings());
/**
* SPDX Identifier List from the registry.
*/
class SPDXLicenseIdentifiersOnline
{
const REGISTRY = 'http://www.spdx.org/licenses/';
const EXPRESSION = '//*[@typeof="spdx:License"]/code[@property="spdx:licenseId"]/text()';
private $identifiers;
/**
* @return array
*/
public function getStrings()
{
if ($this->identifiers) {
return $this->identifiers;
}
$this->identifiers = $this->importNodesFromURL(
self::REGISTRY,
self::EXPRESSION
);
return $this->identifiers;
}
private function importNodesFromURL($url, $expressionTextNodes)
{
$doc = new DOMDocument();
$doc->loadHTMLFile($url);
$xp = new DOMXPath($doc);
$codes = $xp->query($expressionTextNodes);
if (!$codes) {
throw new \Exception(sprintf('XPath query failed: %s', $expressionTextNodes));
}
if ($codes->length < 20) {
throw new \Exception('Obtaining the license table failed, there can not be less than 20 identifiers.');
}
$identifiers = array();
foreach ($codes as $code) {
$identifiers[] = $code->nodeValue;
}
return $identifiers;
}
}
/**
* Print an array the way this script needs it.
*/
class JsonPrinter
{
/**
*
* @param array $array
*/
public function printStringArray(array $array)
{
$lines = array('');
$line = &$lines[0];
$last = count($array) - 1;
foreach ($array as $item => $code) {
$code = sprintf('"%s"%s', trim($code), $item === $last ? '' : ', ');
$length = strlen($line) + strlen($code) - 1;
if ($length > 76) {
$line = rtrim($line);
unset($line);
$lines[] = $code;
$line = &$lines[count($lines) - 1];
} else {
$line .= $code;
}
}
$json = sprintf("[%s]", implode("\n ", $lines));
$json = str_replace(array("[\"", "\"]"), array("[\n \"", "\"\n]"), $json);
echo $json;
}
}

9
bin/update-spdx-licenses Normal file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env php
<?php
require __DIR__ . '/../src/bootstrap.php';
use Composer\Util\SpdxLicensesUpdater;
$licenses = new SpdxLicensesUpdater;
$licenses->update();

View File

@ -1,59 +0,0 @@
[
"Glide", "Abstyles", "AFL-1.1", "AFL-1.2", "AFL-2.0", "AFL-2.1", "AFL-3.0",
"AMPAS", "APL-1.0", "Adobe-Glyph", "APAFML", "Adobe-2006", "AGPL-1.0",
"Afmparse", "Aladdin", "ADSL", "AMDPLPA", "ANTLR-PD", "Apache-1.0",
"Apache-1.1", "Apache-2.0", "AML", "APSL-1.0", "APSL-1.1", "APSL-1.2",
"APSL-2.0", "Artistic-1.0", "Artistic-1.0-Perl", "Artistic-1.0-cl8",
"Artistic-2.0", "AAL", "Bahyph", "Barr", "Beerware", "BitTorrent-1.0",
"BitTorrent-1.1", "BSL-1.0", "Borceux", "BSD-2-Clause",
"BSD-2-Clause-FreeBSD", "BSD-2-Clause-NetBSD", "BSD-3-Clause",
"BSD-3-Clause-Clear", "BSD-4-Clause", "BSD-Protection",
"BSD-3-Clause-Attribution", "BSD-4-Clause-UC", "bzip2-1.0.5", "bzip2-1.0.6",
"Caldera", "CECILL-1.0", "CECILL-1.1", "CECILL-2.0", "CECILL-B", "CECILL-C",
"ClArtistic", "MIT-CMU", "CNRI-Python", "CNRI-Python-GPL-Compatible",
"CPOL-1.02", "CDDL-1.0", "CDDL-1.1", "CPAL-1.0", "CPL-1.0", "CATOSL-1.1",
"Condor-1.1", "CC-BY-1.0", "CC-BY-2.0", "CC-BY-2.5", "CC-BY-3.0",
"CC-BY-4.0", "CC-BY-ND-1.0", "CC-BY-ND-2.0", "CC-BY-ND-2.5", "CC-BY-ND-3.0",
"CC-BY-ND-4.0", "CC-BY-NC-1.0", "CC-BY-NC-2.0", "CC-BY-NC-2.5",
"CC-BY-NC-3.0", "CC-BY-NC-4.0", "CC-BY-NC-ND-1.0", "CC-BY-NC-ND-2.0",
"CC-BY-NC-ND-2.5", "CC-BY-NC-ND-3.0", "CC-BY-NC-ND-4.0", "CC-BY-NC-SA-1.0",
"CC-BY-NC-SA-2.0", "CC-BY-NC-SA-2.5", "CC-BY-NC-SA-3.0", "CC-BY-NC-SA-4.0",
"CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0",
"CC-BY-SA-4.0", "CC0-1.0", "Crossword", "CUA-OPL-1.0", "Cube", "D-FSL-1.0",
"diffmark", "WTFPL", "DOC", "Dotseqn", "DSDP", "dvipdfm", "EPL-1.0",
"eCos-2.0", "ECL-1.0", "ECL-2.0", "eGenix", "EFL-1.0", "EFL-2.0",
"MIT-advertising", "MIT-enna", "Entessa", "ErlPL-1.1", "EUDatagrid",
"EUPL-1.0", "EUPL-1.1", "Eurosym", "Fair", "MIT-feh", "Frameworx-1.0",
"FTL", "FSFUL", "FSFULLR", "Giftware", "GL2PS", "Glulxe", "AGPL-3.0",
"GFDL-1.1", "GFDL-1.2", "GFDL-1.3", "GPL-1.0", "GPL-1.0+", "GPL-2.0",
"GPL-2.0+", "GPL-2.0-with-autoconf-exception",
"GPL-2.0-with-bison-exception", "GPL-2.0-with-classpath-exception",
"GPL-2.0-with-font-exception", "GPL-2.0-with-GCC-exception", "GPL-3.0",
"GPL-3.0+", "GPL-3.0-with-autoconf-exception", "GPL-3.0-with-GCC-exception",
"LGPL-2.1", "LGPL-2.1+", "LGPL-3.0", "LGPL-3.0+", "LGPL-2.0", "LGPL-2.0+",
"gnuplot", "gSOAP-1.3b", "HaskellReport", "HPND", "IBM-pibs", "IPL-1.0",
"ImageMagick", "iMatix", "Imlib2", "IJG", "Intel-ACPI", "Intel", "IPA",
"ISC", "JasPer-2.0", "JSON", "LPPL-1.3a", "LPPL-1.0", "LPPL-1.1",
"LPPL-1.2", "LPPL-1.3c", "Latex2e", "BSD-3-Clause-LBNL", "Leptonica",
"Libpng", "libtiff", "LPL-1.02", "LPL-1.0", "MakeIndex", "MTLL", "MS-PL",
"MS-RL", "MirOS", "MITNFA", "MIT", "Motosoto", "MPL-1.0", "MPL-1.1",
"MPL-2.0", "MPL-2.0-no-copyleft-exception", "mpich2", "Multics", "Mup",
"NASA-1.3", "Naumen", "NBPL-1.0", "NetCDF", "NGPL", "NOSL", "NPL-1.0",
"NPL-1.1", "Newsletr", "NLPL", "Nokia", "NPOSL-3.0", "Noweb", "NRL", "NTP",
"Nunit", "OCLC-2.0", "ODbL-1.0", "PDDL-1.0", "OGTSL", "OLDAP-2.2.2",
"OLDAP-1.1", "OLDAP-1.2", "OLDAP-1.3", "OLDAP-1.4", "OLDAP-2.0",
"OLDAP-2.0.1", "OLDAP-2.1", "OLDAP-2.2", "OLDAP-2.2.1", "OLDAP-2.3",
"OLDAP-2.4", "OLDAP-2.5", "OLDAP-2.6", "OLDAP-2.7", "OML", "OPL-1.0",
"OSL-1.0", "OSL-1.1", "OSL-2.0", "OSL-2.1", "OSL-3.0", "OLDAP-2.8",
"OpenSSL", "PHP-3.0", "PHP-3.01", "Plexus", "PostgreSQL", "psfrag",
"psutils", "Python-2.0", "QPL-1.0", "Qhull", "Rdisc", "RPSL-1.0", "RPL-1.1",
"RPL-1.5", "RHeCos-1.1", "RSCPL", "Ruby", "SAX-PD", "Saxpath", "SCEA",
"SWL", "SGI-B-1.0", "SGI-B-1.1", "SGI-B-2.0", "OFL-1.0", "OFL-1.1",
"SimPL-2.0", "Sleepycat", "SNIA", "SMLNJ", "StandardML-NJ",
"SugarCRM-1.1.3", "SISSL", "SISSL-1.2", "SPL-1.0", "Watcom-1.0", "TCL",
"Unlicense", "TMate", "TORQUE-1.1", "TOSL", "Unicode-TOU", "NCSA", "Vim",
"VOSTROM", "VSL-1.0", "W3C", "Wsuipa", "WXwindows", "Xnet", "X11", "Xerox",
"XFree86-1.1", "xinetd", "xpp", "XSkat", "YPL-1.0", "YPL-1.1", "Zed",
"Zend-2.0", "Zimbra-1.3", "Zlib", "zlib-acknowledgement", "ZPL-1.1",
"ZPL-2.0", "ZPL-2.1"
]

1226
res/spdx-licenses.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@ use Composer\Repository\CompositeRepository;
use Composer\Repository\ComposerRepository; use Composer\Repository\ComposerRepository;
use Composer\Repository\PlatformRepository; use Composer\Repository\PlatformRepository;
use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryInterface;
use Composer\Util\SpdxLicense;
/** /**
* @author Robert Schönthal <seroscho@googlemail.com> * @author Robert Schönthal <seroscho@googlemail.com>
@ -115,18 +116,18 @@ EOT
$versions = array($package->getPrettyVersion() => $package->getVersion()); $versions = array($package->getPrettyVersion() => $package->getVersion());
} }
$this->printMeta($input, $output, $package, $versions, $installedRepo, $repos); $this->printMeta($package, $versions, $installedRepo);
$this->printLinks($input, $output, $package, 'requires'); $this->printLinks($package, 'requires');
$this->printLinks($input, $output, $package, 'devRequires', 'requires (dev)'); $this->printLinks($package, 'devRequires', 'requires (dev)');
if ($package->getSuggests()) { if ($package->getSuggests()) {
$this->getIO()->write("\n<info>suggests</info>"); $this->getIO()->write("\n<info>suggests</info>");
foreach ($package->getSuggests() as $suggested => $reason) { foreach ($package->getSuggests() as $suggested => $reason) {
$this->getIO()->write($suggested . ' <comment>' . $reason . '</comment>'); $this->getIO()->write($suggested . ' <comment>' . $reason . '</comment>');
} }
} }
$this->printLinks($input, $output, $package, 'provides'); $this->printLinks($package, 'provides');
$this->printLinks($input, $output, $package, 'conflicts'); $this->printLinks($package, 'conflicts');
$this->printLinks($input, $output, $package, 'replaces'); $this->printLinks($package, 'replaces');
return; return;
} }
@ -283,14 +284,14 @@ EOT
/** /**
* prints package meta data * prints package meta data
*/ */
protected function printMeta(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos) protected function printMeta(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo)
{ {
$this->getIO()->write('<info>name</info> : ' . $package->getPrettyName()); $this->getIO()->write('<info>name</info> : ' . $package->getPrettyName());
$this->getIO()->write('<info>descrip.</info> : ' . $package->getDescription()); $this->getIO()->write('<info>descrip.</info> : ' . $package->getDescription());
$this->getIO()->write('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array())); $this->getIO()->write('<info>keywords</info> : ' . join(', ', $package->getKeywords() ?: array()));
$this->printVersions($input, $output, $package, $versions, $installedRepo, $repos); $this->printVersions($package, $versions, $installedRepo);
$this->getIO()->write('<info>type</info> : ' . $package->getType()); $this->getIO()->write('<info>type</info> : ' . $package->getType());
$this->getIO()->write('<info>license</info> : ' . implode(', ', $package->getLicense())); $this->printLicenses($package);
$this->getIO()->write('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference())); $this->getIO()->write('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
$this->getIO()->write('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference())); $this->getIO()->write('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
$this->getIO()->write('<info>names</info> : ' . implode(', ', $package->getNames())); $this->getIO()->write('<info>names</info> : ' . implode(', ', $package->getNames()));
@ -339,7 +340,7 @@ EOT
/** /**
* prints all available versions of this package and highlights the installed one if any * prints all available versions of this package and highlights the installed one if any
*/ */
protected function printVersions(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo, RepositoryInterface $repos) protected function printVersions(CompletePackageInterface $package, array $versions, RepositoryInterface $installedRepo)
{ {
uasort($versions, 'version_compare'); uasort($versions, 'version_compare');
$versions = array_keys(array_reverse($versions)); $versions = array_keys(array_reverse($versions));
@ -361,13 +362,11 @@ EOT
/** /**
* print link objects * print link objects
* *
* @param InputInterface $input
* @param OutputInterface $output
* @param CompletePackageInterface $package * @param CompletePackageInterface $package
* @param string $linkType * @param string $linkType
* @param string $title * @param string $title
*/ */
protected function printLinks(InputInterface $input, OutputInterface $output, CompletePackageInterface $package, $linkType, $title = null) protected function printLinks(CompletePackageInterface $package, $linkType, $title = null)
{ {
$title = $title ?: $linkType; $title = $title ?: $linkType;
if ($links = $package->{'get'.ucfirst($linkType)}()) { if ($links = $package->{'get'.ucfirst($linkType)}()) {
@ -378,4 +377,29 @@ EOT
} }
} }
} }
/**
* Prints the licenses of a package with metadata
*
* @param CompletePackageInterface $package
*/
protected function printLicenses(CompletePackageInterface $package)
{
$spdxLicense = new SpdxLicense;
$licenses = $package->getLicense();
foreach($licenses as $licenseId) {
$license = $spdxLicense->getLicenseByIdentifier($licenseId); // keys: 0 fullname, 1 osi, 2 url
// is license OSI approved?
if($license[1] === true) {
$out = sprintf('%s (%s) (OSI approved) %s', $license[0], $licenseId, $license[2]);
} else {
$out = sprintf('%s (%s) %s', $license[0], $licenseId, $license[2]);
}
$this->getIO()->write('<info>license</info> : ' . $out);
}
}
} }

View File

@ -82,7 +82,7 @@ class ConfigValidator
} }
} }
$licenseValidator = new SpdxLicenseIdentifier(); $licenseValidator = new SpdxLicense();
if ('proprietary' !== $manifest['license'] && array() !== $manifest['license'] && !$licenseValidator->validate($manifest['license'])) { if ('proprietary' !== $manifest['license'] && array() !== $manifest['license'] && !$licenseValidator->validate($manifest['license'])) {
$warnings[] = sprintf( $warnings[] = sprintf(
'License %s is not a valid SPDX license identifier, see http://www.spdx.org/licenses/ if you use an open license.' 'License %s is not a valid SPDX license identifier, see http://www.spdx.org/licenses/ if you use an open license.'

View File

@ -1,178 +1,229 @@
<?php <?php
/* /*
* This file is part of Composer. * This file is part of Composer.
* *
* (c) Nils Adermann <naderman@naderman.de> * (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be> * Jordi Boggiano <j.boggiano@seld.be>
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
namespace Composer\Util; namespace Composer\Util;
use Composer\Json\JsonFile; use Composer\Json\JsonFile;
/** /**
* Supports composer array and SPDX tag notation for disjunctive/conjunctive * Supports composer array and SPDX tag notation for disjunctive/conjunctive
* licenses. * licenses.
* *
* @author Tom Klingenberg <tklingenberg@lastflood.net> * @author Tom Klingenberg <tklingenberg@lastflood.net>
*/ */
class SpdxLicenseIdentifier class SpdxLicense
{ {
/** /**
* @var array * @var array
*/ */
private $identifiers; private $licenses;
public function __construct() public function __construct()
{ {
$this->initIdentifiers(); $this->loadLicenses();
} }
/** private function loadLicenses()
* @param array|string $license {
* if(is_array($this->licenses)) {
* @return bool return $this->licenses;
* @throws \InvalidArgumentException }
*/
public function validate($license) $jsonFile = new JsonFile(__DIR__ . '/../../../res/spdx-licenses.json');
{ $this->licenses = $jsonFile->read();
if (is_array($license)) {
$count = count($license); return $this->licenses;
if ($count !== count(array_filter($license, 'is_string'))) { }
throw new \InvalidArgumentException('Array of strings expected.');
} /**
$license = $count > 1 ? '('.implode(' or ', $license).')' : (string) reset($license); * Returns license metadata by license identifier.
} *
if (!is_string($license)) { * @param string $identifier
throw new \InvalidArgumentException(sprintf( *
'Array or String expected, %s given.', gettype($license) * @return array
)); */
} public function getLicenseByIdentifier($identifier)
{
return $this->isValidLicenseString($license); $license = $this->licenses[$identifier];
}
// add URL for the license text (it's not included in the json)
/** $license[2] = 'http://spdx.org/licenses/' . $identifier . '#licenseText';
* Loads SPDX identifiers
*/ return $license;
private function initIdentifiers() }
{
$jsonFile = new JsonFile(__DIR__ . '/../../../res/spdx-identifier.json'); /**
$this->identifiers = $jsonFile->read(); * Returns the short identifier of a license by full name.
} *
* @param string $identifier
/** *
* @param string $identifier * @return string
* */
* @return bool public function getIdentifierByName($name)
*/ {
private function isValidLicenseIdentifier($identifier) foreach ($this->licenses as $identifier => $licenseData) {
{ if($licenseData[0] === $name) { // key 0 = fullname
return in_array($identifier, $this->identifiers); return $identifier;
} }
}
/** }
* @param string $license
* /**
* @return bool * Returns the OSI Approved status for a license by identifier.
* @throws \RuntimeException *
*/ * @return bool
private function isValidLicenseString($license) */
{ public function isOsiApprovedByIdentifier($identifier)
$tokens = array( {
'po' => '\(', return $this->licenses[$identifier][1]; // key 1 = osi approved
'pc' => '\)', }
'op' => '(?:or|and)',
'lix' => '(?:NONE|NOASSERTION)', /**
'lir' => 'LicenseRef-\d+', * Check, if the identifier for a license is valid.
'lic' => '[-+_.a-zA-Z0-9]{3,}', *
'ws' => '\s+', * @param string $identifier
'_' => '.', *
); * @return bool
*/
$next = function () use ($license, $tokens) { private function isValidLicenseIdentifier($identifier)
static $offset = 0; {
$identifiers = array_keys($this->licenses);
if ($offset >= strlen($license)) {
return null; return in_array($identifier, $identifiers);
} }
foreach ($tokens as $name => $token) { /**
if (false === $r = preg_match('{' . $token . '}', $license, $matches, PREG_OFFSET_CAPTURE, $offset)) { * @param array|string $license
throw new \RuntimeException('Pattern for token %s failed (regex error).', $name); *
} * @return bool
if ($r === 0) { * @throws \InvalidArgumentException
continue; */
} public function validate($license)
if ($matches[0][1] !== $offset) { {
continue; if (is_array($license)) {
} $count = count($license);
$offset += strlen($matches[0][0]); if ($count !== count(array_filter($license, 'is_string'))) {
throw new \InvalidArgumentException('Array of strings expected.');
return array($name, $matches[0][0]); }
} $license = $count > 1 ? '('.implode(' or ', $license).')' : (string) reset($license);
}
throw new \RuntimeException('At least the last pattern needs to match, but it did not (dot-match-all is missing?).');
}; if (!is_string($license)) {
throw new \InvalidArgumentException(sprintf(
$open = 0; 'Array or String expected, %s given.', gettype($license)
$require = 1; ));
$lastop = null; }
while (list($token, $string) = $next()) { return $this->isValidLicenseString($license);
switch ($token) { }
case 'po':
if ($open || !$require) { /**
return false; * @param string $license
} *
$open = 1; * @return bool
break; * @throws \RuntimeException
case 'pc': */
if ($open !== 1 || $require || !$lastop) { private function isValidLicenseString($license)
return false; {
} $tokens = array(
$open = 2; 'po' => '\(',
break; 'pc' => '\)',
case 'op': 'op' => '(?:or|and)',
if ($require || !$open) { 'lix' => '(?:NONE|NOASSERTION)',
return false; 'lir' => 'LicenseRef-\d+',
} 'lic' => '[-+_.a-zA-Z0-9]{3,}',
$lastop || $lastop = $string; 'ws' => '\s+',
if ($lastop !== $string) { '_' => '.',
return false; );
}
$require = 1; $next = function () use ($license, $tokens) {
break; static $offset = 0;
case 'lix':
if ($open) { if ($offset >= strlen($license)) {
return false; return null;
} }
goto lir;
case 'lic': foreach ($tokens as $name => $token) {
if (!$this->isValidLicenseIdentifier($string)) { if (false === $r = preg_match('{' . $token . '}', $license, $matches, PREG_OFFSET_CAPTURE, $offset)) {
return false; throw new \RuntimeException('Pattern for token %s failed (regex error).', $name);
} }
// Fall-through intended if ($r === 0) {
case 'lir': continue;
lir: }
if (!$require) { if ($matches[0][1] !== $offset) {
return false; continue;
} }
$require = 0; $offset += strlen($matches[0][0]);
break;
case 'ws': return array($name, $matches[0][0]);
break; }
case '_':
return false; throw new \RuntimeException('At least the last pattern needs to match, but it did not (dot-match-all is missing?).');
default: };
throw new \RuntimeException(sprintf('Unparsed token: %s.', print_r($token, true)));
} $open = 0;
} $require = 1;
$lastop = null;
return !($open % 2 || $require);
} while (list($token, $string) = $next()) {
} switch ($token) {
case 'po':
if ($open || !$require) {
return false;
}
$open = 1;
break;
case 'pc':
if ($open !== 1 || $require || !$lastop) {
return false;
}
$open = 2;
break;
case 'op':
if ($require || !$open) {
return false;
}
$lastop || $lastop = $string;
if ($lastop !== $string) {
return false;
}
$require = 1;
break;
case 'lix':
if ($open) {
return false;
}
goto lir;
case 'lic':
if (!$this->isValidLicenseIdentifier($string)) {
return false;
}
// Fall-through intended
case 'lir':
lir:
if (!$require) {
return false;
}
$require = 0;
break;
case 'ws':
break;
case '_':
return false;
default:
throw new \RuntimeException(sprintf('Unparsed token: %s.', print_r($token, true)));
}
}
return !($open % 2 || $require);
}
}

View File

@ -0,0 +1,67 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Util;
use Composer\Json\JsonFormatter;
/**
* The SPDX Licenses Updater scrapes licenses from the spdx website
* and updates the "res/spdx-licenses.json" file accordingly.
*
* The class is used by the update script "bin/update-spdx-licenses".
*/
class SpdxLicensesUpdater
{
private $licensesUrl = 'http://www.spdx.org/licenses/';
public function update()
{
$json = json_encode($this->getLicenses(), true);
$prettyJson = JsonFormatter::format($json, true, true);
file_put_contents(__DIR__ . '/../../../res/spdx-licenses.json', $prettyJson);
}
private function getLicenses()
{
$licenses = array();
$dom = new \DOMDocument;
$dom->loadHTMLFile($this->licensesUrl);
$xPath = new \DOMXPath($dom);
$trs = $xPath->query('//table//tbody//tr');
// iterate over each row in the table
foreach($trs as $tr) {
$tds = $tr->getElementsByTagName('td'); // get the columns in this row
if($tds->length < 4) {
throw new \Exception('Obtaining the license table failed. Wrong table format. Found less than 4 cells in a row.');
}
if(trim($tds->item(3)->nodeValue) == 'License Text') {
$fullname = trim($tds->item(0)->nodeValue);
$identifier = trim($tds->item(1)->nodeValue);
$osiApproved = ((isset($tds->item(2)->nodeValue) && $tds->item(2)->nodeValue === 'Y')) ? true : false;
// The license URL is not scraped intentionally to keep json file size low.
// It's build when requested, see SpdxLicense->getLicenseByIdentifier().
//$licenseURL = = $tds->item(3)->getAttribute('href');
$licenses += array($identifier => array($fullname, $osiApproved));
}
}
return $licenses;
}
}

View File

@ -2,12 +2,28 @@
namespace Composer\Test\Util; namespace Composer\Test\Util;
use Composer\TestCase; use Composer\TestCase;
use Composer\Util\SpdxLicenseIdentifier; use Composer\Util\SpdxLicense;
class SpdxLicenseIdentifierTest extends TestCase class SpdxLicenseTest extends TestCase
{ {
/**
* @var object
*/
private $license;
public function setUp()
{
$this->license = new SpdxLicense;
}
public static function provideValidLicenses() public static function provideValidLicenses()
{ {
$json = file_get_contents(__DIR__ . '/../../../../res/spdx-licenses.json');
$licenses = json_decode($json, true);
$identifiers = array_keys($licenses);
$valid = array_merge( $valid = array_merge(
array( array(
"MIT", "MIT",
@ -18,7 +34,7 @@ class SpdxLicenseIdentifierTest extends TestCase
"(LGPL-2.0 or GPL-3.0+)", "(LGPL-2.0 or GPL-3.0+)",
"(EUDatagrid and GPL-3.0+)", "(EUDatagrid and GPL-3.0+)",
), ),
json_decode(file_get_contents(__DIR__ . '/../../../../res/spdx-identifier.json')) $identifiers
); );
foreach ($valid as &$r) { foreach ($valid as &$r) {
@ -68,8 +84,7 @@ class SpdxLicenseIdentifierTest extends TestCase
*/ */
public function testValidate($license) public function testValidate($license)
{ {
$validator = new SpdxLicenseIdentifier(); $this->assertTrue($this->license->validate($license));
$this->assertTrue($validator->validate($license));
} }
/** /**
@ -78,8 +93,7 @@ class SpdxLicenseIdentifierTest extends TestCase
*/ */
public function testInvalidLicenses($invalidLicense) public function testInvalidLicenses($invalidLicense)
{ {
$validator = new SpdxLicenseIdentifier(); $this->assertFalse($this->license->validate($invalidLicense));
$this->assertFalse($validator->validate($invalidLicense));
} }
/** /**
@ -88,7 +102,31 @@ class SpdxLicenseIdentifierTest extends TestCase
*/ */
public function testInvalidArgument($invalidArgument) public function testInvalidArgument($invalidArgument)
{ {
$validator = new SpdxLicenseIdentifier(); $this->license->validate($invalidArgument);
$validator->validate($invalidArgument); }
public function testGetLicenseByIdentifier()
{
$license = $this->license->getLicenseByIdentifier('AGPL-1.0');
$this->assertEquals($license[0], 'Affero General Public License v1.0'); // fullname
$this->assertFalse($license[1]); // osi approved
}
public function testGetIdentifierByName()
{
$identifier = $this->license->getIdentifierByName('Affero General Public License v1.0');
$this->assertEquals($identifier, 'AGPL-1.0');
$identifier = $this->license->getIdentifierByName('BSD 2-clause "Simplified" License');
$this->assertEquals($identifier, 'BSD-2-Clause');
}
public function testIsOsiApprovedByIdentifier()
{
$osiApproved = $this->license->isOsiApprovedByIdentifier('MIT');
$this->assertTrue($osiApproved);
$osiApproved = $this->license->isOsiApprovedByIdentifier('AGPL-1.0');
$this->assertFalse($osiApproved);
} }
} }