diff --git a/src/Metadata/MetadataFactory.php b/src/Metadata/MetadataFactory.php index 1854804..9f298e0 100644 --- a/src/Metadata/MetadataFactory.php +++ b/src/Metadata/MetadataFactory.php @@ -37,19 +37,21 @@ public function buildMetadata(string $className): ClassMetadata throw new SerializerException('Cannot create reflection for ' . $className, 0, $exception); } + $propertyMetadata = $this->buildPropertyMetadata($reflection); + return new ClassMetadata( array_merge( - $this->buildPropertyMetadata($reflection), + $propertyMetadata, $this->buildMethodMetadata($reflection) ), - $this->buildConstructorMetadata($reflection), + $this->buildConstructorMetadata($propertyMetadata, $reflection), ); } /** * @throws SerializerException */ - private function buildConstructorMetadata(ReflectionClass $reflection): array + private function buildConstructorMetadata(array $propertyMetadata, ReflectionClass $reflection): array { $constructorMethod = $reflection->getConstructor(); if (null === $constructorMethod) { @@ -64,12 +66,8 @@ private function buildConstructorMetadata(ReflectionClass $reflection): array $metadata = []; foreach ($constructorMethod->getParameters() as $parameter) { - if ($parameter->isDefaultValueAvailable()) { - // we will use only the required parameters - continue; - } - $attribute = $this->findRelatedClassPropertyAttribute($reflection, $parameter->getName()); + if (null === $attribute) { // accept if the constructor has a property that should not be serialized // because the object may only be used for serialization and not deserialization @@ -77,6 +75,17 @@ private function buildConstructorMetadata(ReflectionClass $reflection): array } $dataName = $attribute->serializedName ?? $parameter->getName(); + $propertyMeta = $propertyMetadata[$dataName] ?? null; + if ($parameter->isPromoted() && $propertyMeta) { + $metadata[$dataName] = $propertyMeta; + + continue; + } + + if ($parameter->isDefaultValueAvailable()) { + // we will use only the required parameters + continue; + } $metadata[$dataName] = new Metadata( (string) $parameter->getType(), @@ -89,7 +98,7 @@ private function buildConstructorMetadata(ReflectionClass $reflection): array $attribute->strategy, $attribute->persistedName, $attribute->discriminatorMap, - orderBy: $attribute->orderBy + orderBy: $attribute->orderBy, ); } @@ -215,6 +224,13 @@ private function getPropertyMetadata(ReflectionProperty $property, Serialize $at $setter = $property->getName(); } } + if ($property->isPublic()) { + $getterSetterStrategy = false; + $getter = $setter = $property->getName(); + if (version_compare(PHP_VERSION, '8.4.0', '>=') && $property->isPrivateSet()) { + $setter = null; + } + } if ($getterSetterStrategy) { $getter = $getterPrefix . ucfirst($property->getName()); $declaringClass = $property->getDeclaringClass(); diff --git a/src/OpenApi/SerializerModelDescriber.php b/src/OpenApi/SerializerModelDescriber.php index a0d9269..f95d036 100644 --- a/src/OpenApi/SerializerModelDescriber.php +++ b/src/OpenApi/SerializerModelDescriber.php @@ -73,7 +73,10 @@ public function describe(Model $model, Schema $schema): void // Method docBlock description. if (null === $metadata->setter && null !== $model->getType()->getClassName()) { /** @psalm-suppress ArgumentTypeCoercion */ - $methodReflection = new ReflectionMethod($model->getType()->getClassName(), $metadata->getter); + $methodReflection = $metadata->getterSetterStrategy + ? new ReflectionMethod($model->getType()->getClassName(), $metadata->getter) + : new ReflectionProperty($model->getType()->getClassName(), $metadata->getter) + ; $this->addDocBlockDescription($methodReflection, $property); }