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

DoclerLabs / api-client-generator / 21433000758

28 Jan 2026 09:39AM UTC coverage: 86.69% (-0.3%) from 86.973%
21433000758

Pull #131

github

web-flow
Bump phpunit/phpunit from 9.5.9 to 9.6.33

Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 9.5.9 to 9.6.33.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/9.6.33/ChangeLog-9.6.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/9.5.9...9.6.33)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-version: 9.6.33
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #131: Bump phpunit/phpunit from 9.5.9 to 9.6.33

3465 of 3997 relevant lines covered (86.69%)

7.11 hits per line

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

70.99
/src/Input/Factory/FieldFactory.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace DoclerLabs\ApiClientGenerator\Input\Factory;
6

7
use cebe\openapi\spec\Parameter;
8
use cebe\openapi\spec\Reference;
9
use cebe\openapi\SpecObjectInterface;
10
use DoclerLabs\ApiClientGenerator\Ast\PhpVersion;
11
use DoclerLabs\ApiClientGenerator\Entity\Constraint\ConstraintCollection;
12
use DoclerLabs\ApiClientGenerator\Entity\Constraint\MaximumConstraint;
13
use DoclerLabs\ApiClientGenerator\Entity\Constraint\MaxItemsConstraint;
14
use DoclerLabs\ApiClientGenerator\Entity\Constraint\MaxLengthConstraint;
15
use DoclerLabs\ApiClientGenerator\Entity\Constraint\MinimumConstraint;
16
use DoclerLabs\ApiClientGenerator\Entity\Constraint\MinItemsConstraint;
17
use DoclerLabs\ApiClientGenerator\Entity\Constraint\MinLengthConstraint;
18
use DoclerLabs\ApiClientGenerator\Entity\Constraint\PatternConstraint;
19
use DoclerLabs\ApiClientGenerator\Entity\Field;
20
use DoclerLabs\ApiClientGenerator\Entity\FieldType;
21
use DoclerLabs\ApiClientGenerator\Input\InvalidSpecificationException;
22
use DoclerLabs\ApiClientGenerator\Input\PhpNameValidator;
23
use DoclerLabs\ApiClientGenerator\Naming\CaseCaster;
24
use DoclerLabs\ApiClientGenerator\Naming\SchemaNaming;
25
use Throwable;
26
use UnexpectedValueException;
27

