• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wol-soft / php-json-schema-model-generator / 4564420247

pending completion
4564420247

Pull #68

github

GitHub
Merge 3a8dbf1f2 into 1294d211a
Pull Request #68: Fix recursive reference resolving

288 of 288 new or added lines in 39 files covered. (100.0%)

2777 of 2813 relevant lines covered (98.72%)

526.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.65
/src/PropertyProcessor/Property/MultiTypeProcessor.php
1
<?php
2

3
declare(strict_types = 1);
4

5
namespace PHPModelGenerator\PropertyProcessor\Property;
6

7
use PHPModelGenerator\Exception\SchemaException;
8
use PHPModelGenerator\Model\Property\PropertyInterface;
9
use PHPModelGenerator\Model\Schema;
10
use PHPModelGenerator\Model\SchemaDefinition\JsonSchema;
11
use PHPModelGenerator\Model\Validator\MultiTypeCheckValidator;
12
use PHPModelGenerator\Model\Validator\TypeCheckInterface;
13
use PHPModelGenerator\PropertyProcessor\Decorator\Property\PropertyTransferDecorator;
14
use PHPModelGenerator\PropertyProcessor\Decorator\TypeHint\TypeHintDecorator;
15
use PHPModelGenerator\PropertyProcessor\PropertyMetaDataCollection;
16
use PHPModelGenerator\PropertyProcessor\PropertyProcessorFactory;
17
use PHPModelGenerator\PropertyProcessor\PropertyProcessorInterface;
18
use PHPModelGenerator\SchemaProcessor\SchemaProcessor;
19
use ReflectionException;
20

21
/**
22
 * Class MultiTypePropertyProcessor
23
 *
24
 * @package PHPModelGenerator\PropertyProcessor\Property
25
 */
