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

DoclerLabs / api-client-generator / 9254068657

27 May 2024 11:30AM UTC coverage: 86.981% (-1.4%) from 88.428%
9254068657

push

github

web-flow
Merge pull request #112 from DoclerLabs/php81

php 8.1 features

106 of 172 new or added lines in 20 files covered. (61.63%)

4 existing lines in 2 files now uncovered.

2913 of 3349 relevant lines covered (86.98%)

4.92 hits per line

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

66.93
/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
    }
2✔
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;
2✔
44
            $objectProperties     = [];
2✔
45
            $oneOf                = [];
2✔
46
            $anyOf                = [];
2✔
47
            $additionalProperties = true;
2✔
48
            $schemaReference      = $schemaOrReference;
2✔
49
            $schema               = $this->resolveReference($schemaOrReference);
2✔
50

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

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

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

63
            $type = $schema->type;
2✔
64
            if (isset($schema->oneOf)) {
2✔
65
                $type = FieldType::SPEC_TYPE_OBJECT;
×
66
                $this->processSchemaOptions($schema->oneOf, $operationName, $fieldName, $referenceName, $oneOf, $schema);
×
67
            } elseif (isset($schema->anyOf)) {
2✔
68
                $type = FieldType::SPEC_TYPE_OBJECT;
×
69
                $this->processSchemaOptions($schema->anyOf, $operationName, $fieldName, $referenceName, $anyOf, $schema);
×
70
            } elseif (isset($schema->allOf)) {
2✔
71
                $type = FieldType::SPEC_TYPE_OBJECT;
×
72
                if ($referenceName === '') {
×
73
                    $referenceName = SchemaNaming::getClassName($schemaReference, $fieldName);
×
74
                }
75

76
                $objectProperties = $this->mergeAllOfProperties($operationName, $schema);
×
77
                $schema           = $this->mergeAllOfAttributes($schema);
×
78
            } elseif (FieldType::isSpecificationTypeArray($type)) {
2✔
79
                $itemReferenceName = '';
2✔
80
                if ($schema->items === null) {
2✔
81
                    throw new InvalidSpecificationException('Array field does not have items specified.');
×
82
                }
83
                $sibling        = $this->resolveReference($schema->items);
2✔
84
                $itemsReference = $schema->items;
2✔
85
                if (isset($sibling->allOf) || FieldType::isSpecificationTypeObject($sibling->type)) {
2✔
86
                    $itemReferenceName = SchemaNaming::getClassName(
2✔
87
                        $itemsReference,
88
                        $operationName . ucfirst($fieldName) . 'Item'
2✔
89
                    );
90
                } elseif ($itemsReference instanceof Reference) {
1✔
NEW
91
                    $itemReferenceName = SchemaNaming::getClassName($itemsReference);
×
92
                }
93
                $arrayItem = $this->create(
2✔
94
                    $operationName,
95
                    lcfirst($itemReferenceName),
2✔
96
                    $sibling,
97
                    true,
2✔
98
                    $itemReferenceName
99
                );
100
            } elseif (FieldType::isSpecificationTypeObject($type)) {
2✔
101
                if ($referenceName === '') {
2✔
102
                    $referenceName = SchemaNaming::getClassName(
1✔
103
                        $schemaReference,
104
                        sprintf('%s%s', ucfirst($parentReferenceName), ucfirst($fieldName))
1✔
105
                    );
106
                }
107

108
                $objectProperties = $this->mapProperties($operationName, $schema, $referenceName);
2✔
109
            } elseif (!empty($schema->enum)) {
2✔
110
                if ($referenceName === '') {
1✔
111
                    $referenceName = SchemaNaming::getClassName(
1✔
112
                        $schemaReference,
113
                        sprintf('%s%s', ucfirst($parentReferenceName), ucfirst($fieldName))
1✔
114
                    );
115
                }
116
            }
117
            if (isset($schema->additionalProperties)) {
2✔
118
                if (is_bool($schema->additionalProperties)) {
2✔
119
                    $additionalProperties = $schema->additionalProperties;
2✔
120
                } elseif (is_object($schema->additionalProperties)) {
1✔
121
                    $warningMessage = sprintf(
1✔
122
                        'Additional properties object is not supported: %s.',
1✔
123
                        $referenceName
124
                    );
125
                    trigger_error($warningMessage, E_USER_WARNING);
1✔
126
                }
127
            }
128

129
            $fieldType = new FieldType($type, $this->phpVersion);
2✔
130
            $field     = new Field(
2✔
131
                $this->phpVersion,
2✔
132
                $fieldName,
133
                $fieldType,
134
                new ConstraintCollection(
2✔
135
                    new MinimumConstraint($schema->minimum, $schema->exclusiveMinimum, $fieldType),
2✔
136
                    new MaximumConstraint($schema->maximum, $schema->exclusiveMaximum, $fieldType),
2✔
137
                    new MinLengthConstraint($schema->minLength),
2✔
138
                    new MaxLengthConstraint($schema->maxLength),
2✔
139
                    new PatternConstraint($schema->pattern),
2✔
140
                    new MinItemsConstraint($schema->minItems),
2✔
141
                    new MaxItemsConstraint($schema->maxItems)
2✔
142
                ),
143
                $referenceName,
144
                $required,
145
                (bool)$schema->nullable,
2✔
146
                $additionalProperties,
147
                !empty($oneOf),
2✔
148
                !empty($anyOf)
2✔
149
            );
