Skip to content

Commit 3ce901a

Browse files
committed
[LiveComponent] Use TypeInfo Type
1 parent 59e840d commit 3ce901a

File tree

8 files changed

+448
-133
lines changed

8 files changed

+448
-133
lines changed

src/LiveComponent/composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@
5656
"zenstruck/foundry": "^2.0"
5757
},
5858
"conflict": {
59-
"symfony/config": "<5.4.0"
59+
"symfony/config": "<5.4.0",
60+
"symfony/type-info": "<7.3",
61+
"symfony/property-info": "~7.0"
6062
},
6163
"config": {
6264
"sort-packages": true

src/LiveComponent/src/LiveComponentHydrator.php

Lines changed: 175 additions & 49 deletions
Large diffs are not rendered by default.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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+
namespace Symfony\UX\LiveComponent\Metadata;
13+
14+
use Symfony\Component\PropertyInfo\Type;
15+
use Symfony\UX\LiveComponent\Attribute\LiveProp;
16+
17+
/**
18+
* @author Kevin Bond <[email protected]>
19+
*
20+
* @internal
21+
*/
22+
final class LegacyLivePropMetadata
23+
{
24+
public function __construct(
25+
private string $name,
26+
private LiveProp $liveProp,
27+
private ?string $typeName,
28+
private bool $isBuiltIn,
29+
private bool $allowsNull,
30+
private ?Type $collectionValueType,
31+
) {
32+
}
33+
34+
public function getName(): string
35+
{
36+
return $this->name;
37+
}
38+
39+
public function getType(): ?string
40+
{
41+
return $this->typeName;
42+
}
43+
44+
public function isBuiltIn(): bool
45+
{
46+
return $this->isBuiltIn;
47+
}
48+
49+
public function allowsNull(): bool
50+
{
51+
return $this->allowsNull;
52+
}
53+
54+
public function urlMapping(): ?UrlMapping
55+
{
56+
return $this->liveProp->url() ?: null;
57+
}
58+
59+
public function calculateFieldName(object $component, string $fallback): string
60+
{
61+
return $this->liveProp->calculateFieldName($component, $fallback);
62+
}
63+
64+
/**
65+
* @return array<string>
66+
*/
67+
public function writablePaths(): array
68+
{
69+
return $this->liveProp->writablePaths();
70+
}
71+
72+
public function hydrateMethod(): ?string
73+
{
74+
return $this->liveProp->hydrateMethod();
75+
}
76+
77+
public function dehydrateMethod(): ?string
78+
{
79+
return $this->liveProp->dehydrateMethod();
80+
}
81+
82+
public function isIdentityWritable(): bool
83+
{
84+
return $this->liveProp->isIdentityWritable();
85+
}
86+
87+
public function acceptUpdatesFromParent(): bool
88+
{
89+
return $this->liveProp->acceptUpdatesFromParent();
90+
}
91+
92+
public function useSerializerForHydration(): bool
93+
{
94+
return $this->liveProp->useSerializerForHydration();
95+
}
96+
97+
public function serializationContext(): array
98+
{
99+
return $this->liveProp->serializationContext();
100+
}
101+
102+
public function collectionValueType(): ?Type
103+
{
104+
return $this->collectionValueType;
105+
}
106+
107+
public function getFormat(): ?string
108+
{
109+
return $this->liveProp->format();
110+
}
111+
112+
public function onUpdated(): string|array|null
113+
{
114+
return $this->liveProp->onUpdated();
115+
}
116+
117+
public function hasModifier(): bool
118+
{
119+
return null !== $this->liveProp->modifier();
120+
}
121+
122+
/**
123+
* Applies a modifier specified in LiveProp attribute.
124+
*
125+
* If a modifier is specified, a modified clone is returned.
126+
* Otherwise, the metadata is returned as it is.
127+
*/
128+
public function withModifier(object $component): self
129+
{
130+
if (null === ($modifier = $this->liveProp->modifier())) {
131+
return $this;
132+
}
133+
134+
if (!method_exists($component, $modifier)) {
135+
throw new \LogicException(\sprintf('Method "%s::%s()" given in LiveProp "modifier" does not exist.', $component::class, $modifier));
136+
}
137+
138+
$modifiedLiveProp = $component->{$modifier}($this->liveProp, $this->getName());
139+
if (!$modifiedLiveProp instanceof LiveProp) {
140+
throw new \LogicException(\sprintf('Method "%s::%s()" should return an instance of "%s" (given: "%s").', $component::class, $modifier, LiveProp::class, get_debug_type($modifiedLiveProp)));
141+
}
142+
143+
$clone = clone $this;
144+
$clone->liveProp = $modifiedLiveProp;
145+
146+
return $clone;
147+
}
148+
}

src/LiveComponent/src/Metadata/LiveComponentMetadata.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ class LiveComponentMetadata
2222
{
2323
public function __construct(
2424
private ComponentMetadata $componentMetadata,
25-
/** @var LivePropMetadata[] */
25+
/** @var list<LivePropMetadata|LegacyLivePropMetadata> */
2626
private array $livePropsMetadata,
2727
) {
2828
uasort(
2929
$this->livePropsMetadata,
30-
static fn (LivePropMetadata $a, LivePropMetadata $b) => $a->hasModifier() <=> $b->hasModifier()
30+
static fn (LivePropMetadata|LegacyLivePropMetadata $a, LivePropMetadata|LegacyLivePropMetadata $b) => $a->hasModifier() <=> $b->hasModifier()
3131
);
3232
}
3333

@@ -37,7 +37,7 @@ public function getComponentMetadata(): ComponentMetadata
3737
}
3838

3939
/**
40-
* @return LivePropMetadata[]
40+
* @return list<LivePropMetadata|LegacyLivePropMetadata>
4141
*/
4242
public function getAllLivePropsMetadata(object $component): iterable
4343
{
@@ -55,7 +55,7 @@ public function getAllLivePropsMetadata(object $component): iterable
5555
*/
5656
public function getOnlyPropsThatAcceptUpdatesFromParent(array $inputProps): array
5757
{
58-
$writableProps = array_filter($this->livePropsMetadata, function (LivePropMetadata $livePropMetadata) {
58+
$writableProps = array_filter($this->livePropsMetadata, function (LivePropMetadata|LegacyLivePropMetadata $livePropMetadata) {
5959
return $livePropMetadata->acceptUpdatesFromParent();
6060
});
6161

src/LiveComponent/src/Metadata/LiveComponentMetadataFactory.php

Lines changed: 45 additions & 31 deletions
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\IntersectionType;
17+
use Symfony\Component\TypeInfo\Type\NullableType;
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;
@@ -48,7 +51,7 @@ public function getMetadata(string $name): LiveComponentMetadata
4851
}
4952

5053
/**
51-
* @return LivePropMetadata[]
54+
* @return list<LivePropMetadata|LegacyLivePropMetadata>
5255
*
5356
* @internal
5457
*/
@@ -72,43 +75,54 @@ public function createPropMetadatas(\ReflectionClass $class): array
7275
return array_values($metadatas);
7376
}
7477

75-
public function createLivePropMetadata(string $className, string $propertyName, \ReflectionProperty $property, LiveProp $liveProp): LivePropMetadata
78+
public function createLivePropMetadata(string $className, string $propertyName, \ReflectionProperty $property, LiveProp $liveProp): LivePropMetadata|LegacyLivePropMetadata
7679
{
77-
$type = $property->getType();
78-
if ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) {
79-
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()));
80-
}
80+
// BC layer when "symfony/type-info" is not available
81+
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+
}
8186

