|
@@ -12,8 +12,6 @@
|
|
|
|
|
|
namespace Composer\Util;
|
|
namespace Composer\Util;
|
|
|
|
|
|
-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.
|
|
@@ -22,53 +20,60 @@ use Composer\Json\JsonFile;
|
|
*/
|
|
*/
|
|
class SpdxLicense
|
|
class SpdxLicense
|
|
{
|
|
{
|
|
- /**
|
|
|
|
- * @var array
|
|
|
|
- */
|
|
|
|
|
|
+ /** @var array */
|
|
private $licenses;
|
|
private $licenses;
|
|
|
|
|
|
|
|
+ /** @var array */
|
|
|
|
+ private $exceptions;
|
|
|
|
+
|
|
public function __construct()
|
|
public function __construct()
|
|
{
|
|
{
|
|
$this->loadLicenses();
|
|
$this->loadLicenses();
|
|
|
|
+ $this->loadExceptions();
|
|
}
|
|
}
|
|
|
|
|
|
- private function loadLicenses()
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Returns license metadata by license identifier.
|
|
|
|
+ *
|
|
|
|
+ * @param string $identifier
|
|
|
|
+ *
|
|
|
|
+ * @return array|null
|
|
|
|
+ */
|
|
|
|
+ public function getLicenseByIdentifier($identifier)
|
|
{
|
|
{
|
|
- if (is_array($this->licenses)) {
|
|
|
|
- return $this->licenses;
|
|
|
|
|
|
+ if (!isset($this->licenses[$identifier])) {
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
- $jsonFile = new JsonFile(__DIR__ . '/../../../res/spdx-licenses.json');
|
|
|
|
- $this->licenses = $jsonFile->read();
|
|
|
|
|
|
+ $license = $this->licenses[$identifier];
|
|
|
|
+ $license[] = 'http://spdx.org/licenses/' . $identifier . '.html#licenseText';
|
|
|
|
|
|
- return $this->licenses;
|
|
|
|
|
|
+ return $license;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Returns license metadata by license identifier.
|
|
|
|
|
|
+ * Returns license exception metadata by license exception identifier.
|
|
*
|
|
*
|
|
* @param string $identifier
|
|
* @param string $identifier
|
|
*
|
|
*
|
|
* @return array|null
|
|
* @return array|null
|
|
*/
|
|
*/
|
|
- public function getLicenseByIdentifier($identifier)
|
|
|
|
|
|
+ public function getExceptionByIdentifier($identifier)
|
|
{
|
|
{
|
|
- if (!isset($this->licenses[$identifier])) {
|
|
|
|
|
|
+ if (!isset($this->exceptions[$identifier])) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- $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';
|
|
|
|
|
|
+ $license = $this->exceptions[$identifier];
|
|
|
|
+ $license[] = 'http://spdx.org/licenses/' . $identifier . '.html#licenseExceptionText';
|
|
|
|
|
|
return $license;
|
|
return $license;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Returns the short identifier of a license by full name.
|
|
|
|
|
|
+ * Returns the short identifier of a license (exception) by full name.
|
|
*
|
|
*
|
|
- * @param string $identifier
|
|
|
|
|
|
+ * @param string $name
|
|
*
|
|
*
|
|
* @return string
|
|
* @return string
|
|
*/
|
|
*/
|
|
@@ -79,11 +84,19 @@ class SpdxLicense
|
|
return $identifier;
|
|
return $identifier;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ foreach ($this->exceptions as $identifier => $licenseData) {
|
|
|
|
+ if ($licenseData[0] === $name) { // key 0 = fullname
|
|
|
|
+ return $identifier;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Returns the OSI Approved status for a license by identifier.
|
|
* Returns the OSI Approved status for a license by identifier.
|
|
*
|
|
*
|
|
|
|
+ * @param string $identifier
|
|
|
|
+ *
|
|
* @return bool
|
|
* @return bool
|
|
*/
|
|
*/
|
|
public function isOsiApprovedByIdentifier($identifier)
|
|
public function isOsiApprovedByIdentifier($identifier)
|
|
@@ -105,6 +118,20 @@ class SpdxLicense
|
|
return in_array($identifier, $identifiers);
|
|
return in_array($identifier, $identifiers);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Check, if the identifier for a exception is valid.
|
|
|
|
+ *
|
|
|
|
+ * @param string $identifier
|
|
|
|
+ *
|
|
|
|
+ * @return bool
|
|
|
|
+ */
|
|
|
|
+ private function isValidExceptionIdentifier($identifier)
|
|
|
|
+ {
|
|
|
|
+ $identifiers = array_keys($this->exceptions);
|
|
|
|
+
|
|
|
|
+ return in_array($identifier, $identifiers);
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* @param array|string $license
|
|
* @param array|string $license
|
|
*
|
|
*
|
|
@@ -118,18 +145,49 @@ class SpdxLicense
|
|
if ($count !== count(array_filter($license, 'is_string'))) {
|
|
if ($count !== count(array_filter($license, 'is_string'))) {
|
|
throw new \InvalidArgumentException('Array of strings expected.');
|
|
throw new \InvalidArgumentException('Array of strings expected.');
|
|
}
|
|
}
|
|
- $license = $count > 1 ? '('.implode(' or ', $license).')' : (string) reset($license);
|
|
|
|
|
|
+ $license = $count > 1 ? '('.implode(' OR ', $license).')' : (string) reset($license);
|
|
}
|
|
}
|
|
|
|
|
|
if (!is_string($license)) {
|
|
if (!is_string($license)) {
|
|
throw new \InvalidArgumentException(sprintf(
|
|
throw new \InvalidArgumentException(sprintf(
|
|
- 'Array or String expected, %s given.', gettype($license)
|
|
|
|
|
|
+ 'Array or String expected, %s given.',
|
|
|
|
+ gettype($license)
|
|
));
|
|
));
|
|
}
|
|
}
|
|
|
|
|
|
return $this->isValidLicenseString($license);
|
|
return $this->isValidLicenseString($license);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * @return array
|
|
|
|
+ */
|
|
|
|
+ private function loadLicenses()
|
|
|
|
+ {
|
|
|
|
+ if (is_array($this->licenses)) {
|
|
|
|
+ return $this->licenses;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $jsonFile = file_get_contents(__DIR__ . '/../../../res/spdx-licenses.json');
|
|
|
|
+ $this->licenses = json_decode($jsonFile, true);
|
|
|
|
+
|
|
|
|
+ return $this->licenses;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return array
|
|
|
|
+ */
|
|
|
|
+ private function loadExceptions()
|
|
|
|
+ {
|
|
|
|
+ if (is_array($this->exceptions)) {
|
|
|
|
+ return $this->exceptions;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $jsonFile = file_get_contents(__DIR__ . '/../../../res/spdx-exceptions.json');
|
|
|
|
+ $this->exceptions = json_decode($jsonFile, true);
|
|
|
|
+
|
|
|
|
+ return $this->exceptions;
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* @param string $license
|
|
* @param string $license
|
|
*
|
|
*
|
|
@@ -141,10 +199,11 @@ class SpdxLicense
|
|
$tokens = array(
|
|
$tokens = array(
|
|
'po' => '\(',
|
|
'po' => '\(',
|
|
'pc' => '\)',
|
|
'pc' => '\)',
|
|
- 'op' => '(?:or|and)',
|
|
|
|
|
|
+ 'op' => '(?:or|OR|and|AND)',
|
|
|
|
+ 'wi' => '(?:with|WITH)',
|
|
'lix' => '(?:NONE|NOASSERTION)',
|
|
'lix' => '(?:NONE|NOASSERTION)',
|
|
'lir' => 'LicenseRef-\d+',
|
|
'lir' => 'LicenseRef-\d+',
|
|
- 'lic' => '[-+_.a-zA-Z0-9]{3,}',
|
|
|
|
|
|
+ 'lic' => '[-_.a-zA-Z0-9]{3,}\+?',
|
|
'ws' => '\s+',
|
|
'ws' => '\s+',
|
|
'_' => '.',
|
|
'_' => '.',
|
|
);
|
|
);
|
|
@@ -171,44 +230,58 @@ class SpdxLicense
|
|
return array($name, $matches[0][0]);
|
|
return array($name, $matches[0][0]);
|
|
}
|
|
}
|
|
|
|
|
|
- throw new \RuntimeException('At least the last pattern needs to match, but it did not (dot-match-all is missing?).');
|
|
|
|
|
|
+ throw new \RuntimeException(
|
|
|
|
+ 'At least the last pattern needs to match, but it did not (dot-match-all is missing?).'
|
|
|
|
+ );
|
|
};
|
|
};
|
|
|
|
|
|
$open = 0;
|
|
$open = 0;
|
|
- $require = 1;
|
|
|
|
|
|
+ $with = false;
|
|
|
|
+ $require = true;
|
|
$lastop = null;
|
|
$lastop = null;
|
|
|
|
|
|
while (list($token, $string) = $next()) {
|
|
while (list($token, $string) = $next()) {
|
|
switch ($token) {
|
|
switch ($token) {
|
|
case 'po':
|
|
case 'po':
|
|
- if ($open || !$require) {
|
|
|
|
|
|
+ if ($open || !$require || $with) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
$open = 1;
|
|
$open = 1;
|
|
break;
|
|
break;
|
|
case 'pc':
|
|
case 'pc':
|
|
- if ($open !== 1 || $require || !$lastop) {
|
|
|
|
|
|
+ if ($open !== 1 || $require || !$lastop || $with) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
$open = 2;
|
|
$open = 2;
|
|
break;
|
|
break;
|
|
case 'op':
|
|
case 'op':
|
|
- if ($require || !$open) {
|
|
|
|
|
|
+ if ($require || !$open || $with) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
$lastop || $lastop = $string;
|
|
$lastop || $lastop = $string;
|
|
if ($lastop !== $string) {
|
|
if ($lastop !== $string) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
- $require = 1;
|
|
|
|
|
|
+ $require = true;
|
|
|
|
+ break;
|
|
|
|
+ case 'wi':
|
|
|
|
+ $with = true;
|
|
break;
|
|
break;
|
|
case 'lix':
|
|
case 'lix':
|
|
- if ($open) {
|
|
|
|
|
|
+ if ($open || $with) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
goto lir;
|
|
goto lir;
|
|
case 'lic':
|
|
case 'lic':
|
|
- if (!$this->isValidLicenseIdentifier($string)) {
|
|
|
|
|
|
+ if ($with && $this->isValidExceptionIdentifier($string)) {
|
|
|
|
+ $require = true;
|
|
|
|
+ $with = false;
|
|
|
|
+ goto lir;
|
|
|
|
+ }
|
|
|
|
+ if ($with) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ if (!$this->isValidLicenseIdentifier(rtrim($string, '+'))) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
// Fall-through intended
|
|
// Fall-through intended
|
|
@@ -217,7 +290,7 @@ class SpdxLicense
|
|
if (!$require) {
|
|
if (!$require) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
- $require = 0;
|
|
|
|
|
|
+ $require = false;
|
|
break;
|
|
break;
|
|
case 'ws':
|
|
case 'ws':
|
|
break;
|
|
break;
|
|
@@ -228,6 +301,6 @@ class SpdxLicense
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- return !($open % 2 || $require);
|
|
|
|
|
|
+ return !($open % 2 || $require || $with);
|
|
}
|
|
}
|
|
}
|
|
}
|