Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5b6df6f

Browse files
feature #27049 [Serializer] Cache the normalizer to use when possible (dunglas, nicolas-grekas)
This PR was merged into the 4.1-dev branch. Discussion ---------- [Serializer] Cache the normalizer to use when possible | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes <!-- don't forget to update src/**/CHANGELOG.md files --> | BC breaks? | no <!-- see https://symfony.com/bc --> | Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tests pass? | yes <!-- please add some, will be required by reviewers --> | Fixed tickets | #24436 <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | todo Still a WIP: * [x] Support `supportsDenormalization` As this will dramatically improve the performance of the Serializer component, can we consider introducing this change in 4.1 @symfony/deciders? ping @bendavies Commits ------- 16f8a13 [Serializer] Generalize caching support a bit c1e850f [Serializer] Cache the normalizer to use when possible
2 parents d843181 + 16f8a13 commit 5b6df6f

12 files changed

+100
-38
lines changed

src/Symfony/Component/Serializer/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ CHANGELOG
44
4.1.0
55
-----
66

7+
* added `CacheableSupportsMethodInterface` for normalizers and denormalizers that use
8+
only the type and the format in their `supports*()` methods
79
* added `MissingConstructorArgumentsException` new exception for deserialization failure
810
of objects that needs data insertion in constructor
911
* added an optional `default_constructor_arguments` option of context to specify a default data in

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,14 @@
3030
*
3131
* @author Kévin Dunglas <[email protected]>
3232
*/
33-
abstract class AbstractObjectNormalizer extends AbstractNormalizer
33+
abstract class AbstractObjectNormalizer extends AbstractNormalizer implements CacheableSupportsMethodInterface
3434
{
3535
const ENABLE_MAX_DEPTH = 'enable_max_depth';
3636
const DEPTH_KEY_PATTERN = 'depth_%s::%s';
3737
const DISABLE_TYPE_ENFORCEMENT = 'disable_type_enforcement';
3838

3939
private $propertyTypeExtractor;
4040
private $attributesCache = array();
41-
private $cache = array();
4241

4342
/**
4443
* @var callable|null
@@ -225,11 +224,7 @@ public function setMaxDepthHandler(?callable $handler): void
225224
*/
226225
public function supportsDenormalization($data, $type, $format = null)
227226
{
228-
if (!isset($this->cache[$type])) {
229-
$this->cache[$type] = class_exists($type) || (interface_exists($type) && null !== $this->classDiscriminatorResolver && null !== $this->classDiscriminatorResolver->getMappingForClass($type));
230-
}
231-
232-
return $this->cache[$type];
227+
return \class_exists($type) || (\interface_exists($type, false) && $this->classDiscriminatorResolver && null !== $this->classDiscriminatorResolver->getMappingForClass($type));
233228
}
234229

235230
/**
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Normalizer;
13+
14+
/**
15+
* Marker interface for normalizers and denormalizers that use
16+
* only the type and the format in their supports*() methods.
17+
*
18+
* By implementing this interface, the return value of the
19+
* supports*() methods will be cached by type and format.
20+
*
21+
* @author Kévin Dunglas <[email protected]>
22+
*/
23+
interface CacheableSupportsMethodInterface
24+
{
25+
}

src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* @author Grégoire Pineau <[email protected]>
2323
* @author Kévin Dunglas <[email protected]>
2424
*/
25-
class ConstraintViolationListNormalizer implements NormalizerInterface
25+
class ConstraintViolationListNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
2626
{
2727
/**
2828
* {@inheritdoc}

src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@
1717
/**
1818
* @author Jordi Boggiano <[email protected]>
1919
*/
20-
class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface
20+
class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface, CacheableSupportsMethodInterface
2121
{
2222
use ObjectToPopulateTrait;
2323
use SerializerAwareTrait;
2424

25-
private $cache = array();
26-
2725
/**
2826
* {@inheritdoc}
2927
*/
@@ -67,14 +65,6 @@ public function supportsNormalization($data, $format = null)
6765
*/
6866
public function supportsDenormalization($data, $type, $format = null)
6967
{
70-
if (isset($this->cache[$type])) {
71-
return $this->cache[$type];
72-
}
73-
74-
if (!class_exists($type)) {
75-
return $this->cache[$type] = false;
76-
}
77-
78-
return $this->cache[$type] = is_subclass_of($type, 'Symfony\Component\Serializer\Normalizer\DenormalizableInterface');
68+
return \is_subclass_of($type, DenormalizableInterface::class);
7969
}
8070
}

src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*
2424
* @author Kévin Dunglas <[email protected]>
2525
*/
26-
class DataUriNormalizer implements NormalizerInterface, DenormalizerInterface
26+
class DataUriNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface
2727
{
2828
private static $supportedTypes = array(
2929
\SplFileInfo::class => true,

src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*
2121
* @author Jérôme Parmentier <[email protected]>
2222
*/
23-
class DateIntervalNormalizer implements NormalizerInterface, DenormalizerInterface
23+
class DateIntervalNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface
2424
{
2525
const FORMAT_KEY = 'dateinterval_format';
2626

src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*
2121
* @author Kévin Dunglas <[email protected]>
2222
*/
23-
class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface
23+
class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface
2424
{
2525
const FORMAT_KEY = 'datetime_format';
2626
const TIMEZONE_KEY = 'datetime_timezone';

src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,21 @@
3535
class GetSetMethodNormalizer extends AbstractObjectNormalizer
3636
{
3737
private static $setterAccessibleCache = array();
38-
private $cache = array();
3938

4039
/**
4140
* {@inheritdoc}
4241
*/
4342
public function supportsNormalization($data, $format = null)
4443
{
45-
return parent::supportsNormalization($data, $format) && (isset($this->cache[$type = \get_class($data)]) ? $this->cache[$type] : $this->cache[$type] = $this->supports($type));
44+
return parent::supportsNormalization($data, $format) && $this->supports(\get_class($data));
4645
}
4746

4847
/**
4948
* {@inheritdoc}
5049
*/
5150
public function supportsDenormalization($data, $type, $format = null)
5251
{
53-
return parent::supportsDenormalization($data, $type, $format) && (isset($this->cache[$type]) ? $this->cache[$type] : $this->cache[$type] = $this->supports($type));
52+
return parent::supportsDenormalization($data, $type, $format) && $this->supports($type);
5453
}
5554

5655
/**

src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
*
2020
* @author Fred Cox <[email protected]>
2121
*/
22-
class JsonSerializableNormalizer extends AbstractNormalizer
22+
class JsonSerializableNormalizer extends AbstractNormalizer implements CacheableSupportsMethodInterface
2323
{
2424
/**
2525
* {@inheritdoc}

src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,20 @@
3030
*/
3131
class PropertyNormalizer extends AbstractObjectNormalizer
3232
{
33-
private $cache = array();
34-
3533
/**
3634
* {@inheritdoc}
3735
*/
3836
public function supportsNormalization($data, $format = null)
3937
{
40-
return parent::supportsNormalization($data, $format) && (isset($this->cache[$type = \get_class($data)]) ? $this->cache[$type] : $this->cache[$type] = $this->supports($type));
38+
return parent::supportsNormalization($data, $format) && $this->supports(\get_class($data));
4139
}
4240

4341
/**
4442
* {@inheritdoc}
4543
*/
4644
public function supportsDenormalization($data, $type, $format = null)
4745
{
48-
return parent::supportsDenormalization($data, $type, $format) && (isset($this->cache[$type]) ? $this->cache[$type] : $this->cache[$type] = $this->supports($type));
46+
return parent::supportsDenormalization($data, $type, $format) && $this->supports($type);
4947
}
5048

5149
/**

src/Symfony/Component/Serializer/Serializer.php

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
2727
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
2828
use Symfony\Component\Serializer\Exception\LogicException;
29+
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
2930

3031
/**
3132
* Serializer serializes and deserializes data.
@@ -55,10 +56,14 @@ class Serializer implements SerializerInterface, ContextAwareNormalizerInterface
5556
protected $decoder;
5657

5758
/**
58-
* @var array
59+
* @internal since Symfony 4.1
5960
*/
6061
protected $normalizers = array();
6162

63+
private $cachedNormalizers;
64+
private $denormalizerCache = array();
65+
private $normalizerCache = array();
66+
6267
public function __construct(array $normalizers = array(), array $encoders = array())
6368
{
6469
foreach ($normalizers as $normalizer) {
@@ -200,10 +205,35 @@ public function supportsDenormalization($data, $type, $format = null, array $con
200205
*
201206
* @return NormalizerInterface|null
202207
*/
203-
private function getNormalizer($data, $format, array $context)
208+
private function getNormalizer($data, ?string $format, array $context)
204209
{
205-
foreach ($this->normalizers as $normalizer) {
206-
if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format, $context)) {
210+
if ($this->cachedNormalizers !== $this->normalizers) {
211+
$this->cachedNormalizers = $this->normalizers;
212+
$this->denormalizerCache = $this->normalizerCache = array();
213+
}
214+
$type = \is_object($data) ? \get_class($data) : 'native-'.\gettype($data);
215+
216+
if (!isset($this->normalizerCache[$format][$type])) {
217+
$this->normalizerCache[$format][$type] = array();
218+
219+
foreach ($this->normalizers as $k => $normalizer) {
220+
if (!$normalizer instanceof NormalizerInterface) {
221+
continue;
222+
}
223+
224+
if (!$normalizer instanceof CacheableSupportsMethodInterface) {
225+
$this->normalizerCache[$format][$type][$k] = false;
226+
} elseif ($normalizer->supportsNormalization($data, $format)) {
227+
$this->normalizerCache[$format][$type][$k] = true;
228+
229+
return $normalizer;
230+
}
231+
}
232+
}
233+
234+
foreach ($this->normalizerCache[$format][$type] as $k => $cached) {
235+
$normalizer = $this->normalizers[$k];
236+
if ($cached || $normalizer->supportsNormalization($data, $format, $context)) {
207237
return $normalizer;
208238
}
209239
}
@@ -219,10 +249,33 @@ private function getNormalizer($data, $format, array $context)
219249
*
220250
* @return DenormalizerInterface|null
221251
*/
222-
private function getDenormalizer($data, $class, $format, array $context)
252+
private function getDenormalizer($data, string $class, ?string $format, array $context)
223253
{
224-
foreach ($this->normalizers as $normalizer) {
225-
if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $class, $format, $context)) {
254+
if ($this->cachedNormalizers !== $this->normalizers) {
255+
$this->cachedNormalizers = $this->normalizers;
256+
$this->denormalizerCache = $this->normalizerCache = array();
257+
}
258+
if (!isset($this->denormalizerCache[$format][$class])) {
259+
$this->denormalizerCache[$format][$class] = array();
260+
261+
foreach ($this->normalizers as $k => $normalizer) {
262+
if (!$normalizer instanceof DenormalizerInterface) {
263+
continue;
264+
}
265+
266+
if (!$normalizer instanceof CacheableSupportsMethodInterface) {
267+
$this->denormalizerCache[$format][$class][$k] = false;
268+
} elseif ($normalizer->supportsDenormalization(null, $class, $format)) {
269+
$this->denormalizerCache[$format][$class][$k] = true;
270+
271+
return $normalizer;
272+
}
273+
}
274+
}
275+
276+
foreach ($this->denormalizerCache[$format][$class] as $k => $cached) {
277+
$normalizer = $this->normalizers[$k];
278+
if ($cached || $normalizer->supportsDenormalization($data, $class, $format, $context)) {
226279
return $normalizer;
227280
}
228281
}

0 commit comments

Comments
 (0)