82-
$infoTypes = $this->propertyTypeExtractor->getTypes($className, $propertyName) ?? [];
87+
$infoTypes = $this->propertyTypeExtractor->getTypes($className, $propertyName) ?? [];
8388

84-
$collectionValueType = null;
85-
foreach ($infoTypes as $infoType) {
86-
if ($infoType->isCollection()) {
87-
foreach ($infoType->getCollectionValueTypes() as $valueType) {
88-
$collectionValueType = $valueType;
89-
break;
89+
$collectionValueType = null;
90+
foreach ($infoTypes as $infoType) {
91+
if ($infoType->isCollection()) {
92+
foreach ($infoType->getCollectionValueTypes() as $valueType) {
93+
$collectionValueType = $valueType;
94+
break;
95+
}
9096
}
9197
}
92-
}
9398

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();
99+
if (null === $type && null === $collectionValueType && isset($infoTypes[0])) {
100+
$infoType = LegacyType::BUILTIN_TYPE_OBJECT === $infoTypes[0]->getBuiltinType() ? $infoTypes[0]->getClassName() : $infoTypes[0]->getBuiltinType();
101+
$isTypeBuiltIn = null === $infoTypes[0]->getClassName();
102+
$isTypeNullable = $infoTypes[0]->isNullable();
103+
} else {
104+
$infoType = $type?->getName();
105+
$isTypeBuiltIn = $type?->isBuiltin() ?? false;
106+
$isTypeNullable = $type?->allowsNull() ?? true;
107+
}
108+
109+
return new LegacyLivePropMetadata(
110+
$property->getName(),
111+
$liveProp,
112+
$infoType,
113+
$isTypeBuiltIn,
114+
$isTypeNullable,
115+
$collectionValueType
116+
);
98117
} else {
99-
$infoType = $type?->getName();
100-
$isTypeBuiltIn = $type?->isBuiltin() ?? false;
101-
$isTypeNullable = $type?->allowsNull() ?? true;
102-
}
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));
122+
}
103123

