Skip to content

Commit 500d3f9

Browse files
committed
bug #1593 [LiveComponent] Throw exception for typed LiveProps as interfaces (smnandre)
This PR was merged into the 2.x branch. Discussion ---------- [LiveComponent] Throw exception for typed LiveProps as interfaces | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Issues | Fix #1590 | License | MIT Check is done just after the hydration extensions are called, to allow userland implementations. Commits ------- 5b2e9a6 [LiveComponent] Throw an exception when trying to de/hydrate prop value typed with an interface
2 parents adccf73 + 5b2e9a6 commit 500d3f9

File tree

4 files changed

+77
-0
lines changed

4 files changed

+77
-0
lines changed

src/LiveComponent/src/LiveComponentHydrator.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,10 @@ private function dehydrateObjectValue(object $value, string $classType, ?string
496496
}
497497
}
498498

499+
if (interface_exists($classType)) {
500+
throw new \LogicException(sprintf('Cannot dehydrate value typed as interface "%s" on component "%s". Change this to a concrete type that can be dehydrated. Or set the hydrateWith/dehydrateWith options in LiveProp or set "useSerializerForHydration: true" on the LiveProp to use the serializer.', get_debug_type($value), $parentObject::class));
501+
}
502+
499503
$dehydratedObjectValues = [];
500504
foreach ((new PropertyInfoExtractor([new ReflectionExtractor()]))->getProperties($classType) as $property) {
501505
$propertyValue = $this->propertyAccessor->getValue($value, $property);
@@ -540,6 +544,10 @@ private function hydrateObjectValue(mixed $value, string $className, bool $allow
540544
}
541545
}
542546

547+
if (interface_exists($className)) {
548+
throw new \LogicException(sprintf('Cannot hydrate value typed as interface "%s" on component "%s". Change this to a concrete type that can be hydrated. Or set the hydrateWith/dehydrateWith options in LiveProp or set "useSerializerForHydration: true" on the LiveProp to use the serializer.', $className, $component::class));
549+
}
550+
543551
if (\is_array($value)) {
544552
$object = new $className();
545553
foreach ($value as $propertyName => $propertyValue) {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.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+
namespace Symfony\UX\LiveComponent\Tests\Fixtures\Dto;
13+
14+
class SimpleDto implements SimpleDtoInterface
15+
{
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.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+
namespace Symfony\UX\LiveComponent\Tests\Fixtures\Dto;
13+
14+
interface SimpleDtoInterface
15+
{
16+
}

src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\Embeddable2;
2626
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\Money;
2727
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\ParentDTO;
28+
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\SimpleDto;
29+
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\SimpleDtoInterface;
2830
use Symfony\UX\LiveComponent\Tests\Fixtures\Dto\Temperature;
2931
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\CategoryFixtureEntity;
3032
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\Embeddable1;
@@ -1307,6 +1309,41 @@ public function __construct()
13071309
);
13081310
}
13091311

1312+
public function testInterfaceTypedLivePropCannotBeHydrated(): void
1313+
{
1314+
$componentClass = new class() {
1315+
#[LiveProp(writable: true)]
1316+
public ?SimpleDtoInterface $prop = null;
1317+
};
1318+
1319+
$this->expectException(\LogicException::class);
1320+
$this->expectExceptionMessageMatches('/Unable to hydrate value of type "Symfony\UX\LiveComponent\Tests\Fixtures\Dto\SimpleDto" for property "prop" typed as "Symfony\UX\LiveComponent\Tests\Fixtures\Dto\SimpleDtoInterface" on component/');
1321+
$this->expectExceptionMessageMatches('/ Change this to a concrete type that can be hydrate/');
1322+
1323+
$this->hydrator()->hydrate(
1324+
component: new $componentClass(),
1325+
props: $this->hydrator()->addChecksumToData(['prop' => 'foo']),
1326+
updatedProps: ['prop' => 'bar'],
1327+
componentMetadata: $this->createLiveMetadata($componentClass),
1328+
);
1329+
}
1330+
1331+
public function testInterfaceTypedLivePropCannotBeDehydrated(): void
1332+
{
1333+
$componentClass = new class() {
1334+
#[LiveProp(writable: true)]
1335+
public ?SimpleDtoInterface $prop = null;
1336+
};
1337+
$component = new $componentClass();
1338+
$component->prop = new SimpleDto();
1339+
1340+
$this->expectException(\LogicException::class);
1341+
$this->expectExceptionMessageMatches('/Cannot dehydrate value typed as interface "Symfony\UX\LiveComponent\Tests\Fixtures\Dto\SimpleDtoInterface" on component ');
1342+
$this->expectExceptionMessageMatches('/ Change this to a concrete type that can be dehydrated/');
1343+
1344+
$this->hydrator()->dehydrate($component, new ComponentAttributes([]), $this->createLiveMetadata($component));
1345+
}
1346+
13101347
/**
13111348
* @dataProvider provideInvalidHydrationTests
13121349
*/

0 commit comments

Comments
 (0)