Skip to content

Commit 7bbe87e

Browse files
authored
fix: prevent PHP "mixed" from being emitted as invalid OpenAPI "type: mixed" (#2011)
1 parent ede7ae7 commit 7bbe87e

4 files changed

Lines changed: 25 additions & 10 deletions

File tree

src/Type/AbstractTypeResolver.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ public function mapNativeType(OA\Schema $schema, $type): bool
5252
$type = $type[0];
5353
}
5454

55+
// PHP `mixed` is not a valid OpenAPI type; represent as unconstrained (no type set)
56+
if ('mixed' === $type) {
57+
return true;
58+
}
59+
5560
$schema->type = $type;
5661

5762
return true;

src/Type/LegacyTypeResolver.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,12 @@ protected function doAugment(Analysis $analysis, OA\Schema $schema, \Reflector $
7676
protected function augmentItems(OA\Schema $schema, Analysis $analysis): void
7777
{
7878
if (!Generator::isDefault($schema->type)) {
79+
// PHP `mixed` is not a valid OpenAPI type; leave items unconstrained
80+
$itemType = 'mixed' === $schema->type ? Generator::UNDEFINED : $schema->type;
81+
7982
if (Generator::isDefault($schema->items)) {
8083
$schema->items = new OA\Items([
81-
'type' => $schema->type,
84+
'type' => $itemType,
8285
'_context' => new Context(['generated' => true], $schema->_context),
8386
]);
8487

@@ -91,13 +94,15 @@ protected function augmentItems(OA\Schema $schema, Analysis $analysis): void
9194
$schema->ref = Generator::UNDEFINED;
9295
}
9396
} elseif (Generator::isDefault($schema->items->type, $schema->items->oneOf, $schema->items->allOf, $schema->items->anyOf)) {
94-
$schema->items->type = $schema->type;
97+
$schema->items->type = $itemType;
9598

9699
$this->type2ref($schema->items, $analysis);
97100
}
98101
}
99102

100-
$this->mapNativeType($schema->items, $schema->items->type);
103+
if (!Generator::isDefault($schema->items)) {
104+
$this->mapNativeType($schema->items, $schema->items->type);
105+
}
101106
$schema->type = 'array';
102107
}
103108

src/Type/TypeInfoTypeResolver.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
use Symfony\Component\TypeInfo\Type\ObjectType;
3333
use Symfony\Component\TypeInfo\Type\UnionType;
3434
use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory;
35+
use Symfony\Component\TypeInfo\TypeIdentifier;
3536
use Symfony\Component\TypeInfo\TypeResolver\ReflectionTypeResolver;
3637

