From c19db4d5508375aab3afd3947b6f76827603898f Mon Sep 17 00:00:00 2001 From: matheo Date: Tue, 5 Sep 2023 18:18:28 +0200 Subject: [PATCH] use LiveComponentMetadataFactory logic to generate LivePropMetadata --- .../LiveComponentExtension.php | 1 + .../src/LiveComponentHydrator.php | 82 +++++++++++-------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php b/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php index 942233851aa..8c9dad62a07 100644 --- a/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php +++ b/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php @@ -98,6 +98,7 @@ function (ChildDefinition $definition, AsLiveComponent $attribute) { ->setArguments([ tagged_iterator(LiveComponentBundle::HYDRATION_EXTENSION_TAG), new Reference('property_accessor'), + new Reference('property_info'), new Reference('serializer'), '%kernel.secret%', ]) diff --git a/src/LiveComponent/src/LiveComponentHydrator.php b/src/LiveComponent/src/LiveComponentHydrator.php index 62ffa13f40a..20da730d2f5 100644 --- a/src/LiveComponent/src/LiveComponentHydrator.php +++ b/src/LiveComponent/src/LiveComponentHydrator.php @@ -16,7 +16,7 @@ use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; -use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; +use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -47,6 +47,7 @@ final class LiveComponentHydrator public function __construct( private iterable $hydrationExtensions, private PropertyAccessorInterface $propertyAccessor, + private PropertyTypeExtractorInterface $propertyTypeExtractor, private NormalizerInterface|DenormalizerInterface $normalizer, private string $secret ) { @@ -343,22 +344,15 @@ private function dehydrateValue(mixed $value, LivePropMetadata $propMetadata, ob } if (\is_array($value)) { - if ($propMetadata->collectionValueType() && Type::BUILTIN_TYPE_OBJECT === $propMetadata->collectionValueType()->getBuiltinType()) { - $collectionClass = $propMetadata->collectionValueType()->getClassName(); - foreach ($value as $key => $objectItem) { - if (!$objectItem instanceof $collectionClass) { - 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))); - } - - $value[$key] = $this->dehydrateObjectValue($objectItem, $collectionClass, $propMetadata->getFormat(), $component::class, sprintf('%s.%s', $propMetadata->getName(), $key)); + foreach ($value as $key => $objectItem) { + $type = \gettype($objectItem); + if ('object' === $type) { + $type = \get_class($objectItem); } - } - if (!$this->isValueValidDehydratedValue($value)) { - $badKeys = $this->getNonScalarKeys($value, $propMetadata->getName()); - $badKeysText = implode(', ', array_map(fn ($key) => sprintf('%s: %s', $key, $badKeys[$key]), array_keys($badKeys))); + $propMetadata = new LivePropMetadata($key, new LiveProp(true), $type, false, true, null); - 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)); + $value[$key] = $this->dehydrateValue($objectItem, $propMetadata, $component); } return $value; @@ -394,19 +388,11 @@ private function dehydrateObjectValue(object $value, string $classType, ?string } } - $reflexionExtractor = new ReflectionExtractor(); - $properties = $reflexionExtractor->getProperties($classType); $propertiesValues = []; - foreach ($properties as $property) { - if ($reflexionExtractor->isReadable($classType, $property)) { - $propertyValue = $this->propertyAccessor->getValue($value, $property); - $type = $reflexionExtractor->getTypes($classType, $property)[0]->getBuiltinType(); - if ($type === 'object') { - $type = $reflexionExtractor->getTypes($classType, $property)[0]->getClassName(); - } - $propMetadata = new LivePropMetadata($property, new LiveProp(true), $type, false, true, null); - $propertiesValues[$property] = $this->dehydrateValue($propertyValue, $propMetadata, $component); - } + foreach ((new \ReflectionClass($classType))->getProperties() as $property) { + $propertyValue = $this->propertyAccessor->getValue($value, $property->getName()); + $propMetadata = $this->generateLivePropMetadata($classType, $property->getName()); + $propertiesValues[$property->getName()] = $this->dehydrateValue($propertyValue, $propMetadata, $component); } return $propertiesValues; @@ -484,17 +470,10 @@ private function hydrateObjectValue(mixed $value, string $className, bool $allow } } - if (is_array($value)) { - $object = new $className; - $extractor = new ReflectionExtractor(); + if (\is_array($value)) { + $object = new $className(); foreach ($value as $property => $propertyValue) { - $type = $extractor->getTypes($className, $property)[0]->getBuiltinType(); - $buildIn = true; - if ($type === 'object') { - $type = $extractor->getTypes($className, $property)[0]->getClassName(); - $buildIn = false; - } - $propMetadata = new LivePropMetadata($property, new LiveProp(true), $type, $buildIn, true, null); + $propMetadata = $this->generateLivePropMetadata($className, $property); $this->propertyAccessor->setValue($object, $property, $this->hydrateValue($propertyValue, $propMetadata, $component)); } @@ -593,4 +572,35 @@ private function recursiveKeySort(array &$data): void } ksort($data); } + + private function generateLivePropMetadata(string $className, string $propertyName): LivePropMetadata + { + $reflexionClass = new \ReflectionClass($className); + $property = $reflexionClass->getProperty($propertyName); + + $collectionValueType = null; + $infoTypes = $this->propertyTypeExtractor->getTypes($className, $propertyName) ?? []; + foreach ($infoTypes as $infoType) { + if ($infoType->isCollection()) { + foreach ($infoType->getCollectionValueTypes() as $valueType) { + $collectionValueType = $valueType; + break; + } + } + } + + $type = $property->getType(); + if ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) { + 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())); + } + + return new LivePropMetadata( + $property->getName(), + new LiveProp(true), + $type ? $type->getName() : null, + $type ? $type->isBuiltin() : false, + $type ? $type->allowsNull() : true, + $collectionValueType, + ); + } }