28
class FieldFactory
29
{
30
    public function __construct(private PhpNameValidator $nameValidator, private PhpVersion $phpVersion)
31
    {
32
    }
3✔
33

34
    public function create(
35
        string $operationName,
36
        string $fieldName,
37
        SpecObjectInterface $schemaOrReference,
38
        bool $required,
39
        string $referenceName = '',
40
        string $parentReferenceName = ''
41
    ): Field {
42
        try {
43
            $arrayItem            = null;
3✔
44
            $objectProperties     = [];
3✔
45
            $oneOf                = [];
3✔
46
            $anyOf                = [];
3✔
47
            $additionalProperties = true;
3✔
48
            $schemaReference      = $schemaOrReference;
3✔
49
            $schema               = $this->resolveReference($schemaOrReference);
3✔
50
            $nullable             = (bool)$schema->nullable;
3✔
51

52
            if ($schemaOrReference instanceof Reference) {
3✔
53
                $referenceName = SchemaNaming::getClassName($schemaOrReference);
3✔
54
            }
55

56
            if ($referenceName !== '' && !$this->nameValidator->isValidClassName($referenceName)) {
3✔
57
                throw new InvalidSpecificationException('Invalid field reference name: ' . $referenceName);
×
58
            }
59

60
            if ($fieldName !== '' && !$this->nameValidator->isValidVariableName(CaseCaster::toCamel($fieldName))) {
3✔
61
                throw new InvalidSpecificationException('Invalid field name: ' . $fieldName);
×
62
            }
63

64
            $type = $schema->type;
3✔
65
            if (
66
                is_array($type)
3✔
67
                && count($type) === 2
3✔
68
                && in_array('null', $type, true)
3✔
69
            ) {
70
                // 3.1 schema nullable does not exist anymore, rather it's done via type
71
                $nullable = true;
1✔
72
                $type     = array_filter($type, static fn ($type) => $type !== 'null')[0];
1✔
73
            }
74

75
            if (
76
                // if 3.1 and still array, we fallback to mixed type
77
                is_array($type)
3✔
78
                // same goes for oneOf and anyOf without references
79
                || (
80
                    isset($schema->oneOf)
3✔
81
                    && array_filter($schema->oneOf, static fn ($schemaOption) => !($schemaOption instanceof Reference)) !== []
3✔
82
                )
83
                || (
84
                    isset($schema->anyOf)
3✔
85
                    && array_filter($schema->anyOf, static fn ($schemaOption) => !($schemaOption instanceof Reference)) !== []
3✔
86
                )
87
            ) {
88
                $type = null;
×
89
            } elseif (isset($schema->oneOf)) {
3✔
90
                $type = FieldType::SPEC_TYPE_OBJECT;
×
91
                $this->processSchemaOptions($schema->oneOf, $operationName, $fieldName, $referenceName, $oneOf, $schema);
×
92
            } elseif (isset($schema->anyOf)) {
3✔
93
                $type = FieldType::SPEC_TYPE_OBJECT;
×
94
                $this->processSchemaOptions($schema->anyOf, $operationName, $fieldName, $referenceName, $anyOf, $schema);
×
95
            } elseif (isset($schema->allOf)) {
3✔
96
                $type = FieldType::SPEC_TYPE_OBJECT;
×
97
                if ($referenceName === '') {
×
98
                    $referenceName = SchemaNaming::getClassName($schemaReference, $fieldName);
×
99
                }
100

101
                $objectProperties = $this->mergeAllOfProperties($operationName, $schema);
×
102
                $schema           = $this->mergeAllOfAttributes($schema, $nullable);
×
103
            } elseif (FieldType::isSpecificationTypeArray($type)) {
3✔
104
                $itemReferenceName = '';
3✔
105
                if ($schema->items === null) {
3✔
106
                    throw new InvalidSpecificationException('Array field does not have items specified.');
×
107
                }
108
                $sibling        = $this->resolveReference($schema->items);
3✔
109
                $itemsReference = $schema->items;
3✔
110
                if (isset($sibling->allOf) || FieldType::isSpecificationTypeObject($sibling->type)) {
3✔
111
                    $itemReferenceName = SchemaNaming::getClassName(
3✔
112
                        $itemsReference,
3✔
113
                        $operationName . ucfirst($fieldName) . 'Item'
3✔
114
                    );
3✔
115
                } elseif ($itemsReference instanceof Reference) {
2✔
116
                    $itemReferenceName = SchemaNaming::getClassName($itemsReference);
×
117
                }
118
                $arrayItem = $this->create(
3✔
119
                    $operationName,
3✔
120
                    lcfirst($itemReferenceName),
3✔
121
                    $sibling,
3✔
122
                    true,
3✔
123
                    $itemReferenceName
3✔
124
                );
3✔
125
            } elseif (FieldType::isSpecificationTypeObject($type)) {
3✔
126
                if ($referenceName === '') {
3✔
127
                    $referenceName = SchemaNaming::getClassName(
1✔
128
                        $schemaReference,
1✔
129
                        sprintf('%s%s', ucfirst($parentReferenceName), ucfirst($fieldName))
1✔
130
                    );
1✔
131
                }
132

133
                $objectProperties = $this->mapProperties($operationName, $schema, $referenceName);
3✔
134
            } elseif (!empty($schema->enum)) {
3✔
135
                if ($referenceName === '') {
2✔
136
                    $referenceName = SchemaNaming::getClassName(
2✔
137
                        $schemaReference,
2✔
138
                        sprintf('%s%s', ucfirst($parentReferenceName), ucfirst($fieldName))
2✔
139
                    );
2✔
140
                }
141
            }
142
            if (isset($schema->additionalProperties)) {
3✔
143
                if (is_bool($schema->additionalProperties)) {
3✔
144
                    $additionalProperties = $schema->additionalProperties;
3✔
145
                } elseif (is_object($schema->additionalProperties)) {
2✔
146
                    $warningMessage = sprintf(
2✔
147
                        'Additional properties object is not supported: %s.',
2✔
148
                        $referenceName
2✔
149
                    );
2✔
150
                    trigger_error($warningMessage, E_USER_WARNING);
2✔
151
                }
152
            }
153

154
            $fieldType = new FieldType($type, $this->phpVersion);
3✔
155
            $field     = new Field(
3✔
156
                $this->phpVersion,
3✔
157
                $fieldName,
3✔
158
                $fieldType,
3✔
159
                new ConstraintCollection(
3✔
160
                    new MinimumConstraint($schema->minimum, $schema->exclusiveMinimum, $fieldType),
3✔
161
                    new MaximumConstraint($schema->maximum, $schema->exclusiveMaximum, $fieldType),
3✔
162
                    new MinLengthConstraint($schema->minLength),
3✔
163
                    new MaxLengthConstraint($schema->maxLength),
3✔
164
                    new PatternConstraint($schema->pattern),
3✔
165
                    new MinItemsConstraint($schema->minItems),
3✔
166
                    new MaxItemsConstraint($schema->maxItems)
3✔
167
                ),
3✔
168
                $referenceName,
3✔
169
                $required,
3✔
170
                $nullable,
3✔
171
                $additionalProperties,
3✔
172
                !empty($oneOf),
3✔
173
                !empty($anyOf)
3✔
174
            );
3✔
175

176
            if ($arrayItem !== null) {
3✔
177
                $field->setArrayItem($arrayItem);
3✔
178
            } elseif (!empty($objectProperties)) {
3✔
179
                $field->setObjectProperties($objectProperties);
3✔
180
            } elseif (isset($schema->enum)) {
3✔
181
                $field->setEnumValues($schema->enum);
2✔
182
            } elseif (!empty($oneOf)) {
3✔
183
                $field->setObjectProperties($oneOf);
×
184
            } elseif (!empty($anyOf)) {
3✔
185
                $field->setObjectProperties($anyOf);
×
186
            }
187

188
            if (isset($schema->format)) {
3✔
189
                $field->setFormat($schema->format);
3✔
190
            }
191

192
            if (isset($schema->default)) {
3✔
193
                $field->setDefault($schema->default);
2✔
194
            }
195

196
            if (isset($schema->discriminator)) {
3✔
197
                $field->setDiscriminator($schema->discriminator);
×
198
            }
199

200
            return $field;
3✔
201
        } catch (Throwable $exception) {
×
202
            throw new InvalidSpecificationException(
×
203
                sprintf(
×
204
                    'Error on mapping `%s`: %s',
×
205
                    $fieldName,
×
206
                    $exception->getMessage()
×
207
                )
×
208
            );
×
209
        }
210
    }
211

212
    protected function mapProperties(
213
        string $operationName,
214
        SpecObjectInterface $schema,
215
        string $schemaReferenceName
216
    ): array {
217
        $fields = [];
3✔
218
        foreach ($schema->properties as $childName => $child) {
3✔
219
            $required = false;
3✔
220
            if (isset($schema->required) && is_array($schema->required)) {
3✔
221
                $required = in_array($childName, $schema->required, true);
3✔
222
            }
223
            $fields[] = $this->create($operationName, $childName, $child, $required, '', $schemaReferenceName);
3✔
224
        }
225

226
        return $fields;
3✔
227
    }
228

229
    private function processSchemaOptions(
230
        array $schemaOptions,
231
        string $operationName,
232
        string $fieldName,
233
        string &$referenceName,
234
        array &$options,
235
        SpecObjectInterface $schemaReference
236
    ): void {
237
        if ($referenceName === '') {
×
238
            $referenceName = SchemaNaming::getClassName($schemaReference, $fieldName);
×
239
        }
240

241
        /** @var Reference $schemaOption */
242
        foreach ($schemaOptions as $schemaOption) {
×
243
            $explodedReference = explode('/', $schemaOption->getReference());
×
244
            $objectName        = $explodedReference[count($explodedReference) - 1];
×
245
            $options[]         = $this->create($operationName, $objectName, $this->resolveReference($schemaOption), false);
×
246
        }
247
    }
248

249
    private function resolveReference(SpecObjectInterface $schema): SpecObjectInterface
250
    {
251
        $resolved = $schema;
3✔
252
        if ($schema instanceof Reference) {
3✔
253
            $resolved = $schema->resolve();
3✔
254
        }
255

256
        if (is_array($resolved) || $resolved === null) {
3✔
257
            throw new UnexpectedValueException('Unexpected value after the reference resolution');
×
258
        }
259

260
        if ($resolved instanceof Parameter) {
3✔
261
            throw new InvalidSpecificationException('Usage of parameter inside of the schema is unexpected.');
×
262
        }
263

264
        return $resolved;
3✔
265
    }
266

267
    private function mergeAllOfProperties(string $operationName, SpecObjectInterface $schema): array
268
    {
269
        $allOfProperties = [];
×
270
        foreach ($schema->allOf as $allOfSchema) {
×
271
            if ($allOfSchema instanceof Reference) {
×
272
                $allOfSchema = $allOfSchema->resolve();
×
273
            }
274
            $allOfProperties[] = $this->mapProperties($operationName, $allOfSchema, '');
×
275
        }
276

277
        return array_merge([], ...$allOfProperties);
×
278
    }
279

280
    private function mergeAllOfAttributes(SpecObjectInterface $schema, bool &$nullable): SpecObjectInterface
281
    {
282
        foreach ($schema->allOf as $allOfSchema) {
×
283
            if ($allOfSchema instanceof Reference) {
×
284
                $allOfSchema = $allOfSchema->resolve();
×
285
            }
286

287
            if (in_array('null', (array)$allOfSchema->type, true)) {
×
288
                // 3.1 schema nullable does not exist anymore, rather it's done via type
289
                $nullable = true;
×
290
            } elseif (isset($allOfSchema->nullable)) {
×
291
                $nullable = $allOfSchema->nullable;
×
292
            }
293

294
            // @TODO add more attributes here later if needed
295
        }
296

297
        return $schema;
×
298
    }
299
}
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

© 2026 Coveralls, Inc