3738
class TypeInfoTypeResolver extends AbstractTypeResolver
@@ -138,7 +139,11 @@ protected function setSchemaType(OA\Schema $schema, Type $type, Analysis $analys
138139
}
139140
}
140141
} else {
141-
if ($type instanceof BuiltinType || $type instanceof ObjectType) {
142+
if ($type instanceof BuiltinType) {
143+
if (!$type->isIdentifiedBy(TypeIdentifier::MIXED)) {
144+
$schema->type = (string) $type;
145+
}
146+
} elseif ($type instanceof ObjectType) {
142147
$schema->type = (string) $type;
143148
} elseif ($type instanceof IntRangeType) {
144149
$schema->type = $type->getTypeIdentifier()->value;

tests/Type/TypeResolverTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ public static function resolverAugmentCases(): iterable
4545
'nonzeroint' => '{ "type": "integer", "not": { "enum": [ 0 ] }, "property": "nonZeroInt" }',
4646
'legacy:arrayshape' => '{ "type": "array", "items": { "type": "boolean" }, "property": "arrayShape" }',
4747
'type-info:arrayshape' => '{ "type": "object", "additionalProperties": { "type": "boolean" }, "property": "arrayShape" }',
48-
'legacy:stringmap' => '{ "type": "array", "items": { "type": "mixed" }, "property": "stringMap" }',
48+
'legacy:stringmap' => '{ "type": "array", "items": {}, "property": "stringMap" }',
4949
'type-info:stringmap' => '{ "type": "object", "additionalProperties": { "type": "string" }, "property": "stringMap" }',
50-
'legacy:intkeyedmap' => '{ "type": "array", "items": { "type": "mixed" }, "property": "intKeyedMap" }',
50+
'legacy:intkeyedmap' => '{ "type": "array", "items": {}, "property": "intKeyedMap" }',
5151
'type-info:intkeyedmap' => '{ "type": "object", "additionalProperties": { "type": "string" }, "property": "intKeyedMap" }',
5252
'uniontype' => '{ "property": "unionType" }',
5353
'promotedstring' => '{ "type": "string", "property": "promotedString" }',
5454
'legacy:mixedunion' => '{ "example": "My value", "property": "mixedUnion" }',
55-
'type-info:mixedunion' => '{ "example": "My value", "oneOf": [ { "type": "string" }, { "type": "array", "items": { "type": "mixed" } } ], "property": "mixedUnion" }',
55+
'type-info:mixedunion' => '{ "example": "My value", "oneOf": [ { "type": "string" }, { "type": "array", "items": {} } ], "property": "mixedUnion" }',
5656
'getstring' => '{ "type": "string", "property": "getString" }',
5757
'paramdatetimelist' => '{ "type": "array", "items": { "type": "string", "format": "date-time" }, "property": "paramDateTimeList" }',
5858
'paramstringlist' => '{ "type": "array", "items": { "type": "string" }, "property": "paramStringList" }',
@@ -94,15 +94,15 @@ public static function resolverAugmentCases(): iterable
9494
'nonzeroint' => '{ "type": "integer", "not": { "const": 0 }, "property": "nonZeroInt" } ',
9595
'legacy:arrayshape' => '{ "type": "array", "items": { "type": "boolean" }, "property": "arrayShape" }',
9696
'type-info:arrayshape' => '{ "type": "object", "additionalProperties": { "type": "boolean" }, "property": "arrayShape" }',
97-
'legacy:stringmap' => '{ "type": "array", "items": { "type": "mixed" }, "property": "stringMap" }',
97+
'legacy:stringmap' => '{ "type": "array", "items": {}, "property": "stringMap" }',
9898
'type-info:stringmap' => '{ "type": "object", "additionalProperties": { "type": "string" }, "property": "stringMap" }',
99-
'legacy:intkeyedmap' => '{ "type": "array", "items": { "type": "mixed" }, "property": "intKeyedMap" }',
99+
'legacy:intkeyedmap' => '{ "type": "array", "items": {}, "property": "intKeyedMap" }',
100100
'type-info:intkeyedmap' => '{ "type": "object", "additionalProperties": { "type": "string" }, "property": "intKeyedMap" }',
101101
'legacy:uniontype' => '{ "property": "unionType" }',
102102
'type-info:uniontype' => '{ "type": [ "integer", "string" ], "property": "unionType" }',
103103
'promotedstring' => '{ "type": "string", "property": "promotedString" }',
104104
'legacy:mixedunion' => '{ "example": "My value", "property": "mixedUnion" }',
105-
'type-info:mixedunion' => '{ "example": "My value", "oneOf": [ { "type": [ "string" ] }, { "type": "array", "items": { "type": "mixed" } } ], "property": "mixedUnion" }',
105+
'type-info:mixedunion' => '{ "example": "My value", "oneOf": [ { "type": [ "string" ] }, { "type": "array", "items": {} } ], "property": "mixedUnion" }',
106106
'getstring' => '{ "type": "string", "property": "getString" }',
107107
'paramdatetimelist' => '{ "type": "array", "items": { "type": "string", "format": "date-time" }, "property": "paramDateTimeList" }',
108108
'paramstringlist' => '{ "type": "array", "items": { "type": "string" }, "property": "paramStringList" }',

0 commit comments

Comments
 (0)