150

151
            if ($arrayItem !== null) {
2✔
152
                $field->setArrayItem($arrayItem);
2✔
153
            } elseif (!empty($objectProperties)) {
2✔
154
                $field->setObjectProperties($objectProperties);
2✔
155
            } elseif (isset($schema->enum)) {
2✔
156
                $field->setEnumValues($schema->enum);
1✔
157
            } elseif (!empty($oneOf)) {
2✔
158
                $field->setObjectProperties($oneOf);
×
159
            } elseif (!empty($anyOf)) {
2✔
160
                $field->setObjectProperties($anyOf);
×
161
            }
162

163
            if (isset($schema->format)) {
2✔
164
                $field->setFormat($schema->format);
2✔
165
            }
166

167
            if (isset($schema->default)) {
2✔
168
                $field->setDefault($schema->default);
1✔
169
            }
170

171
            if (isset($schema->discriminator)) {
2✔
172
                $field->setDiscriminator($schema->discriminator);
×
173
            }
174

175
            return $field;
2✔
176
        } catch (Throwable $exception) {
×
177
            throw new InvalidSpecificationException(
×
178
                sprintf(
×
179
                    'Error on mapping `%s`: %s',
×
180
                    $fieldName,
181
                    $exception->getMessage()
×
182
                )
183
            );
184
        }
185
    }
186

187
    protected function mapProperties(
188
        string $operationName,
189
        SpecObjectInterface $schema,
190
        string $schemaReferenceName
191
    ): array {
192
        $fields = [];
2✔
193
        foreach ($schema->properties as $childName => $child) {
2✔
194
            $required = false;
2✔
195
            if (isset($schema->required) && is_array($schema->required)) {
2✔
196
                $required = in_array($childName, $schema->required, true);
2✔
197
            }
198
            $fields[] = $this->create($operationName, $childName, $child, $required, '', $schemaReferenceName);
2✔
199
        }
200

201
        return $fields;
2✔
202
    }
203

204
    private function processSchemaOptions(
205
        array $schemaOptions,
206
        string $operationName,
207
        string $fieldName,
208
        string &$referenceName,
209
        array &$options,
210
        SpecObjectInterface $schemaReference
211
    ): void {
212
        if ($referenceName === '') {
×
213
            $referenceName = SchemaNaming::getClassName($schemaReference, $fieldName);
×
214
        }
215

216
        /** @var Reference $schemaOption */
217
        foreach ($schemaOptions as $schemaOption) {
×
218
            $explodedReference = explode('/', $schemaOption->getReference());
×
219
            $objectName        = $explodedReference[count($explodedReference) - 1];
×
220
            $options[]         = $this->create($operationName, $objectName, $this->resolveReference($schemaOption), false);
×
221
        }
222
    }
×
223

224
    private function resolveReference(SpecObjectInterface $schema): SpecObjectInterface
225
    {
226
        $resolved = $schema;
2✔
227
        if ($schema instanceof Reference) {
2✔
228
            $resolved = $schema->resolve();
2✔
229
        }
230

231
        if (is_array($resolved) || $resolved === null) {
2✔
232
            throw new UnexpectedValueException('Unexpected value after the reference resolution');
×
233
        }
234

235
        if ($resolved instanceof Parameter) {
2✔
236
            throw new InvalidSpecificationException('Usage of parameter inside of the schema is unexpected.');
×
237
        }
238

239
        return $resolved;
2✔
240
    }
241

242
    private function mergeAllOfProperties(string $operationName, SpecObjectInterface $schema): array
243
    {
244
        $allOfProperties = [];
×
245
        foreach ($schema->allOf as $allOfSchema) {
×
246
            if ($allOfSchema instanceof Reference) {
×
247
                $allOfSchema = $allOfSchema->resolve();
×
248
            }
249
            $allOfProperties[] = $this->mapProperties($operationName, $allOfSchema, '');
×
250
        }
251

252
        return array_merge([], ...$allOfProperties);
×
253
    }
254

255
    private function mergeAllOfAttributes(SpecObjectInterface $schema): SpecObjectInterface
256
    {
257
        foreach ($schema->allOf as $allOfSchema) {
×
258
            if ($allOfSchema instanceof Reference) {
×
259
                $allOfSchema = $allOfSchema->resolve();
×
260
            }
261

262
            if (isset($allOfSchema->nullable)) {
×
263
                $schema->nullable = $allOfSchema->nullable;
×
264
            }
265

266
            // @TODO add more attributes here later if needed
267
        }
268

269
        return $schema;
×
270
    }
271
}
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