Skip to content

Commit e888b67

Browse files
authored
rework serializers (#155)
* `SerializerInterface` interface * BREAKING: renamed to `Serializer` * BREAKING: Method `serialize()` got a new optional parameter `$prettyPrint` * `BaseSerializer` abstract class * BREAKING: complete redesign * `{Json,Xml}Serializer` class * BREAKING: complete redesign Signed-off-by: Jan Kowalleck <[email protected]>
1 parent ceabb08 commit e888b67

File tree

16 files changed

+462
-385
lines changed

16 files changed

+462
-385
lines changed

.gitattributes

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ composer.lock text eol=lf diff=json
3434
/examples export-ignore
3535
/docs/dev export-ignore
3636

37-
# files that are forced to be exported in "dist" releaes
38-
/phpdoc.dist.xml -export-ignore
37+
# files that are forced to be exported in "dist" release
38+
/README.md -export-ignore
3939
/NOTICE -export-ignore
4040
/LICENSE -export-ignore
4141
/semver.txt -export-ignore
42+
/phpdoc.dist.xml -export-ignore

HISTORY.md

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,20 @@ API changes
8181
* `\CycloneDX\Core\Serialize` namespace
8282
* Overall
8383
* BREAKING: renamed namespace to `Serialization` ([#5] via [#146])
84-
* `BaseSerializer` class
85-
* BREAKING: removed deprecated method `setSpec()` (via [#144])
86-
* `BaseValidator` class
87-
* BREAKING: removed deprecated method `setSpec()` (via [#144])
84+
* `SerializerInterface` interface
85+
* BREAKING: renamed to `Serializer` ([#133] via [#155])
86+
* BREAKING: existing method `serialize()` got a new optional parameter `$prettyPrint` (via [#155])
87+
* `BaseSerializer` abstract class
88+
* BREAKING: complete redesign (via [#155])
89+
* `{Json,Xml}Serializer` class
90+
* BREAKING: complete redesign (via [#155])
8891
* `{DOM,JSON}\NormalizerFactory` classes
8992
* BREAKING: removed method `makeForLicenseExpression()` (via [#131])
9093
* BREAKING: removed method `makeForDisjunctiveLicense()` (via [#131])
9194
* BREAKING: removed method `makeForDisjunctiveLicenseRepository()` (via [#131])
9295
* BREAKING: removed method `makeForHashRepositonary()` - use `makeForHashDictionary()` instead ([#133] via [#131])
9396
* BREAKING: removed method `setSpec()` (via [#131])
94-
* Added new method `makeForHashDictionary()` ([#133] via [#131])
97+
* Added method `makeForHashDictionary()` ([#133] via [#131])
9598
* Added method `makeForLicense()` (via [#131])
9699
* Added method `makeForLicenseRepository()` (via [#131])
97100
* `{DOM,JSON}\Normalizers` namespaces
@@ -107,6 +110,8 @@ API changes
107110
This is considered a non-breaking change, because the behaviour was already documented in the API, even though there was no need for an implementation before.
108111
* `ExternalReferenceNormalizer` classes
109112
* Changed, so that it tries to convert unsupported types to "other", before it throws an `\DomainException` ([#137] via [#147])
113+
* `JSON\Normalizers\BomNormalizer` class
114+
* Changed: method `normalize`'s result data may contain the `$schema` string (via [#155])
110115
* `JSON\Normalizers\ExternalReferenceNormalizer` class
111116
* BREAKING: method `normalize` may throw `\UnexpectedValueException` when the url is invalid to format "ini-reference" (via [#151])
112117
* `\CycloneDX\Core\Spdx` namespace
@@ -115,16 +120,17 @@ API changes
115120
* BREAKING: completely reworked everything ([#139] via [#142])
116121
See the code base for references
117122
* `\CycloneDX\Core\Validation` namespace
118-
* `XmlValidator` classes
119-
* Added support for CycloneDX v1.4 ([#57] via [#65])
120-
* `JsonValidator` classes
123+
* `BaseValidator` class
124+
* BREAKING: removed deprecated method `setSpec()` (via [#144])
125+
* `ValidatorInterface` interface
126+
* BREAKING: renamed interface to `Validator` ([#133] via [#143])
127+
* `Validators\{Json,Xml}Validator` classes
121128
* Added support for CycloneDX v1.4 ([#57] via [#65])
129+
* `Validators\JsonValidator` classes
122130
* Utilizes a much more competent validation library than before ([#80] via [#151])
123-
* `JsonStrictValidator` class
131+
* `Validators\JsonStrictValidator` class
124132
* Added support for CycloneDX v1.4 ([#57] via [#65])
125133
* Utilizes a much more competent validation library than before ([#80] via [#151])
126-
* `ValidatorInterface` interface
127-
* BREAKING: renamed interface to `Validator` ([#133] via [#143])
128134

129135
[#5]: https://github.com/CycloneDX/cyclonedx-php-library/issues/5
130136
[#6]: https://github.com/CycloneDX/cyclonedx-php-library/issues/6
@@ -148,6 +154,7 @@ API changes
148154
[#146]: https://github.com/CycloneDX/cyclonedx-php-library/pull/146
149155
[#149]: https://github.com/CycloneDX/cyclonedx-php-library/pull/149
150156
[#151]: https://github.com/CycloneDX/cyclonedx-php-library/pull/151
157+
[#155]: https://github.com/CycloneDX/cyclonedx-php-library/pull/155
151158

152159
## 1.6.3 - 2022-09-15
153160

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ $bom->getComponents()->addItems(
9090
);
9191
```
9292

93+
## API documentation
94+
95+
There is no pre-rendered documentation at the time.
96+
Instead, there is a prepared config for [phpDoc3](https://docs.phpdoc.org/guide/getting-started/index.html)
97+
that you can use to generate the docs for yourself.
98+
9399
## Conflicts
94100

95101
Due to the fact that this library was split out of [`/src/Core` of cyclonedx-php-composer (346e6200fb2f5086061b15c2ee44f540893ce97d)](https://github.com/CycloneDX/cyclonedx-php-composer/tree/346e6200fb2f5086061b15c2ee44f540893ce97d/src/Core)

examples/build_and_serialize.php

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,31 @@
2626
// Example how to serialize a Bom to JSON / XML.
2727

2828
$bom = new \CycloneDX\Core\Models\Bom();
29-
$bom->getComponents()->addItems(
30-
new \CycloneDX\Core\Models\Component(
31-
\CycloneDX\Core\Enums\Classification::LIBRARY,
32-
'myComponent'
29+
$bom->getMetadata()->setComponent(
30+
$rootComponent = new \CycloneDX\Core\Models\Component(
31+
\CycloneDX\Core\Enums\Classification::APPLICATION,
32+
'myApp'
3333
)
3434
);
35+
$component = new \CycloneDX\Core\Models\Component(
36+
\CycloneDX\Core\Enums\Classification::LIBRARY,
37+
'myComponent'
38+
);
39+
$bom->getComponents()->addItems($component);
40+
$rootComponent->getDependencies()->addItems($component->getBomRef());
3541

3642
$spec = \CycloneDX\Core\Spec\SpecFactory::make1dot4();
3743

38-
$jsonSerializer = new \CycloneDX\Core\Serialization\JsonSerializer($spec);
39-
$serializedJSON = $jsonSerializer->serialize($bom);
44+
$prettyPrint = false;
45+
46+
$jsonSerializer = new \CycloneDX\Core\Serialization\JsonSerializer(
47+
new \CycloneDX\Core\Serialization\JSON\NormalizerFactory($spec)
48+
);
49+
$serializedJSON = $jsonSerializer->serialize($bom, $prettyPrint);
4050
echo $serializedJSON, \PHP_EOL;
4151

42-
$xmlSerializer = new \CycloneDX\Core\Serialization\XmlSerializer($spec);
43-
$serializedXML = $xmlSerializer->serialize($bom);
52+
$xmlSerializer = new \CycloneDX\Core\Serialization\XmlSerializer(
53+
new \CycloneDX\Core\Serialization\DOM\NormalizerFactory($spec)
54+
);
55+
$serializedXML = $xmlSerializer->serialize($bom, $prettyPrint);
4456
echo $serializedXML, \PHP_EOL;

phpdoc.dist.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
<path>src</path>
1616
</source>
1717
<default-package-name>CycloneDX Library</default-package-name>
18-
<include-source>false</include-source>
18+
<ignore-tags>
19+
<ignore-tag>SuppressWarnings</ignore-tag>
20+
<ignore-tag>psalm-suppress</ignore-tag>
21+
</ignore-tags>
1922
</api>
2023
</version>
2124
</phpdocumentor>

src/Core/Serialization/BaseSerializer.php

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -25,70 +25,92 @@
2525

2626
use CycloneDX\Core\Models\Bom;
2727
use CycloneDX\Core\Models\BomRef;
28-
use CycloneDX\Core\Spec\Spec;
28+
use Exception;
2929

3030
/**
31-
* @author jkowalleck
31+
* @template TNormalizedBom
3232
*/
33-
abstract class BaseSerializer implements SerializerInterface
33+
abstract class BaseSerializer implements Serializer
3434
{
3535
/**
36-
* @readonly
36+
* Get a list of all {@see \CycloneDX\Core\Models\BomRef} in {@see \CycloneDX\Core\Models\Bom}.
37+
* The list might contain duplicates.
38+
*
39+
* @return BomRef[]
40+
*
41+
* @psalm-return list<BomRef>
3742
*/
38-
private Spec $spec;
39-
40-
public function __construct(Spec $spec)
43+
private function getAllBomRefs(Bom $bom): array
4144
{
42-
$this->spec = $spec;
43-
}
45+
$allBomRefs = [];
46+
$allComponents = $bom->getComponents()->getItems();
47+
$metadataComponent = $bom->getMetadata()->getComponent();
48+
if (null !== $metadataComponent) {
49+
$allComponents[] = $metadataComponent;
50+
}
51+
foreach ($allComponents as $component) {
52+
$allBomRefs[] = $component->getBomRef();
53+
array_push($allBomRefs, ...$component->getDependencies()->getItems());
54+
}
4455

45-
public function getSpec(): Spec
46-
{
47-
return $this->spec;
56+
return $allBomRefs;
4857
}
4958

5059
/**
51-
* {@inheritdoc}
60+
* Normalize for serialization.
61+
*
62+
* Also utilizes {@see \CycloneDX\Core\Serialization\BomRefDiscriminator}
63+
* to guarantee that each BomRef has a unique value.
64+
*
65+
* @throws Exception
66+
*
67+
* @return TNormalizedBom a version of the Bom that was normalized for serialization
5268
*/
53-
public function serialize(Bom $bom): string
69+
private function normalize(BOM $bom)
5470
{
5571
$bomRefDiscriminator = new BomRefDiscriminator(...$this->getAllBomRefs($bom));
5672
$bomRefDiscriminator->discriminate();
73+
// This IS NOT the place to put meaning to the BomRef values. This would be out of scope.
74+
// This IS the place to make BomRef values (temporary) unique in their own document scope.
5775
try {
58-
return $this->normalize($bom);
76+
return $this->realNormalize($bom);
5977
} finally {
6078
$bomRefDiscriminator->reset();
6179
}
6280
}
6381

6482
/**
65-
* Normalize the Bom to a string.
66-
*
67-
* May throw implementation-dependent Exceptions.
83+
* {@inheritDoc}
6884
*
69-
* @psalm-return non-empty-string
85+
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
7086
*/
71-
abstract protected function normalize(Bom $bom): string;
87+
final public function serialize(Bom $bom, ?bool $prettyPrint = null): string
88+
{
89+
return $this->realSerialize(
90+
$this->normalize($bom),
91+
$prettyPrint
92+
);
93+
}
7294

7395
/**
74-
* @return BomRef[]
96+
* Normalize a {@see \CycloneDX\Core\Models\Bom} to the data structure that {@see realSerialize()} can handle.
97+
*
98+
* @throws Exception
99+
*
100+
* @return TNormalizedBom a version of the Bom that was normalized for serialization
75101
*/
76-
private function getAllBomRefs(Bom $bom): array
77-
{
78-
$allBomRefs = [];
79-
80-
$allComponents = $bom->getComponents()->getItems();
81-
82-
$metadataComponent = $bom->getMetadata()->getComponent();
83-
if (null !== $metadataComponent) {
84-
$allComponents[] = $metadataComponent;
85-
}
102+
abstract protected function realNormalize(Bom $bom);
103+
// no typehint for return type, as it is not actually `mixed` but a templated type.
86104

87-
foreach ($allComponents as $component) {
88-
$allBomRefs[] = $component->getBomRef();
89-
array_push($allBomRefs, ...$component->getDependencies()->getItems());
90-
}
91-
92-
return $allBomRefs;
93-
}
105+
/**
106+
* Serialize a {@see realNormalize() normalized} version of a {@see \CycloneDX\Core\Models\Bom}.
107+
*
108+
* @param TNormalizedBom $normalizedBom a version of the Bom that was normalized for serialization
109+
*
110+
* @throws Exception
111+
*
112+
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
113+
*/
114+
abstract protected function realSerialize($normalizedBom, ?bool $prettyPrint): string;
115+
// no typehint for `$normalizedBom` parameter, as it is not actually `mixed` but a templated type.
94116
}

src/Core/Serialization/JSON/Normalizers/BomNormalizer.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use CycloneDX\Core\Models\Bom;
2828
use CycloneDX\Core\Models\Metadata;
2929
use CycloneDX\Core\Serialization\JSON\_BaseNormalizer;
30+
use CycloneDX\Core\Spec\Version;
3031

3132
/**
3233
* @author jkowalleck
@@ -37,12 +38,20 @@ class BomNormalizer extends _BaseNormalizer
3738

3839
private const BOM_FORMAT = 'CycloneDX';
3940

41+
private const SCHEMA = [
42+
Version::v1dot1 => null,
43+
Version::v1dot2 => 'http://cyclonedx.org/schema/bom-1.2b.schema.json',
44+
Version::v1dot3 => 'http://cyclonedx.org/schema/bom-1.3a.schema.json',
45+
Version::v1dot4 => 'http://cyclonedx.org/schema/bom-1.4.schema.json',
46+
];
47+
4048
public function normalize(Bom $bom): array
4149
{
4250
$factory = $this->getNormalizerFactory();
4351

4452
return array_filter(
4553
[
54+
'$schema' => self::SCHEMA[$factory->getSpec()->getVersion()] ?? null,
4655
'bomFormat' => self::BOM_FORMAT,
4756
'specVersion' => $factory->getSpec()->getVersion(),
4857
'version' => $bom->getVersion(),

0 commit comments

Comments
 (0)