diff --git a/.github/expected-missing-return-types.diff b/.github/expected-missing-return-types.diff index a3ee92a47b773..2a605edc61415 100644 --- a/.github/expected-missing-return-types.diff +++ b/.github/expected-missing-return-types.diff @@ -8130,6 +8130,7 @@ index 1924b1ddb0..62c58c8e8b 100644 $annotatedClasses = []; diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php index dff3e248ae..381db9aa8f 100644 +index 6e00840c7e..8e69c81c23 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/ControllerArgumentValueResolverPass.php @@ -34,5 +34,5 @@ class ControllerArgumentValueResolverPass implements CompilerPassInterface @@ -11254,24 +11255,24 @@ index fc6336ebdb..e13a834930 100644 { if (1 > \func_num_args()) { diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php -index 52e985815b..e7d0493152 100644 +index 7d138b0b26..03e28f9d20 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php -@@ -210,5 +210,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn +@@ -215,5 +215,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn * @throws LogicException if the 'allow_extra_attributes' context variable is false and no class metadata factory is provided */ - protected function getAllowedAttributes(string|object $classOrObject, array $context, bool $attributesAsString = false) + protected function getAllowedAttributes(string|object $classOrObject, array $context, bool $attributesAsString = false): array|bool { $allowExtraAttributes = $context[self::ALLOW_EXTRA_ATTRIBUTES] ?? $this->defaultContext[self::ALLOW_EXTRA_ATTRIBUTES]; -@@ -260,5 +260,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn +@@ -265,5 +265,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn * @return bool */ - protected function isAllowedAttribute(object|string $classOrObject, string $attribute, string $format = null, array $context = []) + protected function isAllowedAttribute(object|string $classOrObject, string $attribute, string $format = null, array $context = []): bool { $ignoredAttributes = $context[self::IGNORED_ATTRIBUTES] ?? $this->defaultContext[self::IGNORED_ATTRIBUTES]; -@@ -311,5 +311,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn +@@ -316,5 +316,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn * @throws MissingConstructorArgumentException */ - protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, string $format = null) @@ -11279,7 +11280,7 @@ index 52e985815b..e7d0493152 100644 { if (null !== $object = $this->extractObjectToPopulate($class, $context, self::OBJECT_TO_POPULATE)) { diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php -index a02a46b941..aedfd67c2e 100644 +index 75fe3a5cb1..a28dd40568 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -139,10 +139,10 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer @@ -11321,46 +11322,36 @@ index a02a46b941..aedfd67c2e 100644 - public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */) + public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */): bool { - return class_exists($type) || (interface_exists($type, false) && $this->classDiscriminatorResolver && null !== $this->classDiscriminatorResolver->getMappingForClass($type)); + return class_exists($type) || (interface_exists($type, false) && null !== $this->classDiscriminatorResolver?->getMappingForClass($type)); } - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if (!isset($context['cache_key'])) { -diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerAwareTrait.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerAwareTrait.php -index c5cc86ecf6..c65534fafb 100644 ---- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerAwareTrait.php -+++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerAwareTrait.php -@@ -25,5 +25,5 @@ trait DenormalizerAwareTrait - * @return void - */ -- public function setDenormalizer(DenormalizerInterface $denormalizer) -+ public function setDenormalizer(DenormalizerInterface $denormalizer): void - { - $this->denormalizer = $denormalizer; diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php -index 1786d6fff1..04a2e62ed2 100644 +index 1d83b2da11..1c632f42bf 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php -@@ -45,5 +45,5 @@ interface DenormalizerInterface +@@ -47,5 +47,5 @@ interface DenormalizerInterface * @throws ExceptionInterface Occurs for all the other cases of errors */ - public function denormalize(mixed $data, string $type, string $format = null, array $context = []); + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed; /** -@@ -57,4 +57,4 @@ interface DenormalizerInterface +@@ -64,5 +64,5 @@ interface DenormalizerInterface * @return bool */ - public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */); + public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */): bool; - } + + /** diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php -index e08dd5d9ec..cc282ae4bb 100644 +index 2719c8b52c..1112f7f3cc 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php -@@ -138,5 +138,5 @@ class GetSetMethodNormalizer extends AbstractObjectNormalizer +@@ -148,5 +148,5 @@ class GetSetMethodNormalizer extends AbstractObjectNormalizer * @return void */ - protected function setAttributeValue(object $object, string $attribute, mixed $value, string $format = null, array $context = []) @@ -11379,27 +11370,28 @@ index 40a4fa0e8c..a1e2749aae 100644 { $this->normalizer = $normalizer; diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php -index cb43d78cc7..d215ffe997 100644 +index d6d0707ff5..9953ad3005 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php -@@ -37,5 +37,5 @@ interface NormalizerInterface +@@ -39,5 +39,5 @@ interface NormalizerInterface * @throws ExceptionInterface Occurs for all the other cases of errors */ - public function normalize(mixed $object, string $format = null, array $context = []); + public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null; /** -@@ -48,4 +48,4 @@ interface NormalizerInterface +@@ -55,5 +55,5 @@ interface NormalizerInterface * @return bool */ - public function supportsNormalization(mixed $data, string $format = null /* , array $context = [] */); + public function supportsNormalization(mixed $data, string $format = null /* , array $context = [] */): bool; - } + + /** diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php -index 8018cb7a49..aa06b9c50b 100644 +index 140e89c6a1..f77348252b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php -@@ -133,5 +133,5 @@ class ObjectNormalizer extends AbstractObjectNormalizer +@@ -143,5 +143,5 @@ class ObjectNormalizer extends AbstractObjectNormalizer * @return void */ - protected function setAttributeValue(object $object, string $attribute, mixed $value, string $format = null, array $context = []) @@ -11407,10 +11399,10 @@ index 8018cb7a49..aa06b9c50b 100644 { try { diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php -index 3dd734055d..cbc0e86d27 100644 +index 645ba74290..d960bf4b20 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php -@@ -175,5 +175,5 @@ class PropertyNormalizer extends AbstractObjectNormalizer +@@ -185,5 +185,5 @@ class PropertyNormalizer extends AbstractObjectNormalizer * @return void */ - protected function setAttributeValue(object $object, string $attribute, mixed $value, string $format = null, array $context = []) diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/Normalizer/FlattenExceptionNormalizer.php b/src/Symfony/Component/Messenger/Transport/Serialization/Normalizer/FlattenExceptionNormalizer.php index eb673c1211fcc..ac30b09df925a 100644 --- a/src/Symfony/Component/Messenger/Transport/Serialization/Normalizer/FlattenExceptionNormalizer.php +++ b/src/Symfony/Component/Messenger/Transport/Serialization/Normalizer/FlattenExceptionNormalizer.php @@ -45,6 +45,13 @@ public function normalize(mixed $object, string $format = null, array $context = return $normalized; } + public function getSupportedTypes(?string $format): array + { + return [ + FlattenException::class => false, + ]; + } + public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool { return $data instanceof FlattenException && ($context[Serializer::MESSENGER_SERIALIZATION_CONTEXT] ?? false); diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 12269eda7ff4f..99f68f71f931b 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -6,7 +6,9 @@ CHANGELOG * Add `XmlEncoder::SAVE_OPTIONS` context option * Add `BackedEnumNormalizer::ALLOW_INVALID_VALUES` context option + * Add method `getSupportedTypes(?string $format)` to `NormalizerInterface` and `DenormalizerInterface` * Deprecate `MissingConstructorArgumentsException` in favor of `MissingConstructorArgumentException` + * Deprecate `CacheableSupportsMethodInterface` in favor of the new `getSupportedTypes(?string $format)` methods 6.2 --- diff --git a/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php b/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php index ca41fc6a83f07..d4cf6fcbd2496 100644 --- a/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php +++ b/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php @@ -33,6 +33,19 @@ public function __construct( private NormalizerInterface|DenormalizerInterface $normalizer, private SerializerDataCollector $dataCollector, ) { + if (!method_exists($normalizer, 'getSupportedTypes')) { + trigger_deprecation('symfony/serializer', '6.3', 'Not implementing the "NormalizerInterface::getSupportedTypes()" in "%s" is deprecated.', get_debug_type($normalizer)); + } + } + + public function getSupportedTypes(?string $format): array + { + // @deprecated remove condition in 7.0 + if (!method_exists($this->normalizer, 'getSupportedTypes')) { + return ['*' => $this->normalizer instanceof CacheableSupportsMethodInterface && $this->normalizer->hasCacheableSupportsMethod()]; + } + + return $this->normalizer->getSupportedTypes($format); } public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null @@ -114,8 +127,13 @@ public function setDenormalizer(DenormalizerInterface $denormalizer): void $this->normalizer->setDenormalizer($denormalizer); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return $this->normalizer instanceof CacheableSupportsMethodInterface && $this->normalizer->hasCacheableSupportsMethod(); } diff --git a/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php b/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php index 01ae795841937..2a8e96c7af833 100644 --- a/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php +++ b/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php @@ -14,6 +14,7 @@ use Symfony\Component\Serializer\DataCollector\SerializerDataCollector; use Symfony\Component\Serializer\Encoder\DecoderInterface; use Symfony\Component\Serializer\Encoder\EncoderInterface; +use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -29,13 +30,13 @@ class TraceableSerializer implements SerializerInterface, NormalizerInterface, D { public const DEBUG_TRACE_ID = 'debug_trace_id'; - /** - * @param SerializerInterface&NormalizerInterface&DenormalizerInterface&EncoderInterface&DecoderInterface $serializer - */ public function __construct( - private SerializerInterface $serializer, + private SerializerInterface&NormalizerInterface&DenormalizerInterface&EncoderInterface&DecoderInterface $serializer, private SerializerDataCollector $dataCollector, ) { + if (!method_exists($serializer, 'getSupportedTypes')) { + trigger_deprecation('symfony/serializer', '6.3', 'Not implementing the "NormalizerInterface::getSupportedTypes()" in "%s" is deprecated.', get_debug_type($serializer)); + } } public function serialize(mixed $data, string $format, array $context = []): string @@ -128,6 +129,16 @@ public function decode(string $data, string $format, array $context = []): mixed return $result; } + public function getSupportedTypes(?string $format): array + { + // @deprecated remove condition in 7.0 + if (!method_exists($this->serializer, 'getSupportedTypes')) { + return ['*' => $this->serializer instanceof CacheableSupportsMethodInterface && $this->serializer->hasCacheableSupportsMethod()]; + } + + return $this->serializer->getSupportedTypes($format); + } + public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool { return $this->serializer->supportsNormalization($data, $format, $context); diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 52e985815bf99..7d138b0b263f5 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -150,8 +150,13 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory } } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return false; } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index a02a46b9415a6..75fe3a5cb180b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -300,7 +300,7 @@ abstract protected function getAttributeValue(object $object, string $attribute, */ public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */) { - return class_exists($type) || (interface_exists($type, false) && $this->classDiscriminatorResolver && null !== $this->classDiscriminatorResolver->getMappingForClass($type)); + return class_exists($type) || (interface_exists($type, false) && null !== $this->classDiscriminatorResolver?->getMappingForClass($type)); } public function denormalize(mixed $data, string $type, string $format = null, array $context = []) diff --git a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php index a88beba7ab6c6..8b36935081f30 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php @@ -27,6 +27,25 @@ class ArrayDenormalizer implements ContextAwareDenormalizerInterface, Denormaliz { use DenormalizerAwareTrait; + public function setDenormalizer(DenormalizerInterface $denormalizer): void + { + if (!method_exists($denormalizer, 'getSupportedTypes')) { + trigger_deprecation('symfony/serializer', '6.3', 'Not implementing the "DenormalizerInterface::getSupportedTypes()" in "%s" is deprecated.', get_debug_type($denormalizer)); + } + + $this->denormalizer = $denormalizer; + } + + public function getSupportedTypes(?string $format): array + { + // @deprecated remove condition in 7.0 + if (!method_exists($this->denormalizer, 'getSupportedTypes')) { + return ['*' => $this->denormalizer instanceof CacheableSupportsMethodInterface && $this->denormalizer->hasCacheableSupportsMethod()]; + } + + return $this->denormalizer->getSupportedTypes($format); + } + /** * @throws NotNormalizableValueException */ @@ -69,8 +88,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma && $this->denormalizer->supportsDenormalization($data, substr($type, 0, -2), $format, $context); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return $this->denormalizer instanceof CacheableSupportsMethodInterface && $this->denormalizer->hasCacheableSupportsMethod(); } } diff --git a/src/Symfony/Component/Serializer/Normalizer/BackedEnumNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/BackedEnumNormalizer.php index 2281849ad20a4..32ee831c8c2df 100644 --- a/src/Symfony/Component/Serializer/Normalizer/BackedEnumNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/BackedEnumNormalizer.php @@ -27,6 +27,13 @@ final class BackedEnumNormalizer implements NormalizerInterface, DenormalizerInt */ public const ALLOW_INVALID_VALUES = 'allow_invalid_values'; + public function getSupportedTypes(?string $format): array + { + return [ + \BackedEnum::class => true, + ]; + } + public function normalize(mixed $object, string $format = null, array $context = []): int|string { if (!$object instanceof \BackedEnum) { @@ -78,8 +85,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return is_subclass_of($type, \BackedEnum::class); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return true; } } diff --git a/src/Symfony/Component/Serializer/Normalizer/CacheableSupportsMethodInterface.php b/src/Symfony/Component/Serializer/Normalizer/CacheableSupportsMethodInterface.php index 3a55f653b1786..ea2df15b2c035 100644 --- a/src/Symfony/Component/Serializer/Normalizer/CacheableSupportsMethodInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/CacheableSupportsMethodInterface.php @@ -19,6 +19,8 @@ * supports*() methods will be cached by type and format. * * @author Kévin Dunglas + * + * @deprecated since Symfony 6.3, implement "getSupportedTypes(?string $format)" instead */ interface CacheableSupportsMethodInterface { diff --git a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php index 0300bd8478750..0e23d90b304d4 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php @@ -39,6 +39,13 @@ public function __construct(array $defaultContext = [], NameConverterInterface $ $this->nameConverter = $nameConverter; } + public function getSupportedTypes(?string $format): array + { + return [ + ConstraintViolationListInterface::class => __CLASS__ === static::class || $this->hasCacheableSupportsMethod(), + ]; + } + public function normalize(mixed $object, string $format = null, array $context = []): array { if (\array_key_exists(self::PAYLOAD_FIELDS, $context)) { @@ -109,8 +116,13 @@ public function supportsNormalization(mixed $data, string $format = null /* , ar return $data instanceof ConstraintViolationListInterface; } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } } diff --git a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php index c498b19353082..4c17c2552a069 100644 --- a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php @@ -22,6 +22,13 @@ class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, Se use ObjectToPopulateTrait; use SerializerAwareTrait; + public function getSupportedTypes(?string $format): array + { + return [ + NormalizableInterface::class => __CLASS__ === static::class || $this->hasCacheableSupportsMethod(), + ]; + } + public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { return $object->normalize($this->serializer, $format, $context); @@ -60,8 +67,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return is_subclass_of($type, DenormalizableInterface::class); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } } diff --git a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php index b0cecd9e87785..9012f6f3aade2 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php @@ -45,6 +45,17 @@ public function __construct(MimeTypeGuesserInterface $mimeTypeGuesser = null) $this->mimeTypeGuesser = $mimeTypeGuesser; } + public function getSupportedTypes(?string $format): array + { + $isCacheable = __CLASS__ === static::class || $this->hasCacheableSupportsMethod(); + + return [ + \SplFileInfo::class => $isCacheable, + \SplFileObject::class => $isCacheable, + File::class => $isCacheable, + ]; + } + public function normalize(mixed $object, string $format = null, array $context = []): string { if (!$object instanceof \SplFileInfo) { @@ -118,8 +129,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return isset(self::SUPPORTED_TYPES[$type]); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } diff --git a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php index bff7bcabbd2c3..616d500e76386 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php @@ -33,6 +33,13 @@ public function __construct(array $defaultContext = []) $this->defaultContext = array_merge($this->defaultContext, $defaultContext); } + public function getSupportedTypes(?string $format): array + { + return [ + \DateInterval::class => __CLASS__ === static::class || $this->hasCacheableSupportsMethod(), + ]; + } + /** * @throws InvalidArgumentException */ @@ -53,8 +60,13 @@ public function supportsNormalization(mixed $data, string $format = null /* , ar return $data instanceof \DateInterval; } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index 715a56d5b3626..2235b1ea504b6 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -47,6 +47,17 @@ public function setDefaultContext(array $defaultContext): void $this->defaultContext = array_merge($this->defaultContext, $defaultContext); } + public function getSupportedTypes(?string $format): array + { + $isCacheable = __CLASS__ === static::class || $this->hasCacheableSupportsMethod(); + + return [ + \DateTimeInterface::class => $isCacheable, + \DateTimeImmutable::class => $isCacheable, + \DateTime::class => $isCacheable, + ]; + } + /** * @throws InvalidArgumentException */ @@ -124,8 +135,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return isset(self::SUPPORTED_TYPES[$type]); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php index 6309ce335b71a..a7c26f1d490f6 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php @@ -22,6 +22,13 @@ */ class DateTimeZoneNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface { + public function getSupportedTypes(?string $format): array + { + return [ + \DateTimeZone::class => __CLASS__ === static::class || $this->hasCacheableSupportsMethod(), + ]; + } + /** * @throws InvalidArgumentException */ @@ -66,8 +73,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return \DateTimeZone::class === $type; } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } } diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php index 1786d6fff1faa..1d83b2da1166d 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php @@ -21,6 +21,8 @@ /** * @author Jordi Boggiano + * + * @method getSupportedTypes(?string $format): array */ interface DenormalizerInterface { @@ -49,12 +51,33 @@ public function denormalize(mixed $data, string $type, string $format = null, ar /** * Checks whether the given class is supported for denormalization by this normalizer. * - * @param mixed $data Data to denormalize from - * @param string $type The class to which the data should be denormalized - * @param string|null $format The format being deserialized from + * Since Symfony 6.3, this method will only be called if the type is + * included in the supported types returned by getSupportedTypes(). + * + * @see getSupportedTypes() + * + * @param mixed $data Data to denormalize from + * @param string $type The class to which the data should be denormalized + * @param string|null $format The format being deserialized from * @param array $context Options available to the denormalizer * * @return bool */ public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */); + + /** + * Returns the types potentially supported by this denormalizer. + * + * For each supported formats (if applicable), the supported types should be + * returned as keys, and each type should be mapped to a boolean indicating + * if the result of supportsDenormalization() can be cached or not + * (a result cannot be cached when it depends on the context or on the data.) + * + * The special type '*' can be used to indicate that the denormalizer might + * support any types. A null value means that the denormalizer does not support + * the corresponding type. + * + * @return array + */ + /* public function getSupportedTypes(?string $format): array; */ } diff --git a/src/Symfony/Component/Serializer/Normalizer/FormErrorNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/FormErrorNormalizer.php index a770e254c821a..77f17639ae15a 100644 --- a/src/Symfony/Component/Serializer/Normalizer/FormErrorNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/FormErrorNormalizer.php @@ -38,6 +38,13 @@ public function normalize(mixed $object, string $format = null, array $context = return $data; } + public function getSupportedTypes(?string $format): array + { + return [ + FormInterface::class => false, + ]; + } + public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool { return $data instanceof FormInterface && $data->isSubmitted() && !$data->isValid(); @@ -76,8 +83,13 @@ private function convertFormChildrenToArray(FormInterface $data): array return $children; } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } } diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index e08dd5d9ecdaa..2719c8b52c16e 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -36,6 +36,11 @@ class GetSetMethodNormalizer extends AbstractObjectNormalizer { private static $setterAccessibleCache = []; + public function getSupportedTypes(?string $format): array + { + return ['*' => __CLASS__ === static::class || $this->hasCacheableSupportsMethod()]; + } + /** * @param array $context */ @@ -52,8 +57,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return parent::supportsDenormalization($data, $type, $format) && $this->supports($type); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } diff --git a/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php index b81385bc8eb75..841fca3196537 100644 --- a/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php @@ -38,6 +38,13 @@ public function normalize(mixed $object, string $format = null, array $context = return $this->serializer->normalize($object->jsonSerialize(), $format, $context); } + public function getSupportedTypes(?string $format): array + { + return [ + \JsonSerializable::class => __CLASS__ === static::class || $this->hasCacheableSupportsMethod(), + ]; + } + /** * @param array $context */ @@ -59,8 +66,13 @@ public function denormalize(mixed $data, string $type, string $format = null, ar throw new LogicException(sprintf('Cannot denormalize with "%s".', \JsonSerializable::class)); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } } diff --git a/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php index a442fc54d5aaf..74a8b66b10100 100644 --- a/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php @@ -42,6 +42,19 @@ public function __construct(PropertyNormalizer $normalizer) $this->headersProperty = new \ReflectionProperty(Headers::class, 'headers'); } + public function getSupportedTypes(?string $format): array + { + $isCacheable = __CLASS__ === static::class || $this->hasCacheableSupportsMethod(); + + return [ + Message::class => $isCacheable, + Headers::class => $isCacheable, + HeaderInterface::class => $isCacheable, + Address::class => $isCacheable, + AbstractPart::class => $isCacheable, + ]; + } + public function setSerializer(SerializerInterface $serializer): void { $this->serializer = $serializer; @@ -101,8 +114,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return is_a($type, Message::class, true) || Headers::class === $type || AbstractPart::class === $type; } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } } diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php index cb43d78cc7650..d6d0707ff5ec8 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php @@ -18,6 +18,8 @@ /** * @author Jordi Boggiano + * + * @method getSupportedTypes(?string $format): array */ interface NormalizerInterface { @@ -41,11 +43,32 @@ public function normalize(mixed $object, string $format = null, array $context = /** * Checks whether the given class is supported for normalization by this normalizer. * + * Since Symfony 6.3, this method will only be called if the $data type is + * included in the supported types returned by getSupportedTypes(). + * + * @see getSupportedTypes() + * * @param mixed $data Data to normalize - * @param string|null $format The format being (de-)serialized from or into + * @param string|null $format The format being (de-)serialized from or into * @param array $context Context options for the normalizer * * @return bool */ public function supportsNormalization(mixed $data, string $format = null /* , array $context = [] */); + + /** + * Returns the types potentially supported by this normalizer. + * + * For each supported formats (if applicable), the supported types should be + * returned as keys, and each type should be mapped to a boolean indicating + * if the result of supportsNormalization() can be cached or not + * (a result cannot be cached when it depends on the context or on the data.) + * + * The special type '*' can be used to indicate that the normalizer might + * support any types. A null value means that the normalizer does not support + * the corresponding type. + * + * @return array + */ + /* public function getSupportedTypes(?string $format): array; */ } diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index 8018cb7a49bb6..140e89c6a13b1 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -47,8 +47,18 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory $this->objectClassResolver = $objectClassResolver ?? fn ($class) => \is_object($class) ? $class::class : $class; } + public function getSupportedTypes(?string $format): array + { + return ['*' => __CLASS__ === static::class || $this->hasCacheableSupportsMethod()]; + } + + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } diff --git a/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php index 59b8427ab781f..dc4dadd88baa3 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php @@ -40,6 +40,13 @@ public function __construct(bool $debug = false, array $defaultContext = []) $this->defaultContext = $defaultContext + $this->defaultContext; } + public function getSupportedTypes(?string $format): array + { + return [ + FlattenException::class => __CLASS__ === self::class || $this->hasCacheableSupportsMethod(), + ]; + } + public function normalize(mixed $object, string $format = null, array $context = []): array { if (!$object instanceof FlattenException) { @@ -71,8 +78,13 @@ public function supportsNormalization(mixed $data, string $format = null /* , ar return $data instanceof FlattenException; } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return true; } } diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index 3dd734055d92d..645ba74290249 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -54,6 +54,11 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory } } + public function getSupportedTypes(?string $format): array + { + return ['*' => __CLASS__ === static::class || $this->hasCacheableSupportsMethod()]; + } + /** * @param array $context */ @@ -70,8 +75,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return parent::supportsDenormalization($data, $type, $format) && $this->supports($type); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } diff --git a/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php index ea86f37542383..e723ec3f2dadb 100644 --- a/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php @@ -41,6 +41,13 @@ public function __construct(array $defaultContext = []) $this->defaultContext = array_merge($this->defaultContext, $defaultContext); } + public function getSupportedTypes(?string $format): array + { + return [ + AbstractUid::class => true, + ]; + } + /** * @param AbstractUid $object */ @@ -92,8 +99,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return is_subclass_of($type, AbstractUid::class, true); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return __CLASS__ === static::class; } } diff --git a/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php index 2eff8b77594d4..738ece32e20f9 100644 --- a/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php @@ -32,6 +32,11 @@ public function __construct(PropertyAccessorInterface $propertyAccessor = null) $this->propertyAccessor = $propertyAccessor ?? PropertyAccess::createPropertyAccessor(); } + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + public function denormalize(mixed $data, string $class, string $format = null, array $context = []): mixed { $propertyPath = $context[self::UNWRAP_PATH]; @@ -53,8 +58,13 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return \array_key_exists(self::UNWRAP_PATH, $context) && !isset($context['unwrapped']); } + /** + * @deprecated since Symfony 6.3, use "getSupportedTypes()" instead + */ public function hasCacheableSupportsMethod(): bool { + trigger_deprecation('symfony/serializer', '6.3', 'The "%s()" method is deprecated, use "getSupportedTypes()" instead.', __METHOD__); + return $this->serializer instanceof CacheableSupportsMethodInterface && $this->serializer->hasCacheableSupportsMethod(); } } diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index ab888592ec7f4..5eb0c5b6b2e2e 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -227,6 +227,11 @@ public function denormalize(mixed $data, string $type, string $format = null, ar return $normalizer->denormalize($data, $type, $format, $context); } + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool { return null !== $this->getNormalizer($data, $format, $context); @@ -256,10 +261,40 @@ private function getNormalizer(mixed $data, ?string $format, array $context): ?N continue; } - if (!$normalizer instanceof CacheableSupportsMethodInterface || !$normalizer->hasCacheableSupportsMethod()) { - $this->normalizerCache[$format][$type][$k] = false; - } elseif ($normalizer->supportsNormalization($data, $format, $context)) { - $this->normalizerCache[$format][$type][$k] = true; + if (!method_exists($normalizer, 'getSupportedTypes')) { + trigger_deprecation('symfony/serializer', '6.3', '"%s" should implement "NormalizerInterface::getSupportedTypes(?string $format): array".', $normalizer::class); + + if (!$normalizer instanceof CacheableSupportsMethodInterface || !$normalizer->hasCacheableSupportsMethod()) { + $this->normalizerCache[$format][$type][$k] = false; + } elseif ($normalizer->supportsNormalization($data, $format, $context)) { + $this->normalizerCache[$format][$type][$k] = true; + break; + } + + continue; + } + + $supportedTypes = $normalizer->getSupportedTypes($format); + + foreach ($supportedTypes as $supportedType => $isCacheable) { + if ('*' === $supportedType || $type !== $supportedType && !is_subclass_of($type, $supportedType, true)) { + continue; + } + + if (null === $isCacheable) { + unset($supportedTypes['*']); + } elseif ($this->normalizerCache[$format][$type][$k] = $isCacheable && $normalizer->supportsNormalization($data, $format, $context)) { + break 2; + } + + break; + } + + if (null === $isCacheable = $supportedTypes['*'] ?? null) { + continue; + } + + if ($this->normalizerCache[$format][$type][$k] ??= $isCacheable && $normalizer->supportsNormalization($data, $format, $context)) { break; } } @@ -293,10 +328,40 @@ private function getDenormalizer(mixed $data, string $class, ?string $format, ar continue; } - if (!$normalizer instanceof CacheableSupportsMethodInterface || !$normalizer->hasCacheableSupportsMethod()) { - $this->denormalizerCache[$format][$class][$k] = false; - } elseif ($normalizer->supportsDenormalization(null, $class, $format, $context)) { - $this->denormalizerCache[$format][$class][$k] = true; + if (!method_exists($normalizer, 'getSupportedTypes')) { + trigger_deprecation('symfony/serializer', '6.3', '"%s" should implement "DenormalizerInterface::getSupportedTypes(?string $format): array".', $normalizer::class); + + if (!$normalizer instanceof CacheableSupportsMethodInterface || !$normalizer->hasCacheableSupportsMethod()) { + $this->denormalizerCache[$format][$class][$k] = false; + } elseif ($normalizer->supportsDenormalization(null, $class, $format, $context)) { + $this->denormalizerCache[$format][$class][$k] = true; + break; + } + + continue; + } + + $supportedTypes = $normalizer->getSupportedTypes($format); + + foreach ($supportedTypes as $supportedType => $isCacheable) { + if ('*' === $supportedType || $class !== $supportedType && !is_subclass_of($class, $supportedType, true)) { + continue; + } + + if (null === $isCacheable) { + unset($supportedTypes['*']); + } elseif ($this->denormalizerCache[$format][$class][$k] = $isCacheable && $normalizer->supportsDenormalization(null, $class, $format, $context)) { + break 2; + } + + break; + } + + if (null === $isCacheable = $supportedTypes['*'] ?? null) { + continue; + } + + if ($this->denormalizerCache[$format][$class][$k] ??= $isCacheable && $normalizer->supportsDenormalization(null, $class, $format, $context)) { break; } } diff --git a/src/Symfony/Component/Serializer/Tests/Debug/TraceableNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Debug/TraceableNormalizerTest.php index 5f78ef81f9def..41e3441ed9be9 100644 --- a/src/Symfony/Component/Serializer/Tests/Debug/TraceableNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Debug/TraceableNormalizerTest.php @@ -15,14 +15,15 @@ use Symfony\Component\Serializer\DataCollector\SerializerDataCollector; use Symfony\Component\Serializer\Debug\TraceableNormalizer; use Symfony\Component\Serializer\Debug\TraceableSerializer; -use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; -use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Tests\Fixtures\UpcomingDenormalizerInterface as DenormalizerInterface; +use Symfony\Component\Serializer\Tests\Fixtures\UpcomingNormalizerInterface as NormalizerInterface; class TraceableNormalizerTest extends TestCase { public function testForwardsToNormalizer() { $normalizer = $this->createMock(NormalizerInterface::class); + $normalizer->method('getSupportedTypes')->willReturn(['*' => false]); $normalizer ->expects($this->once()) ->method('normalize') @@ -30,6 +31,7 @@ public function testForwardsToNormalizer() ->willReturn('normalized'); $denormalizer = $this->createMock(DenormalizerInterface::class); + $denormalizer->method('getSupportedTypes')->willReturn(['*' => false]); $denormalizer ->expects($this->once()) ->method('denormalize') @@ -43,7 +45,9 @@ public function testForwardsToNormalizer() public function testCollectNormalizationData() { $normalizer = $this->createMock(NormalizerInterface::class); + $normalizer->method('getSupportedTypes')->willReturn(['*' => false]); $denormalizer = $this->createMock(DenormalizerInterface::class); + $denormalizer->method('getSupportedTypes')->willReturn(['*' => false]); $dataCollector = $this->createMock(SerializerDataCollector::class); $dataCollector @@ -62,7 +66,9 @@ public function testCollectNormalizationData() public function testNotCollectNormalizationDataIfNoDebugTraceId() { $normalizer = $this->createMock(NormalizerInterface::class); + $normalizer->method('getSupportedTypes')->willReturn(['*' => false]); $denormalizer = $this->createMock(DenormalizerInterface::class); + $denormalizer->method('getSupportedTypes')->willReturn(['*' => false]); $dataCollector = $this->createMock(SerializerDataCollector::class); $dataCollector->expects($this->never())->method('collectNormalization'); @@ -89,9 +95,11 @@ public function testCannotDenormalizeIfNotDenormalizer() public function testSupports() { $normalizer = $this->createMock(NormalizerInterface::class); + $normalizer->method('getSupportedTypes')->willReturn(['*' => false]); $normalizer->method('supportsNormalization')->willReturn(true); $denormalizer = $this->createMock(DenormalizerInterface::class); + $denormalizer->method('getSupportedTypes')->willReturn(['*' => false]); $denormalizer->method('supportsDenormalization')->willReturn(true); $traceableNormalizer = new TraceableNormalizer($normalizer, new SerializerDataCollector()); diff --git a/src/Symfony/Component/Serializer/Tests/Debug/TraceableSerializerTest.php b/src/Symfony/Component/Serializer/Tests/Debug/TraceableSerializerTest.php index 00a1ef58a693f..9cc43d40a9a0b 100644 --- a/src/Symfony/Component/Serializer/Tests/Debug/TraceableSerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Debug/TraceableSerializerTest.php @@ -145,6 +145,11 @@ public function normalize(mixed $object, string $format = null, array $context = return 'normalized'; } + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool { return true; diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php index 11e7bef3e5f17..b98447b54b484 100644 --- a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php @@ -942,6 +942,11 @@ private function createMockDateTimeNormalizer(): MockObject&NormalizerInterface ->with(new \DateTime($this->exampleDateTimeString), 'xml', []) ->willReturn($this->exampleDateTimeString); + $mock + ->expects($this->once()) + ->method('getSupportedTypes') + ->willReturn([\DateTime::class => true]); + $mock ->expects($this->once()) ->method('supportsNormalization') diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractNormalizerDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractNormalizerDummy.php index 82586062bf94f..9da1fc5f51e11 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractNormalizerDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/AbstractNormalizerDummy.php @@ -20,6 +20,11 @@ */ class AbstractNormalizerDummy extends AbstractNormalizer { + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + public function getAllowedAttributes(string|object $classOrObject, array $context, bool $attributesAsString = false): array|bool { return parent::getAllowedAttributes($classOrObject, $context, $attributesAsString); diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/EnvelopeNormalizer.php b/src/Symfony/Component/Serializer/Tests/Fixtures/EnvelopeNormalizer.php index 1492d5d0298ec..021c22a04c0ef 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/EnvelopeNormalizer.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/EnvelopeNormalizer.php @@ -31,6 +31,13 @@ public function normalize($envelope, string $format = null, array $context = []) ]; } + public function getSupportedTypes(?string $format): array + { + return [ + EnvelopeObject::class => true, + ]; + } + public function supportsNormalization($data, string $format = null, array $context = []): bool { return $data instanceof EnvelopeObject; diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/EnvelopedMessageNormalizer.php b/src/Symfony/Component/Serializer/Tests/Fixtures/EnvelopedMessageNormalizer.php index dfdec91b1b613..5d48f4569cb0a 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/EnvelopedMessageNormalizer.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/EnvelopedMessageNormalizer.php @@ -25,6 +25,13 @@ public function normalize($message, string $format = null, array $context = []): ]; } + public function getSupportedTypes(?string $format): array + { + return [ + EnvelopedMessage::class => true, + ]; + } + public function supportsNormalization($data, string $format = null, array $context = []): bool { return $data instanceof EnvelopedMessage; diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/UpcomingDenormalizerInterface.php b/src/Symfony/Component/Serializer/Tests/Fixtures/UpcomingDenormalizerInterface.php new file mode 100644 index 0000000000000..2efc12bb6a20a --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/UpcomingDenormalizerInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; + +// @deprecated remove in 7.0 in favor of direct use of the DenormalizerInterface +interface UpcomingDenormalizerInterface extends DenormalizerInterface +{ + public function getSupportedTypes(?string $format): array; +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/UpcomingNormalizerInterface.php b/src/Symfony/Component/Serializer/Tests/Fixtures/UpcomingNormalizerInterface.php new file mode 100644 index 0000000000000..59972bb5e2b8d --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/UpcomingNormalizerInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +// @deprecated remove in 7.0 in favor of direct use of the NormalizerInterface +interface UpcomingNormalizerInterface extends NormalizerInterface +{ + public function getSupportedTypes(?string $format): array; +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index 4cc6686586e89..e1f466b2237e7 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -546,6 +546,11 @@ public function testDenormalizeUsesContextAttributeForPropertiesInConstructorWit class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer { + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + protected function extractAttributes(object $object, string $format = null, array $context = []): array { return []; @@ -651,6 +656,11 @@ public function __construct() parent::__construct($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory)); } + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + protected function extractAttributes(object $object, string $format = null, array $context = []): array { } @@ -752,6 +762,11 @@ public function denormalize($data, string $type, string $format = null, array $c return null; } + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + public function supportsDenormalization($data, string $type, string $format = null, array $context = []): bool { return true; @@ -760,6 +775,11 @@ public function supportsDenormalization($data, string $type, string $format = nu class AbstractObjectNormalizerCollectionDummy extends AbstractObjectNormalizer { + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + protected function extractAttributes(object $object, string $format = null, array $context = []): array { } @@ -814,6 +834,11 @@ public function denormalize($data, string $type, string $format = null, array $c return $data; } + public function getSupportedTypes(?string $format): array + { + return $this->serializer->getSupportedTypes($format); + } + public function supportsDenormalization($data, string $type, string $format = null, array $context = []): bool { return str_ends_with($type, '[]') diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ArrayDenormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ArrayDenormalizerTest.php index 391d3374f6305..8f7a75b116135 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ArrayDenormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ArrayDenormalizerTest.php @@ -14,23 +14,16 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; -use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface; +use Symfony\Component\Serializer\Tests\Fixtures\UpcomingDenormalizerInterface as DenormalizerInterface; class ArrayDenormalizerTest extends TestCase { - /** - * @var ArrayDenormalizer - */ - private $denormalizer; - - /** - * @var MockObject&ContextAwareDenormalizerInterface - */ - private $serializer; + private ArrayDenormalizer $denormalizer; + private MockObject&DenormalizerInterface $serializer; protected function setUp(): void { - $this->serializer = $this->createMock(ContextAwareDenormalizerInterface::class); + $this->serializer = $this->createMock(DenormalizerInterface::class); $this->denormalizer = new ArrayDenormalizer(); $this->denormalizer->setDenormalizer($this->serializer); } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/TestDenormalizer.php b/src/Symfony/Component/Serializer/Tests/Normalizer/TestDenormalizer.php index ee9f2da0ecc09..70518c797951d 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/TestDenormalizer.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/TestDenormalizer.php @@ -24,6 +24,11 @@ public function denormalize($data, string $type, string $format = null, array $c { } + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + public function supportsDenormalization($data, string $type, string $format = null, array $context = []): bool { return true; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/TestNormalizer.php b/src/Symfony/Component/Serializer/Tests/Normalizer/TestNormalizer.php index 5d941e7a52915..26e5917e72ed5 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/TestNormalizer.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/TestNormalizer.php @@ -25,6 +25,11 @@ public function normalize($object, string $format = null, array $context = []): return null; } + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + public function supportsNormalization($data, string $format = null, array $context = []): bool { return true; diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index fa06773fbf3e1..331eaa25ea44d 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -42,10 +42,8 @@ use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\DateTimeZoneNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; -use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; -use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; use Symfony\Component\Serializer\Normalizer\UidNormalizer; @@ -66,22 +64,13 @@ use Symfony\Component\Serializer\Tests\Fixtures\Php80WithPromotedTypedConstructor; use Symfony\Component\Serializer\Tests\Fixtures\TraversableDummy; use Symfony\Component\Serializer\Tests\Fixtures\TrueBuiltInDummy; +use Symfony\Component\Serializer\Tests\Fixtures\UpcomingDenormalizerInterface as DenormalizerInterface; +use Symfony\Component\Serializer\Tests\Fixtures\UpcomingNormalizerInterface as NormalizerInterface; use Symfony\Component\Serializer\Tests\Normalizer\TestDenormalizer; use Symfony\Component\Serializer\Tests\Normalizer\TestNormalizer; class SerializerTest extends TestCase { - public function testInterface() - { - $serializer = new Serializer(); - - $this->assertInstanceOf(SerializerInterface::class, $serializer); - $this->assertInstanceOf(NormalizerInterface::class, $serializer); - $this->assertInstanceOf(DenormalizerInterface::class, $serializer); - $this->assertInstanceOf(EncoderInterface::class, $serializer); - $this->assertInstanceOf(DecoderInterface::class, $serializer); - } - public function testItThrowsExceptionOnInvalidNormalizer() { $this->expectException(InvalidArgumentException::class); @@ -153,11 +142,13 @@ public function testCustomNormalizerCanNormalizeCollectionsAndScalar() public function testNormalizeWithSupportOnData() { $normalizer1 = $this->createMock(NormalizerInterface::class); + $normalizer1->method('getSupportedTypes')->willReturn(['*' => false]); $normalizer1->method('supportsNormalization') ->willReturnCallback(fn ($data, $format) => isset($data->test)); $normalizer1->method('normalize')->willReturn('test1'); $normalizer2 = $this->createMock(NormalizerInterface::class); + $normalizer2->method('getSupportedTypes')->willReturn(['*' => false]); $normalizer2->method('supportsNormalization') ->willReturn(true); $normalizer2->method('normalize')->willReturn('test2'); @@ -174,11 +165,13 @@ public function testNormalizeWithSupportOnData() public function testDenormalizeWithSupportOnData() { $denormalizer1 = $this->createMock(DenormalizerInterface::class); + $denormalizer1->method('getSupportedTypes')->willReturn(['*' => false]); $denormalizer1->method('supportsDenormalization') ->willReturnCallback(fn ($data, $type, $format) => isset($data['test1'])); $denormalizer1->method('denormalize')->willReturn('test1'); $denormalizer2 = $this->createMock(DenormalizerInterface::class); + $denormalizer2->method('getSupportedTypes')->willReturn(['*' => false]); $denormalizer2->method('supportsDenormalization') ->willReturn(true); $denormalizer2->method('denormalize')->willReturn('test2'); @@ -370,8 +363,7 @@ public function testNormalizerAware() { $normalizerAware = $this->createMock(NormalizerAwareNormalizer::class); $normalizerAware->expects($this->once()) - ->method('setNormalizer') - ->with($this->isInstanceOf(NormalizerInterface::class)); + ->method('setNormalizer'); new Serializer([$normalizerAware]); } @@ -380,8 +372,7 @@ public function testDenormalizerAware() { $denormalizerAware = $this->createMock(DenormalizerAwareDenormalizer::class); $denormalizerAware->expects($this->once()) - ->method('setDenormalizer') - ->with($this->isInstanceOf(DenormalizerInterface::class)); + ->method('setDenormalizer'); new Serializer([$denormalizerAware]); } @@ -1235,6 +1226,76 @@ public static function provideCollectDenormalizationErrors() [new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))], ]; } + + public function testSerializerUsesSupportedTypesMethod() + { + $neverCalledNormalizer = $this->createMock(DummyNormalizer::class); + $neverCalledNormalizer + // once for normalization, once for denormalization + ->expects($this->exactly(2)) + ->method('getSupportedTypes') + ->willReturn([ + Foo::class => true, + Bar::class => false, + ]); + + $supportedAndCachedNormalizer = $this->createMock(DummyNormalizer::class); + $supportedAndCachedNormalizer + // once for normalization, once for denormalization + ->expects($this->exactly(2)) + ->method('getSupportedTypes') + ->willReturn([ + Model::class => true, + ]); + + $serializer = new Serializer( + [ + $neverCalledNormalizer, + $supportedAndCachedNormalizer, + new ObjectNormalizer(), + ], + ['json' => new JsonEncoder()] + ); + + // Normalization process + $neverCalledNormalizer + ->expects($this->never()) + ->method('supportsNormalization'); + $neverCalledNormalizer + ->expects($this->never()) + ->method('normalize'); + + $supportedAndCachedNormalizer + ->expects($this->once()) + ->method('supportsNormalization') + ->willReturn(true); + $supportedAndCachedNormalizer + ->expects($this->exactly(2)) + ->method('normalize') + ->willReturn(['foo' => 'bar']); + + $serializer->normalize(new Model(), 'json'); + $serializer->normalize(new Model(), 'json'); + + // Denormalization pass + $neverCalledNormalizer + ->expects($this->never()) + ->method('supportsDenormalization'); + $neverCalledNormalizer + ->expects($this->never()) + ->method('denormalize'); + $supportedAndCachedNormalizer + ->expects($this->once()) + ->method('supportsDenormalization') + ->willReturn(true); + $supportedAndCachedNormalizer + ->expects($this->exactly(2)) + ->method('denormalize') + ->willReturn(new Model()); + + $serializer->denormalize('foo', Model::class, 'json'); + $serializer->denormalize('foo', Model::class, 'json'); + } } class Model @@ -1389,6 +1450,11 @@ public function getIterator(): \ArrayIterator } } +abstract class DummyNormalizer implements NormalizerInterface, DenormalizerInterface +{ + abstract public function getSupportedTypes(?string $format): array; +} + interface NormalizerAwareNormalizer extends NormalizerInterface, NormalizerAwareInterface { }