|
16 | 16 | use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; |
17 | 17 | use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException; |
18 | 18 | use Symfony\Component\PropertyAccess\PropertyAccessorInterface; |
19 | | -use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; |
| 19 | +use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; |
20 | 20 | use Symfony\Component\PropertyInfo\Type; |
21 | 21 | use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; |
22 | 22 | use Symfony\Component\Serializer\Normalizer\NormalizerInterface; |
@@ -47,6 +47,7 @@ final class LiveComponentHydrator |
47 | 47 | public function __construct( |
48 | 48 | private iterable $hydrationExtensions, |
49 | 49 | private PropertyAccessorInterface $propertyAccessor, |
| 50 | + private PropertyTypeExtractorInterface $propertyTypeExtractor, |
50 | 51 | private NormalizerInterface|DenormalizerInterface $normalizer, |
51 | 52 | private string $secret |
52 | 53 | ) { |
@@ -343,22 +344,15 @@ private function dehydrateValue(mixed $value, LivePropMetadata $propMetadata, ob |
343 | 344 | } |
344 | 345 |
|
345 | 346 | if (\is_array($value)) { |
346 | | - if ($propMetadata->collectionValueType() && Type::BUILTIN_TYPE_OBJECT === $propMetadata->collectionValueType()->getBuiltinType()) { |
347 | | - $collectionClass = $propMetadata->collectionValueType()->getClassName(); |
348 | | - foreach ($value as $key => $objectItem) { |
349 | | - if (!$objectItem instanceof $collectionClass) { |
350 | | - 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(), $component::class, $collectionClass, get_debug_type($objectItem))); |
351 | | - } |
352 | | - |
353 | | - $value[$key] = $this->dehydrateObjectValue($objectItem, $collectionClass, $propMetadata->getFormat(), $component::class, sprintf('%s.%s', $propMetadata->getName(), $key)); |
| 347 | + foreach ($value as $key => $objectItem) { |
| 348 | + $type = gettype($objectItem); |
| 349 | + if ($type === 'object') { |
| 350 | + $type = get_class($objectItem); |
354 | 351 | } |
355 | | - } |
356 | 352 |
|
357 | | - if (!$this->isValueValidDehydratedValue($value)) { |
358 | | - $badKeys = $this->getNonScalarKeys($value, $propMetadata->getName()); |
359 | | - $badKeysText = implode(', ', array_map(fn ($key) => sprintf('%s: %s', $key, $badKeys[$key]), array_keys($badKeys))); |
| 353 | + $propMetadata = new LivePropMetadata($key, new LiveProp(true), $type, false, true, null); |
360 | 354 |
|
361 | | - throw new \LogicException(sprintf('The LiveProp "%s" on component "%s" is an array, but it contains one or more keys that are not scalars: %s', $propMetadata->getName(), $component::class, $badKeysText)); |
| 355 | + $value[$key] = $this->dehydrateValue($objectItem, $propMetadata, $component); |
362 | 356 | } |
363 | 357 |
|
364 | 358 | return $value; |
@@ -394,19 +388,11 @@ private function dehydrateObjectValue(object $value, string $classType, ?string |
394 | 388 | } |
395 | 389 | } |
396 | 390 |
|
397 | | - $reflexionExtractor = new ReflectionExtractor(); |
398 | | - $properties = $reflexionExtractor->getProperties($classType); |
399 | 391 | $propertiesValues = []; |
400 | | - foreach ($properties as $property) { |
401 | | - if ($reflexionExtractor->isReadable($classType, $property)) { |
402 | | - $propertyValue = $this->propertyAccessor->getValue($value, $property); |
403 | | - $type = $reflexionExtractor->getTypes($classType, $property)[0]->getBuiltinType(); |
404 | | - if ($type === 'object') { |
405 | | - $type = $reflexionExtractor->getTypes($classType, $property)[0]->getClassName(); |
406 | | - } |
407 | | - $propMetadata = new LivePropMetadata($property, new LiveProp(true), $type, false, true, null); |
408 | | - $propertiesValues[$property] = $this->dehydrateValue($propertyValue, $propMetadata, $component); |
409 | | - } |
| 392 | + foreach ((new \ReflectionClass($classType))->getProperties() as $property) { |
| 393 | + $propertyValue = $this->propertyAccessor->getValue($value, $property->getName()); |
| 394 | + $propMetadata = $this->generateLivePropMetadata($classType, $property->getName()); |
| 395 | + $propertiesValues[$property->getName()] = $this->dehydrateValue($propertyValue, $propMetadata, $component); |
410 | 396 | } |
411 | 397 |
|
412 | 398 | return $propertiesValues; |
@@ -486,15 +472,8 @@ private function hydrateObjectValue(mixed $value, string $className, bool $allow |
486 | 472 |
|
487 | 473 | if (is_array($value)) { |
488 | 474 | $object = new $className; |
489 | | - $extractor = new ReflectionExtractor(); |
490 | 475 | foreach ($value as $property => $propertyValue) { |
491 | | - $type = $extractor->getTypes($className, $property)[0]->getBuiltinType(); |
492 | | - $buildIn = true; |
493 | | - if ($type === 'object') { |
494 | | - $type = $extractor->getTypes($className, $property)[0]->getClassName(); |
495 | | - $buildIn = false; |
496 | | - } |
497 | | - $propMetadata = new LivePropMetadata($property, new LiveProp(true), $type, $buildIn, true, null); |
| 476 | + $propMetadata = $this->generateLivePropMetadata($className, $property); |
498 | 477 | $this->propertyAccessor->setValue($object, $property, $this->hydrateValue($propertyValue, $propMetadata, $component)); |
499 | 478 | } |
500 | 479 |
|
@@ -593,4 +572,35 @@ private function recursiveKeySort(array &$data): void |
593 | 572 | } |
594 | 573 | ksort($data); |
595 | 574 | } |
| 575 | + |
| 576 | + private function generateLivePropMetadata(string $className, string $propertyName): LivePropMetadata |
| 577 | + { |
| 578 | + $reflexionClass = new \ReflectionClass($className); |
| 579 | + $property = $reflexionClass->getProperty($propertyName); |
| 580 | + |
| 581 | + $collectionValueType = null; |
| 582 | + $infoTypes = $this->propertyTypeExtractor->getTypes($className, $propertyName) ?? []; |
| 583 | + foreach ($infoTypes as $infoType) { |
| 584 | + if ($infoType->isCollection()) { |
| 585 | + foreach ($infoType->getCollectionValueTypes() as $valueType) { |
| 586 | + $collectionValueType = $valueType; |
| 587 | + break; |
| 588 | + } |
| 589 | + } |
| 590 | + } |
| 591 | + |
| 592 | + $type = $property->getType(); |
| 593 | + if ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) { |
| 594 | + 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())); |
| 595 | + } |
| 596 | + |
| 597 | + return new LivePropMetadata( |
| 598 | + $property->getName(), |
| 599 | + new LiveProp(true), |
| 600 | + $type ? $type->getName() : null, |
| 601 | + $type ? $type->isBuiltin() : false, |
| 602 | + $type ? $type->allowsNull() : true, |
| 603 | + $collectionValueType, |
| 604 | + ); |
| 605 | + } |
596 | 606 | } |
0 commit comments