Skip to content

Commit ccadf9a

Browse files
committed
[LiveComponent] Fix BC break when using PropertyTypeExtractorInterface::getType() on a #[LiveProp] property x when getter getX exists
1 parent d071359 commit ccadf9a

File tree

2 files changed

+30
-16
lines changed

2 files changed

+30
-16
lines changed

src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ function (ChildDefinition $definition, AsLiveComponent $attribute) {
191191
->setArguments([
192192
new Reference('ux.twig_component.component_factory'),
193193
new Reference('property_info'),
194+
new Reference('type_info.resolver', ContainerInterface::NULL_ON_INVALID_REFERENCE),
194195
])
195196
->addTag('kernel.reset', ['method' => 'reset'])
196197
;

src/LiveComponent/src/Metadata/LiveComponentMetadataFactory.php

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313

1414
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
1515
use Symfony\Component\PropertyInfo\Type as LegacyType;
16-
use Symfony\Component\TypeInfo\Type\IntersectionType;
17-
use Symfony\Component\TypeInfo\Type\NullableType;
18-
use Symfony\Component\TypeInfo\Type\UnionType;
16+
use Symfony\Component\TypeInfo\Type;
17+
use Symfony\Component\TypeInfo\Type\CollectionType;
18+
use Symfony\Component\TypeInfo\TypeResolver\TypeResolver;
1919
use Symfony\Contracts\Service\ResetInterface;
2020
use Symfony\UX\LiveComponent\Attribute\LiveProp;
2121
use Symfony\UX\TwigComponent\ComponentFactory;
@@ -33,7 +33,11 @@ class LiveComponentMetadataFactory implements ResetInterface
3333
public function __construct(
3434
private ComponentFactory $componentFactory,
3535
private PropertyTypeExtractorInterface $propertyTypeExtractor,
36+
private ?TypeResolver $typeResolver = null,
3637
) {
38+
if (method_exists($this->propertyTypeExtractor, 'getType') && !$this->typeResolver) {
39+
throw new \LogicException('Symfony TypeInfo is required to use LiveProps. Try running "composer require symfony/type-info".');
40+
}
3741
}
3842

3943
public function getMetadata(string $name): LiveComponentMetadata
@@ -77,13 +81,13 @@ public function createPropMetadatas(\ReflectionClass $class): array
7781

7882
public function createLivePropMetadata(string $className, string $propertyName, \ReflectionProperty $property, LiveProp $liveProp): LivePropMetadata|LegacyLivePropMetadata
7983
{
84+
$reflectionType = $property->getType();
85+
if ($reflectionType instanceof \ReflectionUnionType || $reflectionType instanceof \ReflectionIntersectionType) {
86+
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()));
87+
}
88+
8089
// BC layer when "symfony/type-info" is not available
8190
if (!method_exists($this->propertyTypeExtractor, 'getType')) {
82-
$type = $property->getType();
83-
if ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) {
84-
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()));
85-
}
86-
8791
$infoTypes = $this->propertyTypeExtractor->getTypes($className, $propertyName) ?? [];
8892

8993
$collectionValueType = null;
@@ -96,14 +100,16 @@ public function createLivePropMetadata(string $className, string $propertyName,
96100
}
97101
}
98102

99-
if (null === $type && null === $collectionValueType && isset($infoTypes[0])) {
103+
if (null === $reflectionType && null === $collectionValueType && isset($infoTypes[0])) {
104+
// If it's an "advanced" type (like a Collection), let's use the PropertyTypeExtractor to get the Type
100105
$infoType = LegacyType::BUILTIN_TYPE_OBJECT === $infoTypes[0]->getBuiltinType() ? $infoTypes[0]->getClassName() : $infoTypes[0]->getBuiltinType();
101106
$isTypeBuiltIn = null === $infoTypes[0]->getClassName();
102107
$isTypeNullable = $infoTypes[0]->isNullable();
103108
} else {
104-
$infoType = $type?->getName();
105-
$isTypeBuiltIn = $type?->isBuiltin() ?? false;
106-
$isTypeNullable = $type?->allowsNull() ?? true;
109+
// Otherwise, we can use the ReflectionType to get the Type
110+
$infoType = $reflectionType?->getName();
111+
$isTypeBuiltIn = $reflectionType?->isBuiltin() ?? false;
112+
$isTypeNullable = $reflectionType?->allowsNull() ?? true;
107113
}
108114

109115
return new LegacyLivePropMetadata(
@@ -115,10 +121,17 @@ public function createLivePropMetadata(string $className, string $propertyName,
115121
$collectionValueType
116122
);
117123
} else {
118-
$type = $this->propertyTypeExtractor->getType($className, $property->getName());
119-
120-
if ($type instanceof UnionType && !$type instanceof NullableType || $type instanceof IntersectionType) {
121-
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".', $propertyName, $className));
124+
$infoType = $this->propertyTypeExtractor->getType($className, $property->getName());
125+
126+
if ($infoType instanceof CollectionType) {
127+
// If it's an "advanced" type (like CollectionType), let's use the PropertyTypeExtractor to get the Type
128+
$type = $infoType;
129+
} elseif (null !== $reflectionType) {
130+
// Otherwise, we can use the TypeResolver to convert the ReflectionType to a Type
131+
$type = $this->typeResolver->resolve($reflectionType);
132+
} else {
133+
// If no type is available, we default to mixed
134+
$type = Type::mixed();
122135
}
123136

124137
return new LivePropMetadata($property->getName(), $liveProp, $type);

0 commit comments

Comments
 (0)