104-
return new LivePropMetadata(
105-
$property->getName(),
106-
$liveProp,
107-
$infoType,
108-
$isTypeBuiltIn,
109-
$isTypeNullable,
110-
$collectionValueType
111-
);
124+
return new LivePropMetadata($property->getName(), $liveProp, $type);
125+
}
112126
}
113127

114128
/**

src/LiveComponent/src/Metadata/LivePropMetadata.php

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace Symfony\UX\LiveComponent\Metadata;
1313

14-
use Symfony\Component\PropertyInfo\Type;
14+
use Symfony\Component\TypeInfo\Type;
1515
use Symfony\UX\LiveComponent\Attribute\LiveProp;
1616

1717
/**
@@ -24,10 +24,7 @@ final class LivePropMetadata
2424
public function __construct(
2525
private string $name,
2626
private LiveProp $liveProp,
27-
private ?string $typeName,
28-
private bool $isBuiltIn,
29-
private bool $allowsNull,
30-
private ?Type $collectionValueType,
27+
private ?Type $type,
3128
) {
3229
}
3330

@@ -36,19 +33,9 @@ public function getName(): string
3633
return $this->name;
3734
}
3835

39-
public function getType(): ?string
36+
public function getType(): ?Type
4037
{
41-
return $this->typeName;
42-
}
43-
44-
public function isBuiltIn(): bool
45-
{
46-
return $this->isBuiltIn;
47-
}
48-
49-
public function allowsNull(): bool
50-
{
51-
return $this->allowsNull;
38+
return $this->type;
5239
}
5340

5441
public function urlMapping(): ?UrlMapping
@@ -99,11 +86,6 @@ public function serializationContext(): array
9986
return $this->liveProp->serializationContext();
10087
}
10188

102-
public function collectionValueType(): ?Type
103-
{
104-
return $this->collectionValueType;
105-
}
106-
10789
public function getFormat(): ?string
10890
{
10991
return $this->liveProp->format();

0 commit comments

Comments
 (0)