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

api-platform / core / 7274872202

20 Dec 2023 11:43AM UTC coverage: 37.396% (-0.08%) from 37.471%
7274872202

push

github

web-flow
Merge pull request #6060 from soyuka/merge-32b

Merge 3.1

1 of 94 new or added lines in 6 files covered. (1.06%)

4 existing lines in 2 files now uncovered.

10338 of 27645 relevant lines covered (37.4%)

28.66 hits per line

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

24.73
/src/JsonSchema/TypeFactory.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\JsonSchema;
15

16
use ApiPlatform\Metadata\ResourceClassResolverInterface;
17
use ApiPlatform\Metadata\Util\ResourceClassInfoTrait;
18
use Ramsey\Uuid\UuidInterface;
19
use Symfony\Component\PropertyInfo\Type;
20
use Symfony\Component\Uid\Ulid;
21
use Symfony\Component\Uid\Uuid;
22

23
/**
24
 * {@inheritdoc}
25
 *
26
 * @deprecated since 3.3 https://github.com/api-platform/core/pull/5470
27
 *
28
 * @author Kévin Dunglas <dunglas@gmail.com>
29
 */
30
final class TypeFactory implements TypeFactoryInterface
31
{
32
    use ResourceClassInfoTrait;
33

34
    private ?SchemaFactoryInterface $schemaFactory = null;
35

36
    public function __construct(ResourceClassResolverInterface $resourceClassResolver = null)
37
    {
38
        $this->resourceClassResolver = $resourceClassResolver;
146✔
39
    }
40

41
    public function setSchemaFactory(SchemaFactoryInterface $schemaFactory): void
42
    {
43
        $this->schemaFactory = $schemaFactory;
146✔
44
    }
45

46
    /**
47
     * {@inheritdoc}
48
     */
49
    public function getType(Type $type, string $format = 'json', bool $readableLink = null, array $serializerContext = null, Schema $schema = null): array
50
    {
51
        if ('jsonschema' === $format) {
72✔
52
            return [];
72✔
53
        }
54

55
        // TODO: OpenApiFactory uses this to compute filter types
56
        if ($type->isCollection()) {
20✔
57
            $keyType = $type->getCollectionKeyTypes()[0] ?? null;
20✔
58
            $subType = ($type->getCollectionValueTypes()[0] ?? null) ?? new Type($type->getBuiltinType(), false, $type->getClassName(), false);
20✔
59

60
            if (null !== $keyType && Type::BUILTIN_TYPE_STRING === $keyType->getBuiltinType()) {
20✔
61
                return $this->addNullabilityToTypeDefinition([
×
62
                    'type' => 'object',
×
63
                    'additionalProperties' => $this->getType($subType, $format, $readableLink, $serializerContext, $schema),
×
64
                ], $type, $schema);
×
65
            }
66

67
            return $this->addNullabilityToTypeDefinition([
20✔
68
                'type' => 'array',
20✔
69
                'items' => $this->getType($subType, $format, $readableLink, $serializerContext, $schema),
20✔
70
            ], $type, $schema);
20✔
71
        }
72

73
        return $this->addNullabilityToTypeDefinition($this->makeBasicType($type, $format, $readableLink, $serializerContext, $schema), $type, $schema);
20✔
74
    }
75

76
    private function makeBasicType(Type $type, string $format = 'json', bool $readableLink = null, array $serializerContext = null, Schema $schema = null): array
77
    {
78
        return match ($type->getBuiltinType()) {
20✔
79
            Type::BUILTIN_TYPE_INT => ['type' => 'integer'],
20✔
80
            Type::BUILTIN_TYPE_FLOAT => ['type' => 'number'],
20✔
81
            Type::BUILTIN_TYPE_BOOL => ['type' => 'boolean'],
20✔
82
            Type::BUILTIN_TYPE_OBJECT => $this->getClassType($type->getClassName(), $type->isNullable(), $format, $readableLink, $serializerContext, $schema),
20✔
83
            default => ['type' => 'string'],
20✔
84
        };
20✔
85
    }
86

87
    /**
88
     * Gets the JSON Schema document which specifies the data type corresponding to the given PHP class, and recursively adds needed new schema to the current schema if provided.
89
     */
90
    private function getClassType(?string $className, bool $nullable, string $format, ?bool $readableLink, ?array $serializerContext, ?Schema $schema): array
91
    {
92
        if (null === $className) {
×
93
            return ['type' => 'string'];
×
94
        }
95

96
        if (is_a($className, \DateTimeInterface::class, true)) {
×
97
            return [
×
98
                'type' => 'string',
×
99
                'format' => 'date-time',
×
100
            ];
×
101
        }
102
        if (is_a($className, \DateInterval::class, true)) {
×
103
            return [
×
104
                'type' => 'string',
×
105
                'format' => 'duration',
×
106
            ];
×
107
        }
108
        if (is_a($className, UuidInterface::class, true) || is_a($className, Uuid::class, true)) {
×
109
            return [
×
110
                'type' => 'string',
×
111
                'format' => 'uuid',
×
112
            ];
×
113
        }
114
        if (is_a($className, Ulid::class, true)) {
×
115
            return [
×
116
                'type' => 'string',
×
117
                'format' => 'ulid',
×
118
            ];
×
119
        }
120
        if (is_a($className, \SplFileInfo::class, true)) {
×
121
            return [
×
122
                'type' => 'string',
×
123
                'format' => 'binary',
×
124
            ];
×
125
        }
126
        if (!$this->isResourceClass($className) && is_a($className, \BackedEnum::class, true)) {
×
127
            $enumCases = array_map(static fn (\BackedEnum $enum): string|int => $enum->value, $className::cases());
×
128

129
            $type = \is_string($enumCases[0] ?? '') ? 'string' : 'integer';
×
130

131
            if ($nullable) {
×
132
                $enumCases[] = null;
×
133
            }
134

135
            return [
×
136
                'type' => $type,
×
137
                'enum' => $enumCases,
×
138
            ];
×
139
        }
140

141
        // Skip if $schema is null (filters only support basic types)
142
        if (null === $schema) {
×
143
            return ['type' => 'string'];
×
144
        }
145

146
        if (true !== $readableLink && $this->isResourceClass($className)) {
×
147
            return [
×
148
                'type' => 'string',
×
149
                'format' => 'iri-reference',
×
NEW
150
                'example' => 'https://example.com/',
×
UNCOV
151
            ];
×
152
        }
153

154
        $version = $schema->getVersion();
×
155

156
        $subSchema = new Schema($version);
×
157
        $subSchema->setDefinitions($schema->getDefinitions()); // Populate definitions of the main schema
×
158

159
        if (null === $this->schemaFactory) {
×
160
            throw new \LogicException('The schema factory must be injected by calling the "setSchemaFactory" method.');
×
161
        }
162

163
        $serializerContext += [SchemaFactory::FORCE_SUBSCHEMA => true];
×
164
        $subSchema = $this->schemaFactory->buildSchema($className, $format, Schema::TYPE_OUTPUT, null, $subSchema, $serializerContext, false);
×
165

166
        return ['$ref' => $subSchema['$ref']];
×
167
    }
168

169
    /**
170
     * @param array<string, mixed> $jsonSchema
171
     *
172
     * @return array<string, mixed>
173
     */
174
    private function addNullabilityToTypeDefinition(array $jsonSchema, Type $type, ?Schema $schema): array
175
    {
176
        if ($schema && Schema::VERSION_SWAGGER === $schema->getVersion()) {
20✔
177
            return $jsonSchema;
×
178
        }
179

180
        if (!$type->isNullable()) {
20✔
181
            return $jsonSchema;
20✔
182
        }
183

184
        if (\array_key_exists('$ref', $jsonSchema)) {
×
185
            $typeDefinition = ['anyOf' => [$jsonSchema]];
×
186

187
            if ($schema && Schema::VERSION_JSON_SCHEMA === $schema->getVersion()) {
×
188
                $typeDefinition['anyOf'][] = ['type' => 'null'];
×
189
            } else {
190
                // OpenAPI < 3.1
191
                $typeDefinition['nullable'] = true;
×
192
            }
193

194
            return $typeDefinition;
×
195
        }
196

197
        if ($schema && Schema::VERSION_JSON_SCHEMA === $schema->getVersion()) {
×
198
            return [...$jsonSchema, ...[
×
199
                'type' => \is_array($jsonSchema['type'])
×
200
                    ? array_merge($jsonSchema['type'], ['null'])
×
201
                    : [$jsonSchema['type'], 'null'],
×
202
            ]];
×
203
        }
204

205
        return [...$jsonSchema, ...['nullable' => true]];
×
206
    }
207
}
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