Completed
Push — main ( 0c282f...ff8fe1 )
by Niels
23s queued 13s
created

getAttributeContextFromSchemaObject()   B

Complexity

Conditions 8
Paths 5

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 8
eloc 11
c 2
b 0
f 0
nc 5
nop 2
dl 0
loc 20
rs 8.4444
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the OpenapiBundle package.
7
 *
8
 * (c) Niels Nijens <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Nijens\OpenapiBundle\Serialization;
15
16
use Nijens\OpenapiBundle\Json\JsonPointer;
17
use Nijens\OpenapiBundle\Json\Reference;
18
use Nijens\OpenapiBundle\Json\SchemaLoaderInterface;
19
use stdClass;
20
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
21
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
22
use Symfony\Component\Serializer\SerializerInterface;
23
24
/**
25
 * Creates a serialization context for {@see SerializerInterface::serialize} based on the provided schema object name.
26
 *
27
 * @experimental
28
 *
29
 * @author Niels Nijens <[email protected]>
30
 */
31
class SerializationContextBuilder implements SerializationContextBuilderInterface
32
{
33
    /**
34
     * @var SchemaLoaderInterface
35
     */
36
    private $schemaLoader;
37
38
    public function __construct(SchemaLoaderInterface $schemaLoader)
39
    {
40
        $this->schemaLoader = $schemaLoader;
41
    }
42
43
    public function getContextForSchemaObject(string $schemaObjectName, string $openApiSpecificationFile, bool $treatAnyOfAndOneOfAsAllOf = false): array
44
    {
45
        $jsonPointer = new JsonPointer($this->schemaLoader->load($openApiSpecificationFile));
46
        $schemaObject = $jsonPointer->get(sprintf('/components/schemas/%s', $schemaObjectName));
47
48
        return [
49
            AbstractObjectNormalizer::SKIP_NULL_VALUES => true,
50
            AbstractNormalizer::ATTRIBUTES => $this->getAttributeContextFromSchemaObject($schemaObject, $treatAnyOfAndOneOfAsAllOf),
51
        ];
52
    }
53
54
    /**
55
     * @param stdClass|Reference $schemaObject
56
     */
57
    private function getAttributeContextFromSchemaObject($schemaObject, bool $treatAnyOfAndOneOfAsAllOf): array
58
    {
59
        $schemaObject = $this->dereference($schemaObject);
60
61
        if (isset($schemaObject->allOf) || ($treatAnyOfAndOneOfAsAllOf && (isset($schemaObject->anyOf) || isset($schemaObject->oneOf)))) {
62
            return $this->getAttributeContextFromCombinedSchemaObject($schemaObject, $treatAnyOfAndOneOfAsAllOf);
63
        }
64
65
        if (isset($schemaObject->type) === false) {
66
            return [];
67
        }
68
69
        switch ($schemaObject->type) {
70
            case 'object':
71
                return $this->getAttributeContextFromSchemaObjectProperties($schemaObject, $treatAnyOfAndOneOfAsAllOf);
72
            case 'array':
73
                return $this->getAttributeContextFromSchemaObject($schemaObject->items, $treatAnyOfAndOneOfAsAllOf);
74
        }
75
76
        return [];
77
    }
78
79
    private function getAttributeContextFromCombinedSchemaObject(stdClass $schemaObject, bool $treatAnyOfAndOneOfAsAllOf): array
80
    {
81
        $context = [];
82
        $allOfSchemaObjects = $schemaObject->allOf ?? [];
83
        if ($treatAnyOfAndOneOfAsAllOf) {
84
            $allOfSchemaObjects = array_merge($allOfSchemaObjects, $schemaObject->anyOf ?? [], $schemaObject->oneOf ?? []);
85
        }
86
87
        foreach ($allOfSchemaObjects as $allOfSchemaObject) {
88
            $context = array_merge($context, $this->getAttributeContextFromSchemaObject($allOfSchemaObject, $treatAnyOfAndOneOfAsAllOf));
89
        }
90
91
        return $context;
92
    }
93
94
    private function getAttributeContextFromSchemaObjectProperties(stdClass $schemaObject, bool $treatAnyOfAndOneOfAsAllOf): array
95
    {
96
        $objectContext = [];
97
        $properties = $schemaObject->properties ?? [];
98
        foreach ($properties as $propertyKey => $property) {
99
            $propertyContext = $this->getAttributeContextFromSchemaObject($property, $treatAnyOfAndOneOfAsAllOf);
100
101
            if ($this->isType($property, 'object') || count($propertyContext) > 0) {
102
                $objectContext[$propertyKey] = $propertyContext;
103
104
                continue;
105
            }
106
107
            $objectContext[] = $propertyKey;
108
        }
109
110
        if (isset($schemaObject->additionalProperties) && $schemaObject->additionalProperties !== false) {
111
            $objectContext = array_merge(
112
                $objectContext,
113
                $this->getAttributeContextFromSchemaObject($schemaObject->additionalProperties, $treatAnyOfAndOneOfAsAllOf)
114
            );
115
        }
116
117
        return $objectContext;
118
    }
119
120
    /**
121
     * @param stdClass|Reference $schemaObject
122
     */
123
    private function dereference($schemaObject): stdClass
124
    {
125
        if ($schemaObject instanceof Reference === false) {
126
            return $schemaObject;
127
        }
128
129
        $jsonPointer = new JsonPointer($schemaObject->getJsonSchema());
130
131
        return $jsonPointer->get($schemaObject->getPointer());
132
    }
133
134
    /**
135
     * @param stdClass|Reference $schemaObject
136
     */
137
    private function isType($schemaObject, string $type): bool
138
    {
139
        $schemaObject = $this->dereference($schemaObject);
140
        if (isset($schemaObject->allOf)) {
141
            foreach ($schemaObject->allOf as $allOfSchemaObject) {
142
                if ($this->isType($allOfSchemaObject, $type)) {
143
                    return true;
144
                }
145
            }
146
147
            return false;
148
        }
149
150
        return isset($schemaObject->type) && $schemaObject->type === $type;
151
    }
152
}
153