From c1e850fe834e01d32bbfab42cc5276b75422bdea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 25 Apr 2018 17:25:58 +0200 Subject: [PATCH 1/2] [Serializer] Cache the normalizer to use when possible --- .../Normalizer/AbstractObjectNormalizer.php | 2 +- .../ConstraintViolationListNormalizer.php | 2 +- .../Normalizer/CustomNormalizer.php | 2 +- .../Normalizer/DataUriNormalizer.php | 2 +- .../Normalizer/DateIntervalNormalizer.php | 2 +- .../Normalizer/DateTimeNormalizer.php | 2 +- .../Normalizer/JsonSerializableNormalizer.php | 2 +- ...zerWithCacheableSupportResultInterface.php | 21 ++++++++++++++ .../Component/Serializer/Serializer.php | 28 ++++++++++++++++++- 9 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Normalizer/NormalizerWithCacheableSupportResultInterface.php diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index abac37770e333..d9de481d73876 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -30,7 +30,7 @@ * * @author Kévin Dunglas */ -abstract class AbstractObjectNormalizer extends AbstractNormalizer +abstract class AbstractObjectNormalizer extends AbstractNormalizer implements NormalizerWithCacheableSupportResultInterface { const ENABLE_MAX_DEPTH = 'enable_max_depth'; const DEPTH_KEY_PATTERN = 'depth_%s::%s'; diff --git a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php index 68a4cb9213279..9ac604eca33c6 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php @@ -22,7 +22,7 @@ * @author Grégoire Pineau * @author Kévin Dunglas */ -class ConstraintViolationListNormalizer implements NormalizerInterface +class ConstraintViolationListNormalizer implements NormalizerInterface, NormalizerWithCacheableSupportResultInterface { /** * {@inheritdoc} diff --git a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php index 6a15d8da90270..7b54ff2d7f3ac 100644 --- a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php @@ -17,7 +17,7 @@ /** * @author Jordi Boggiano */ -class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface +class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface, NormalizerWithCacheableSupportResultInterface { use ObjectToPopulateTrait; use SerializerAwareTrait; diff --git a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php index 995bdf1441776..499e5f18fbf74 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php @@ -23,7 +23,7 @@ * * @author Kévin Dunglas */ -class DataUriNormalizer implements NormalizerInterface, DenormalizerInterface +class DataUriNormalizer implements NormalizerInterface, DenormalizerInterface, NormalizerWithCacheableSupportResultInterface { private static $supportedTypes = array( \SplFileInfo::class => true, diff --git a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php index 7ab102ed7a9ec..4f80ced0bc60e 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php @@ -20,7 +20,7 @@ * * @author Jérôme Parmentier */ -class DateIntervalNormalizer implements NormalizerInterface, DenormalizerInterface +class DateIntervalNormalizer implements NormalizerInterface, DenormalizerInterface, NormalizerWithCacheableSupportResultInterface { const FORMAT_KEY = 'dateinterval_format'; diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index 3dd94e07e029b..b38f2efa208fa 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -20,7 +20,7 @@ * * @author Kévin Dunglas */ -class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface +class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface, NormalizerWithCacheableSupportResultInterface { const FORMAT_KEY = 'datetime_format'; const TIMEZONE_KEY = 'datetime_timezone'; diff --git a/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php index 27ccf8023cba3..6ba4cee1c4e6c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php @@ -19,7 +19,7 @@ * * @author Fred Cox */ -class JsonSerializableNormalizer extends AbstractNormalizer +class JsonSerializableNormalizer extends AbstractNormalizer implements NormalizerWithCacheableSupportResultInterface { /** * {@inheritdoc} diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerWithCacheableSupportResultInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerWithCacheableSupportResultInterface.php new file mode 100644 index 0000000000000..7b8eabc400948 --- /dev/null +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerWithCacheableSupportResultInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Normalizer; + +/** + * "supportsNormalization()" methods of normalizers implementing this interface have a cacheable return. + * + * @author Kévin Dunglas + */ +interface NormalizerWithCacheableSupportResultInterface +{ +} diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index 1a7087042567a..5601182d1389c 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -26,6 +26,7 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Exception\LogicException; +use Symfony\Component\Serializer\Normalizer\NormalizerWithCacheableSupportResultInterface; /** * Serializer serializes and deserializes data. @@ -54,6 +55,8 @@ class Serializer implements SerializerInterface, ContextAwareNormalizerInterface */ protected $decoder; + private $normalizerCache = array(); + /** * @var array */ @@ -202,11 +205,34 @@ public function supportsDenormalization($data, $type, $format = null, array $con */ private function getNormalizer($data, $format, array $context) { + $type = \is_object($data) ? 'c-'.\get_class($data) : \gettype($data); + if ( + isset($this->normalizerCache[$type][$format]) || + (isset($this->normalizerCache[$type]) && \array_key_exists($format, $this->normalizerCache[$type])) + ) { + return $this->normalizerCache[$type][$format]; + } + + $cacheable = true; foreach ($this->normalizers as $normalizer) { - if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format, $context)) { + if (!$normalizer instanceof NormalizerInterface) { + continue; + } + + $cacheable = $cacheable && $normalizer instanceof NormalizerWithCacheableSupportResultInterface; + if ($normalizer->supportsNormalization($data, $format, $context)) { + if ($cacheable) { + $this->normalizerCache[$type][$format] = $normalizer; + } + return $normalizer; } } + + if ($cacheable) { + // allow to cache primitive types + $this->normalizerCache[$type][$format] = null; + } } /** From 16f8a1381018cbd0bbcc47d589921ac9562b131d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 27 Apr 2018 14:29:13 +0200 Subject: [PATCH 2/2] [Serializer] Generalize caching support a bit --- src/Symfony/Component/Serializer/CHANGELOG.md | 2 + .../Normalizer/AbstractObjectNormalizer.php | 9 +-- ...p => CacheableSupportsMethodInterface.php} | 8 +- .../ConstraintViolationListNormalizer.php | 2 +- .../Normalizer/CustomNormalizer.php | 14 +--- .../Normalizer/DataUriNormalizer.php | 2 +- .../Normalizer/DateIntervalNormalizer.php | 2 +- .../Normalizer/DateTimeNormalizer.php | 2 +- .../Normalizer/GetSetMethodNormalizer.php | 5 +- .../Normalizer/JsonSerializableNormalizer.php | 2 +- .../Normalizer/PropertyNormalizer.php | 6 +- .../Component/Serializer/Serializer.php | 81 ++++++++++++------- 12 files changed, 75 insertions(+), 60 deletions(-) rename src/Symfony/Component/Serializer/Normalizer/{NormalizerWithCacheableSupportResultInterface.php => CacheableSupportsMethodInterface.php} (54%) diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 9c1dccb5d9fe9..983bb9f5a0654 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -4,6 +4,8 @@ CHANGELOG 4.1.0 ----- +* added `CacheableSupportsMethodInterface` for normalizers and denormalizers that use + only the type and the format in their `supports*()` methods * added `MissingConstructorArgumentsException` new exception for deserialization failure of objects that needs data insertion in constructor * added an optional `default_constructor_arguments` option of context to specify a default data in diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index d9de481d73876..a7055498d5ff8 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -30,7 +30,7 @@ * * @author Kévin Dunglas */ -abstract class AbstractObjectNormalizer extends AbstractNormalizer implements NormalizerWithCacheableSupportResultInterface +abstract class AbstractObjectNormalizer extends AbstractNormalizer implements CacheableSupportsMethodInterface { const ENABLE_MAX_DEPTH = 'enable_max_depth'; const DEPTH_KEY_PATTERN = 'depth_%s::%s'; @@ -38,7 +38,6 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer implements No private $propertyTypeExtractor; private $attributesCache = array(); - private $cache = array(); /** * @var callable|null @@ -225,11 +224,7 @@ public function setMaxDepthHandler(?callable $handler): void */ public function supportsDenormalization($data, $type, $format = null) { - if (!isset($this->cache[$type])) { - $this->cache[$type] = class_exists($type) || (interface_exists($type) && null !== $this->classDiscriminatorResolver && null !== $this->classDiscriminatorResolver->getMappingForClass($type)); - } - - return $this->cache[$type]; + return \class_exists($type) || (\interface_exists($type, false) && $this->classDiscriminatorResolver && null !== $this->classDiscriminatorResolver->getMappingForClass($type)); } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerWithCacheableSupportResultInterface.php b/src/Symfony/Component/Serializer/Normalizer/CacheableSupportsMethodInterface.php similarity index 54% rename from src/Symfony/Component/Serializer/Normalizer/NormalizerWithCacheableSupportResultInterface.php rename to src/Symfony/Component/Serializer/Normalizer/CacheableSupportsMethodInterface.php index 7b8eabc400948..f3b50be7cdcd3 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizerWithCacheableSupportResultInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/CacheableSupportsMethodInterface.php @@ -12,10 +12,14 @@ namespace Symfony\Component\Serializer\Normalizer; /** - * "supportsNormalization()" methods of normalizers implementing this interface have a cacheable return. + * Marker interface for normalizers and denormalizers that use + * only the type and the format in their supports*() methods. + * + * By implementing this interface, the return value of the + * supports*() methods will be cached by type and format. * * @author Kévin Dunglas */ -interface NormalizerWithCacheableSupportResultInterface +interface CacheableSupportsMethodInterface { } diff --git a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php index 9ac604eca33c6..cf1c6616fa58c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php @@ -22,7 +22,7 @@ * @author Grégoire Pineau * @author Kévin Dunglas */ -class ConstraintViolationListNormalizer implements NormalizerInterface, NormalizerWithCacheableSupportResultInterface +class ConstraintViolationListNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface { /** * {@inheritdoc} diff --git a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php index 7b54ff2d7f3ac..87ad14102281f 100644 --- a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php @@ -17,13 +17,11 @@ /** * @author Jordi Boggiano */ -class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface, NormalizerWithCacheableSupportResultInterface +class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface, CacheableSupportsMethodInterface { use ObjectToPopulateTrait; use SerializerAwareTrait; - private $cache = array(); - /** * {@inheritdoc} */ @@ -67,14 +65,6 @@ public function supportsNormalization($data, $format = null) */ public function supportsDenormalization($data, $type, $format = null) { - if (isset($this->cache[$type])) { - return $this->cache[$type]; - } - - if (!class_exists($type)) { - return $this->cache[$type] = false; - } - - return $this->cache[$type] = is_subclass_of($type, 'Symfony\Component\Serializer\Normalizer\DenormalizableInterface'); + return \is_subclass_of($type, DenormalizableInterface::class); } } diff --git a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php index 499e5f18fbf74..9b637a269ca33 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php @@ -23,7 +23,7 @@ * * @author Kévin Dunglas */ -class DataUriNormalizer implements NormalizerInterface, DenormalizerInterface, NormalizerWithCacheableSupportResultInterface +class DataUriNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface { private static $supportedTypes = array( \SplFileInfo::class => true, diff --git a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php index 4f80ced0bc60e..ee30dddcada6d 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php @@ -20,7 +20,7 @@ * * @author Jérôme Parmentier */ -class DateIntervalNormalizer implements NormalizerInterface, DenormalizerInterface, NormalizerWithCacheableSupportResultInterface +class DateIntervalNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface { const FORMAT_KEY = 'dateinterval_format'; diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index b38f2efa208fa..aced67e37a881 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -20,7 +20,7 @@ * * @author Kévin Dunglas */ -class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface, NormalizerWithCacheableSupportResultInterface +class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface { const FORMAT_KEY = 'datetime_format'; const TIMEZONE_KEY = 'datetime_timezone'; diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index a6b8327be1ff9..4df997cbbcf39 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -35,14 +35,13 @@ class GetSetMethodNormalizer extends AbstractObjectNormalizer { private static $setterAccessibleCache = array(); - private $cache = array(); /** * {@inheritdoc} */ public function supportsNormalization($data, $format = null) { - return parent::supportsNormalization($data, $format) && (isset($this->cache[$type = \get_class($data)]) ? $this->cache[$type] : $this->cache[$type] = $this->supports($type)); + return parent::supportsNormalization($data, $format) && $this->supports(\get_class($data)); } /** @@ -50,7 +49,7 @@ public function supportsNormalization($data, $format = null) */ public function supportsDenormalization($data, $type, $format = null) { - return parent::supportsDenormalization($data, $type, $format) && (isset($this->cache[$type]) ? $this->cache[$type] : $this->cache[$type] = $this->supports($type)); + return parent::supportsDenormalization($data, $type, $format) && $this->supports($type); } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php index 6ba4cee1c4e6c..912546b4b03c9 100644 --- a/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php @@ -19,7 +19,7 @@ * * @author Fred Cox */ -class JsonSerializableNormalizer extends AbstractNormalizer implements NormalizerWithCacheableSupportResultInterface +class JsonSerializableNormalizer extends AbstractNormalizer implements CacheableSupportsMethodInterface { /** * {@inheritdoc} diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index fe0d3521e890e..5690d07d461ea 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -30,14 +30,12 @@ */ class PropertyNormalizer extends AbstractObjectNormalizer { - private $cache = array(); - /** * {@inheritdoc} */ public function supportsNormalization($data, $format = null) { - return parent::supportsNormalization($data, $format) && (isset($this->cache[$type = \get_class($data)]) ? $this->cache[$type] : $this->cache[$type] = $this->supports($type)); + return parent::supportsNormalization($data, $format) && $this->supports(\get_class($data)); } /** @@ -45,7 +43,7 @@ public function supportsNormalization($data, $format = null) */ public function supportsDenormalization($data, $type, $format = null) { - return parent::supportsDenormalization($data, $type, $format) && (isset($this->cache[$type]) ? $this->cache[$type] : $this->cache[$type] = $this->supports($type)); + return parent::supportsDenormalization($data, $type, $format) && $this->supports($type); } /** diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index 5601182d1389c..488b269d4fa6d 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -26,7 +26,7 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Exception\LogicException; -use Symfony\Component\Serializer\Normalizer\NormalizerWithCacheableSupportResultInterface; +use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; /** * Serializer serializes and deserializes data. @@ -55,13 +55,15 @@ class Serializer implements SerializerInterface, ContextAwareNormalizerInterface */ protected $decoder; - private $normalizerCache = array(); - /** - * @var array + * @internal since Symfony 4.1 */ protected $normalizers = array(); + private $cachedNormalizers; + private $denormalizerCache = array(); + private $normalizerCache = array(); + public function __construct(array $normalizers = array(), array $encoders = array()) { foreach ($normalizers as $normalizer) { @@ -203,35 +205,37 @@ public function supportsDenormalization($data, $type, $format = null, array $con * * @return NormalizerInterface|null */ - private function getNormalizer($data, $format, array $context) + private function getNormalizer($data, ?string $format, array $context) { - $type = \is_object($data) ? 'c-'.\get_class($data) : \gettype($data); - if ( - isset($this->normalizerCache[$type][$format]) || - (isset($this->normalizerCache[$type]) && \array_key_exists($format, $this->normalizerCache[$type])) - ) { - return $this->normalizerCache[$type][$format]; + if ($this->cachedNormalizers !== $this->normalizers) { + $this->cachedNormalizers = $this->normalizers; + $this->denormalizerCache = $this->normalizerCache = array(); } + $type = \is_object($data) ? \get_class($data) : 'native-'.\gettype($data); - $cacheable = true; - foreach ($this->normalizers as $normalizer) { - if (!$normalizer instanceof NormalizerInterface) { - continue; - } + if (!isset($this->normalizerCache[$format][$type])) { + $this->normalizerCache[$format][$type] = array(); - $cacheable = $cacheable && $normalizer instanceof NormalizerWithCacheableSupportResultInterface; - if ($normalizer->supportsNormalization($data, $format, $context)) { - if ($cacheable) { - $this->normalizerCache[$type][$format] = $normalizer; + foreach ($this->normalizers as $k => $normalizer) { + if (!$normalizer instanceof NormalizerInterface) { + continue; } - return $normalizer; + if (!$normalizer instanceof CacheableSupportsMethodInterface) { + $this->normalizerCache[$format][$type][$k] = false; + } elseif ($normalizer->supportsNormalization($data, $format)) { + $this->normalizerCache[$format][$type][$k] = true; + + return $normalizer; + } } } - if ($cacheable) { - // allow to cache primitive types - $this->normalizerCache[$type][$format] = null; + foreach ($this->normalizerCache[$format][$type] as $k => $cached) { + $normalizer = $this->normalizers[$k]; + if ($cached || $normalizer->supportsNormalization($data, $format, $context)) { + return $normalizer; + } } } @@ -245,10 +249,33 @@ private function getNormalizer($data, $format, array $context) * * @return DenormalizerInterface|null */ - private function getDenormalizer($data, $class, $format, array $context) + private function getDenormalizer($data, string $class, ?string $format, array $context) { - foreach ($this->normalizers as $normalizer) { - if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $class, $format, $context)) { + if ($this->cachedNormalizers !== $this->normalizers) { + $this->cachedNormalizers = $this->normalizers; + $this->denormalizerCache = $this->normalizerCache = array(); + } + if (!isset($this->denormalizerCache[$format][$class])) { + $this->denormalizerCache[$format][$class] = array(); + + foreach ($this->normalizers as $k => $normalizer) { + if (!$normalizer instanceof DenormalizerInterface) { + continue; + } + + if (!$normalizer instanceof CacheableSupportsMethodInterface) { + $this->denormalizerCache[$format][$class][$k] = false; + } elseif ($normalizer->supportsDenormalization(null, $class, $format)) { + $this->denormalizerCache[$format][$class][$k] = true; + + return $normalizer; + } + } + } + + foreach ($this->denormalizerCache[$format][$class] as $k => $cached) { + $normalizer = $this->normalizers[$k]; + if ($cached || $normalizer->supportsDenormalization($data, $class, $format, $context)) { return $normalizer; } }