diff --git a/UPGRADE-6.2.md b/UPGRADE-6.2.md index 3879850dc0abc..60a2a46318f51 100644 --- a/UPGRADE-6.2.md +++ b/UPGRADE-6.2.md @@ -102,6 +102,7 @@ Serializer * Deprecate calling `AttributeMetadata::setSerializedName()`, `ClassMetadata::setClassDiscriminatorMapping()` without arguments * Change the signature of `AttributeMetadataInterface::setSerializedName()` to `setSerializedName(?string)` * Change the signature of `ClassMetadataInterface::setClassDiscriminatorMapping()` to `setClassDiscriminatorMapping(?ClassDiscriminatorMapping)` + * Deprecate `Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface`, use `Symfony\Component\Serializer\Normalizer\CacheableSupport` instead Translation ----------- diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 3342ada2fea86..c26294080f222 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +6.3 +--- + +* Cache normalizer selection based on format and type +* Deprecate `Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface`, use `Symfony\Component\Serializer\Normalizer\CacheableSupport` instead + 6.2 --- diff --git a/src/Symfony/Component/Serializer/Normalizer/CacheableSupport.php b/src/Symfony/Component/Serializer/Normalizer/CacheableSupport.php new file mode 100644 index 0000000000000..d1ee3d6d7f735 --- /dev/null +++ b/src/Symfony/Component/Serializer/Normalizer/CacheableSupport.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Normalizer; + +/** + * The return value of supports*() methods in {@see NormalizerInterface} and {@see DenormalizerInterface}. + * Tells if the result should be cached based on type and format. + * + * -1 : no, never supports the $format+$type, cache it + * 0 : no, no cache + * 1 : yes, no cache + * 2 : yes, always supports the $format+$type, cache it + * + * @author Jeroen Spee + */ +enum CacheableSupport: int +{ + case SupportNever = -1; + case SupportNot = 0; + case Support = 1; + case SupportAlways = 2; + + public function supports(): bool + { + return match ($this) { + CacheableSupport::SupportNever, CacheableSupport::SupportNot => false, + CacheableSupport::Support, CacheableSupport::SupportAlways => true, + }; + } +} diff --git a/src/Symfony/Component/Serializer/Normalizer/CacheableSupportsMethodInterface.php b/src/Symfony/Component/Serializer/Normalizer/CacheableSupportsMethodInterface.php index 3a55f653b1786..66e9b5ee27c48 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/serializer 6.1, return CacheableSupport from the supports*() method instead */ interface CacheableSupportsMethodInterface { diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php index ae3adbfe330fa..e225d435ffd45 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php @@ -34,8 +34,6 @@ interface DenormalizerInterface * @param string $format Format the given data was extracted from * @param array $context Options available to the denormalizer * - * @return mixed - * * @throws BadMethodCallException Occurs when the normalizer is not called in an expected context * @throws InvalidArgumentException Occurs when the arguments are not coherent or not supported * @throws UnexpectedValueException Occurs when the item cannot be hydrated with the given data @@ -49,12 +47,11 @@ 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 $format The format being deserialized from - * @param array $context Options available to the denormalizer + * @param mixed $data Data to denormalize from + * @param string $type The class to which the data should be denormalized + * @param string $format The format being deserialized from * - * @return bool + * @return CacheableSupport|bool returning a boolean is deprecated */ public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */); } diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php index 691e9c70f01cb..cd849cb80fca1 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php @@ -41,11 +41,10 @@ public function normalize(mixed $object, string $format = null, array $context = /** * Checks whether the given class is supported for normalization by this normalizer. * - * @param mixed $data Data to normalize - * @param string $format The format being (de-)serialized from or into - * @param array $context Context options for the normalizer + * @param mixed $data Data to normalize + * @param string $format The format being (de-)serialized from or into * - * @return bool + * @return CacheableSupport|bool returning a boolean is deprecated */ public function supportsNormalization(mixed $data, string $format = null /* , array $context = [] */); } diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index 85a3ac5b558d4..2713e5a391b59 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -23,6 +23,7 @@ use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\Exception\PartialDenormalizationException; use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; +use Symfony\Component\Serializer\Normalizer\CacheableSupport; use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface; use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; @@ -247,19 +248,24 @@ public function supportsDenormalization(mixed $data, string $type, string $forma private function getNormalizer(mixed $data, ?string $format, array $context): ?NormalizerInterface { $type = \is_object($data) ? $data::class : 'native-'.\gettype($data); - + $minCached = CacheableSupport::SupportAlways; + $minUncached = CacheableSupport::SupportNever; if (!isset($this->normalizerCache[$format][$type])) { + $minCached = CacheableSupport::Support; + $minUncached = CacheableSupport::SupportNot; $this->normalizerCache[$format][$type] = []; - foreach ($this->normalizers as $k => $normalizer) { if (!$normalizer instanceof NormalizerInterface) { 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; + $support = $this->supportsNormalizationWrapper($normalizer, $data, $format, $context); + if (CacheableSupport::SupportNever === $support) { + continue; + } + + $this->normalizerCache[$format][$type][$k] = $support; + if (CacheableSupport::SupportAlways === $support) { break; } } @@ -267,7 +273,7 @@ private function getNormalizer(mixed $data, ?string $format, array $context): ?N foreach ($this->normalizerCache[$format][$type] as $k => $cached) { $normalizer = $this->normalizers[$k]; - if ($cached || $normalizer->supportsNormalization($data, $format, $context)) { + if ($cached->value >= $minCached->value || ($cached->value > $minUncached->value && $this->supportsNormalizationWrapper($normalizer, $data, $format, $context)->value > CacheableSupport::SupportNot->value)) { return $normalizer; } } @@ -275,6 +281,23 @@ private function getNormalizer(mixed $data, ?string $format, array $context): ?N return null; } + /** + * Backwards-Compatibility layer for CacheableSupportsMethodInterface -> CacheableSupport. + */ + private function supportsNormalizationWrapper(NormalizerInterface $normalizer, mixed $data, ?string $format, array $context): CacheableSupport + { + $value = $normalizer->supportsNormalization($data, $format, $context); + if (\is_bool($value)) { + trigger_deprecation('symfony/serializer', '6.2', 'Returning boolean from "%s::%s" is deprecated, return "%s" instead.', NormalizerInterface::class, 'supports()', CacheableSupport::class); + } + + return match ($value) { + true => $normalizer instanceof CacheableSupportsMethodInterface && $normalizer->hasCacheableSupportsMethod() ? CacheableSupport::SupportAlways : CacheableSupport::Support, + false => $normalizer instanceof CacheableSupportsMethodInterface && $normalizer->hasCacheableSupportsMethod() ? CacheableSupport::SupportNever : CacheableSupport::SupportNot, + default => $value + }; + } + /** * Returns a matching denormalizer. * @@ -285,7 +308,11 @@ private function getNormalizer(mixed $data, ?string $format, array $context): ?N */ private function getDenormalizer(mixed $data, string $class, ?string $format, array $context): ?DenormalizerInterface { + $minCached = CacheableSupport::SupportAlways; + $minUncached = CacheableSupport::SupportNever; if (!isset($this->denormalizerCache[$format][$class])) { + $minCached = CacheableSupport::Support; + $minUncached = CacheableSupport::SupportNot; $this->denormalizerCache[$format][$class] = []; foreach ($this->normalizers as $k => $normalizer) { @@ -293,10 +320,13 @@ 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; + $support = $this->supportsDenormalizationWrapper($normalizer, $data, $class, $format, $context); + if (CacheableSupport::SupportNever === $support) { + continue; + } + + $this->denormalizerCache[$format][$class][$k] = $support; + if (CacheableSupport::SupportAlways === $support) { break; } } @@ -304,7 +334,7 @@ private function getDenormalizer(mixed $data, string $class, ?string $format, ar foreach ($this->denormalizerCache[$format][$class] as $k => $cached) { $normalizer = $this->normalizers[$k]; - if ($cached || $normalizer->supportsDenormalization($data, $class, $format, $context)) { + if ($cached->value >= $minCached->value || ($cached->value > $minUncached->value && $this->supportsDenormalizationWrapper($normalizer, $data, $class, $format, $context)->value > CacheableSupport::SupportNot->value)) { return $normalizer; } } @@ -312,6 +342,23 @@ private function getDenormalizer(mixed $data, string $class, ?string $format, ar return null; } + /** + * Backwards-Compatibility layer for CacheableSupportsMethodInterface -> CacheableSupport. + */ + private function supportsDenormalizationWrapper(DenormalizerInterface $normalizer, mixed $data, string $class, ?string $format, array $context): CacheableSupport + { + $value = $normalizer->supportsDenormalization($data, $class, $format, $context); + if (\is_bool($value)) { + trigger_deprecation('symfony/serializer', '6.2', 'Returning boolean from "%s::%s" is deprecated, return "%s" instead.', DenormalizerInterface::class, 'supports()', CacheableSupport::class); + } + + return match ($value) { + true => $normalizer instanceof CacheableSupportsMethodInterface && $normalizer->hasCacheableSupportsMethod() ? CacheableSupport::SupportAlways : CacheableSupport::Support, + false => $normalizer instanceof CacheableSupportsMethodInterface && $normalizer->hasCacheableSupportsMethod() ? CacheableSupport::SupportNever : CacheableSupport::SupportNot, + default => $value + }; + } + final public function encode(mixed $data, string $format, array $context = []): string { return $this->encoder->encode($data, $format, $context); diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 255abb3864b1d..dd79b8eae51ec 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -37,6 +37,7 @@ use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer; +use Symfony\Component\Serializer\Normalizer\CacheableSupport; use Symfony\Component\Serializer\Normalizer\CustomNormalizer; use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; @@ -1247,6 +1248,29 @@ public function provideCollectDenormalizationErrors() [new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))], ]; } + + public function testCacheableNormalizer() + { + $normalizer = $this->createMock(NormalizerInterface::class); + $serializer = new Serializer([$normalizer], []); + + $normalizer + ->expects($this->exactly(3)) + ->method('supportsNormalization') + ->willReturnCallback(function ($data, $format, array $context = []): CacheableSupport { + if (!$data instanceof Bar) { + return CacheableSupport::SupportNever; + } + + return ($context['TEST_CONTEXT'] ?? false) ? CacheableSupport::Support : CacheableSupport::SupportNot; + }); + + $this->assertTrue($serializer->supportsNormalization(new Bar(''), 'json', ['TEST_CONTEXT' => true])); + $this->assertFalse($serializer->supportsNormalization(new Bar(''), 'json')); + $this->assertFalse($serializer->supportsNormalization(new \stdClass(), 'json', ['TEST_CONTEXT' => true])); + $this->assertFalse($serializer->supportsNormalization(new \stdClass(), 'json', ['TEST_CONTEXT' => true])); + $this->assertFalse($serializer->supportsNormalization(new \stdClass(), 'json', ['TEST_CONTEXT' => true])); + } } class Model pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy