Skip to content

Commit b4a194c

Browse files
committed
Use TypeInfo types if available
1 parent 8db386c commit b4a194c

File tree

3 files changed

+65
-29
lines changed

3 files changed

+65
-29
lines changed

src/LiveComponent/src/LiveComponentHydrator.php

+19-9
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
1919
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
2020
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
21-
use Symfony\Component\PropertyInfo\Type;
21+
use Symfony\Component\PropertyInfo\Type as LegacyType;
2222
use Symfony\Component\Serializer\Exception\ExceptionInterface as SerializerExceptionInterface;
2323
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
2424
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
25+
use Symfony\Component\TypeInfo\Type\BuiltinType;
26+
use Symfony\Component\TypeInfo\Type\ObjectType;
2527
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
2628
use Symfony\UX\LiveComponent\Attribute\LiveProp;
2729
use Symfony\UX\LiveComponent\Exception\HydrationException;
@@ -245,6 +247,7 @@ public function hydrateValue(mixed $value, LivePropMetadata $propMetadata, objec
245247
return $parentObject->{$propMetadata->hydrateMethod()}($value);
246248
}
247249

250+
$collectionValueType = $propMetadata->collectionValueType();
248251
if ($propMetadata->useSerializerForHydration()) {
249252
if (!interface_exists(DenormalizerInterface::class)) {
250253
throw new \LogicException(sprintf('The LiveProp "%s" on component "%s" has "useSerializerForHydration: true", but the Serializer component is not installed. Try running "composer require symfony/serializer".', $propMetadata->getName(), $parentObject::class));
@@ -256,10 +259,12 @@ public function hydrateValue(mixed $value, LivePropMetadata $propMetadata, objec
256259
throw new \LogicException(sprintf('The LiveProp "%s" on component "%s" has "useSerializerForHydration: true", but the given serializer does not implement DenormalizerInterface.', $propMetadata->getName(), $parentObject::class));
257260
}
258261

259-
if ($propMetadata->collectionValueType()) {
260-
$builtInType = $propMetadata->collectionValueType()->getBuiltinType();
261-
if (Type::BUILTIN_TYPE_OBJECT === $builtInType) {
262-
$type = $propMetadata->collectionValueType()->getClassName().'[]';
262+
if ($collectionValueType instanceof BuiltinType || $collectionValueType instanceof ObjectType) {
263+
$type = $collectionValueType->asNonNullable().'[]';
264+
} elseif ($collectionValueType instanceof LegacyType) {
265+
$builtInType = $collectionValueType->getBuiltinType();
266+
if (LegacyType::BUILTIN_TYPE_OBJECT === $builtInType) {
267+
$type = $collectionValueType->getClassName().'[]';
263268
} else {
264269
$type = $builtInType.'[]';
265270
}
@@ -274,8 +279,10 @@ public function hydrateValue(mixed $value, LivePropMetadata $propMetadata, objec
274279
return $this->serializer->denormalize($value, $type, 'json', $propMetadata->serializationContext());
275280
}
276281

277-
if ($propMetadata->collectionValueType() && Type::BUILTIN_TYPE_OBJECT === $propMetadata->collectionValueType()->getBuiltinType()) {
278-
$collectionClass = $propMetadata->collectionValueType()->getClassName();
282+
if ($collectionValueType instanceof ObjectType
283+
|| $collectionValueType instanceof LegacyType && LegacyType::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()
284+
) {
285+
$collectionClass = $collectionValueType->getClassName();
279286
foreach ($value as $key => $objectItem) {
280287
$value[$key] = $this->hydrateObjectValue($objectItem, $collectionClass, true, $propMetadata->getFormat(), $parentObject::class, sprintf('%s.%s', $propMetadata->getName(), $key), $parentObject);
281288
}
@@ -448,8 +455,11 @@ private function dehydrateValue(mixed $value, LivePropMetadata $propMetadata, ob
448455
}
449456

450457
if (\is_array($value)) {
451-
if ($propMetadata->collectionValueType() && Type::BUILTIN_TYPE_OBJECT === $propMetadata->collectionValueType()->getBuiltinType()) {
452-
$collectionClass = $propMetadata->collectionValueType()->getClassName();
458+
$collectionValueType = $propMetadata->collectionValueType();
459+
if ($collectionValueType instanceof ObjectType
460+
|| $collectionValueType instanceof LegacyType && LegacyType::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()
461+
) {
462+
$collectionClass = $collectionValueType->getClassName();
453463
foreach ($value as $key => $objectItem) {
454464
if (!$objectItem instanceof $collectionClass) {
455465
throw new \LogicException(sprintf('The LiveProp "%s" on component "%s" is an array. We determined the array is full of %s objects, but at least on key had a different value of %s', $propMetadata->getName(), $parentObject::class, $collectionClass, get_debug_type($objectItem)));

src/LiveComponent/src/Metadata/LiveComponentMetadataFactory.php

+42-17
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
namespace Symfony\UX\LiveComponent\Metadata;
1313

1414
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
15-
use Symfony\Component\PropertyInfo\Type;
15+
use Symfony\Component\PropertyInfo\Type as LegacyType;
16+
use Symfony\Component\TypeInfo\Type\BuiltinType;
17+
use Symfony\Component\TypeInfo\Type\CollectionType;
18+
use Symfony\Component\TypeInfo\Type\UnionType;
1619
use Symfony\Contracts\Service\ResetInterface;
1720
use Symfony\UX\LiveComponent\Attribute\LiveProp;
1821
use Symfony\UX\TwigComponent\ComponentFactory;
@@ -79,26 +82,48 @@ public function createLivePropMetadata(string $className, string $propertyName,
7982
throw new \LogicException(sprintf('Union or intersection types are not supported for LiveProps. You may want to change the type of property %s in %s.', $property->getName(), $property->getDeclaringClass()->getName()));
8083
}
8184

82-
$infoTypes = $this->propertyTypeExtractor->getTypes($className, $propertyName) ?? [];
85+
if (method_exists($this->propertyTypeExtractor, 'getType')) {
86+
$infoType = $this->propertyTypeExtractor->getType($className, $propertyName);
87+
$isTypeBuiltIn = $type?->isBuiltin() ?? ($infoType instanceof BuiltinType);
88+
$isTypeNullable = $type?->allowsNull() ?? $infoType?->isNullable() ?? true;
8389

84-
$collectionValueType = null;
85-
foreach ($infoTypes as $infoType) {
86-
if ($infoType->isCollection()) {
87-
foreach ($infoType->getCollectionValueTypes() as $valueType) {
88-
$collectionValueType = $valueType;
89-
break;
90+
$infoType = $infoType?->asNonNullable();
91+
92+
$collectionValueType = null;
93+
foreach ($infoType instanceof UnionType ? $infoType->getTypes() : [$infoType] as $currentType) {
94+
if (!$currentType instanceof CollectionType) {
95+
continue;
96+
}
97+
98+
$collectionValueType = $currentType->getCollectionValueType();
99+
if ($collectionValueType instanceof UnionType) {
100+
[$collectionValueType] = $collectionValueType->getTypes();
90101
}
91-
}
92-
}
93102

94-
if (null === $type && null === $collectionValueType && isset($infoTypes[0])) {
95-
$infoType = Type::BUILTIN_TYPE_OBJECT === $infoTypes[0]->getBuiltinType() ? $infoTypes[0]->getClassName() : $infoTypes[0]->getBuiltinType();
96-
$isTypeBuiltIn = null === $infoTypes[0]->getClassName();
97-
$isTypeNullable = $infoTypes[0]->isNullable();
103+
break;
104+
}
98105
} else {
99-
$infoType = $type?->getName();
100-
$isTypeBuiltIn = $type?->isBuiltin() ?? false;
101-
$isTypeNullable = $type?->allowsNull() ?? true;
106+
$infoTypes = $this->propertyTypeExtractor->getTypes($className, $propertyName) ?? [];
107+
108+
$collectionValueType = null;
109+
foreach ($infoTypes as $infoType) {
110+
if ($infoType->isCollection()) {
111+
foreach ($infoType->getCollectionValueTypes() as $valueType) {
112+
$collectionValueType = $valueType;
113+
break;
114+
}
115+
}
116+
}
117+
118+
if (null === $type && null === $collectionValueType && isset($infoTypes[0])) {
119+
$infoType = LegacyType::BUILTIN_TYPE_OBJECT === $infoTypes[0]->getBuiltinType() ? $infoTypes[0]->getClassName() : $infoTypes[0]->getBuiltinType();
120+
$isTypeBuiltIn = null === $infoTypes[0]->getClassName();
121+
$isTypeNullable = $infoTypes[0]->isNullable();
122+
} else {
123+
$infoType = $type?->getName();
124+
$isTypeBuiltIn = $type?->isBuiltin() ?? false;
125+
$isTypeNullable = $type?->allowsNull() ?? true;
126+
}
102127
}
103128

104129
return new LivePropMetadata(

src/LiveComponent/src/Metadata/LivePropMetadata.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111

1212
namespace Symfony\UX\LiveComponent\Metadata;
1313

14-
use Symfony\Component\PropertyInfo\Type;
14+
use Symfony\Component\PropertyInfo\Type as LegacyType;
15+
use Symfony\Component\TypeInfo\Type;
1516
use Symfony\UX\LiveComponent\Attribute\LiveProp;
1617

1718
/**
@@ -27,7 +28,7 @@ public function __construct(
2728
private ?string $typeName,
2829
private bool $isBuiltIn,
2930
private bool $allowsNull,
30-
private ?Type $collectionValueType,
31+
private Type|LegacyType|null $collectionValueType,
3132
) {
3233
}
3334

@@ -99,7 +100,7 @@ public function serializationContext(): array
99100
return $this->liveProp->serializationContext();
100101
}
101102

102-
public function collectionValueType(): ?Type
103+
public function collectionValueType(): Type|LegacyType|null
103104
{
104105
return $this->collectionValueType;
105106
}

0 commit comments

Comments
 (0)