26
class MultiTypeProcessor extends AbstractValueProcessor
27
{
28
    /** @var PropertyProcessorInterface[] */
29
    protected $propertyProcessors = [];
30
    /** @var string[] */
31
    protected $allowedPropertyTypes = [];
32
    /** @var string[] */
33
    protected $checks = [];
34

35
    /**
36
     * MultiTypePropertyProcessor constructor.
37
     *
38
     * @param PropertyProcessorFactory   $propertyProcessorFactory
39
     * @param array                      $types
40
     * @param PropertyMetaDataCollection $propertyMetaDataCollection
41
     * @param SchemaProcessor            $schemaProcessor
42
     * @param Schema                     $schema
43
     *
44
     * @throws SchemaException
45
     */
46
    public function __construct(
47
        PropertyProcessorFactory $propertyProcessorFactory,
48
        array $types,
49
        PropertyMetaDataCollection $propertyMetaDataCollection,
50
        SchemaProcessor $schemaProcessor,
51
        Schema $schema
52
    ) {
53
        parent::__construct($propertyMetaDataCollection, $schemaProcessor, $schema);
63✔
54

55
        foreach ($types as $type) {
63✔
56
            $this->propertyProcessors[$type] = $propertyProcessorFactory->getProcessor(
63✔
57
                $type,
63✔
58
                $propertyMetaDataCollection,
63✔
59
                $schemaProcessor,
63✔
60
                $schema
63✔
61
            );
63✔
62
        }
63
    }
64

65
    /**
66
     * Process a property
67
     *
68
     * @param string $propertyName The name of the property
69
     * @param JsonSchema $propertySchema The schema of the property
70
     *
71
     * @return PropertyInterface
72
     *
73
     * @throws SchemaException
74
     * @throws ReflectionException
75
     */
76
    public function process(string $propertyName, JsonSchema $propertySchema): PropertyInterface
77
    {
78
        $property = parent::process($propertyName, $propertySchema);
63✔
79

80
        $property->onResolve(function () use ($property, $propertyName, $propertySchema): void {
62✔
81
            foreach ($property->getValidators() as $validator) {
62✔
82
                $this->checks[] = $validator->getValidator()->getCheck();
26✔
83
            }
84

85
            $subProperties = $this->processSubProperties($propertyName, $propertySchema, $property);
62✔
86

87
            $processedSubProperties = 0;
59✔
88
            foreach ($subProperties as $subProperty) {
59✔
89
                $subProperty->onResolve(function () use ($property, $subProperties, &$processedSubProperties) {
59✔
90
                    if (++$processedSubProperties === count($subProperties)) {
59✔
91
                        if (empty($this->allowedPropertyTypes)) {
59✔
92
                            return;
×
93
                        }
94

95
                        $property->addTypeHintDecorator(
59✔
96
                            new TypeHintDecorator(
59✔
97
                                array_map(
59✔
98
                                    static function (PropertyInterface $subProperty): string {
59✔
99
                                        return $subProperty->getTypeHint();
59✔
100
                                    },
59✔
101
                                    $subProperties
59✔
102
                                )
59✔
103
                            )
59✔
104
                        );
59✔
105

106
                        $property->addValidator(
59✔
107
                            new MultiTypeCheckValidator(
59✔
108
                                array_unique($this->allowedPropertyTypes),
59✔
109
                                $property,
59✔
110
                                $this->isImplicitNullAllowed($property)
59✔
111
                            ),
59✔
112
                            2
59✔
113
                        );
59✔
114
                    }
115
                });
59✔
116
            }
117
        });
62✔
118

119
        return $property;
59✔
120
    }
121

122
    /**
123
     * Move validators from the $source property to the $destination property
124
     *
125
     * @param PropertyInterface $source
126
     * @param PropertyInterface $destination
127
     */
128
    protected function transferValidators(PropertyInterface $source, PropertyInterface $destination)
129
    {
130
        foreach ($source->getValidators() as $validatorContainer) {
62✔
131
            $validator = $validatorContainer->getValidator();
62✔
132

133
            // filter out type checks to create a single type check which covers all allowed types
134
            if ($validator instanceof TypeCheckInterface) {
62✔
135
                array_push($this->allowedPropertyTypes, ...$validator->getTypes());
62✔
136

137
                continue;
62✔
138
            }
139

140
            // remove duplicated checks like an isset check
141
            if (in_array($validator->getCheck(), $this->checks)) {
57✔
142
                continue;
26✔
143
            }
144

145
            $destination->addValidator($validator, $validatorContainer->getPriority());
35✔
146
            $this->checks[] = $validator->getCheck();
35✔
147
        }
148
    }
149

150
    /**
151
     * @param string            $propertyName
152
     * @param JsonSchema        $propertySchema
153
     * @param PropertyInterface $property
154
     *
155
     * @return PropertyInterface[]
156
     *
157
     * @throws SchemaException
158
     */
159
    protected function processSubProperties(
160
        string $propertyName,
161
        JsonSchema $propertySchema,
162
        PropertyInterface $property
163
    ): array {
164
        $defaultValue = null;
62✔
165
        $invalidDefaultValueException = null;
62✔
166
        $invalidDefaultValues = 0;
62✔
167
        $subProperties = [];
62✔
168
        $json = $propertySchema->getJson();
62✔
169

170
        if (isset($json['default'])) {
62✔
171
            $defaultValue = $json['default'];
5✔
172
            unset($json['default']);
5✔
173
        }
174

175
        foreach ($this->propertyProcessors as $type => $propertyProcessor) {
62✔
176
            $json['type'] = $type;
62✔
177

178
            $subProperty = $propertyProcessor->process($propertyName, $propertySchema->withJson($json));
62✔
179

180
            $subProperty->onResolve(function () use ($property, $subProperty): void {
62✔
181
                $this->transferValidators($subProperty, $property);
62✔
182

183
                if ($subProperty->getDecorators()) {
62✔
184
                    $property->addDecorator(new PropertyTransferDecorator($subProperty));
32✔
185
                }
186
            });
62✔
187

188
            if ($defaultValue !== null && $propertyProcessor instanceof AbstractTypedValueProcessor) {
62✔
189
                try {
190
                    $propertyProcessor->setDefaultValue($property, $defaultValue, $propertySchema);
5✔
191
                } catch (SchemaException $e) {
5✔
192
                    $invalidDefaultValues++;
5✔
193
                    $invalidDefaultValueException = $e;
5✔
194
                }
195
            }
196

197
            $subProperties[] = $subProperty;
62✔
198
        }
199

200
        if ($invalidDefaultValues === count($this->propertyProcessors)) {
61✔
201
            throw $invalidDefaultValueException;
2✔
202
        }
203

204
        return $subProperties;
59✔
205
    }
206
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc