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

Skip to content

Commit ab78c9c

Browse files
committed
introduce int values as return of supports(de)Normalization, to indicate supports-never, -always
1 parent 69f46f2 commit ab78c9c

8 files changed

+138
-23
lines changed

UPGRADE-6.2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ Serializer
102102
* Deprecate calling `AttributeMetadata::setSerializedName()`, `ClassMetadata::setClassDiscriminatorMapping()` without arguments
103103
* Change the signature of `AttributeMetadataInterface::setSerializedName()` to `setSerializedName(?string)`
104104
* Change the signature of `ClassMetadataInterface::setClassDiscriminatorMapping()` to `setClassDiscriminatorMapping(?ClassDiscriminatorMapping)`
105+
* Deprecate `Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface`, use `Symfony\Component\Serializer\Normalizer\CacheableSupport` instead
105106

106107
Translation
107108
-----------

src/Symfony/Component/Serializer/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
CHANGELOG
22
=========
33

4+
6.3
5+
---
6+
7+
* Cache normalizer selection based on format and type
8+
* Deprecate `Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface`, use `Symfony\Component\Serializer\Normalizer\CacheableSupport` instead
9+
410
6.2
511
---
612

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
* The return value of supports*() methods in {@see NormalizerInterface} and {@see DenormalizerInterface}.
16+
* Tells if the result should be cached based on type and format.
17+
*
18+
* -1 : no, never supports the $format+$type, cache it
19+
* 0 : no, no cache
20+
* 1 : yes, no cache
21+
* 2 : yes, always supports the $format+$type, cache it
22+
*
23+
* @author Jeroen Spee <https://github.com/Jeroeny>
24+
*/
25+
enum CacheableSupport: int
26+
{
27+
case SupportNever = -1;
28+
case SupportNot = 0;
29+
case Support = 1;
30+
case SupportAlways = 2;
31+
32+
public function supports(): bool
33+
{
34+
return match ($this) {
35+
CacheableSupport::SupportNever, CacheableSupport::SupportNot => false,
36+
CacheableSupport::Support, CacheableSupport::SupportAlways => true,
37+
};
38+
}
39+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
* supports*() methods will be cached by type and format.
2020
*
2121
* @author Kévin Dunglas <[email protected]>
22+
*
23+
* @deprecated since symfony/serializer 6.1, return CacheableSupport from the supports*() method instead
2224
*/
2325
interface CacheableSupportsMethodInterface
2426
{

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ interface DenormalizerInterface
3434
* @param string $format Format the given data was extracted from
3535
* @param array $context Options available to the denormalizer
3636
*
37-
* @return mixed
38-
*
3937
* @throws BadMethodCallException Occurs when the normalizer is not called in an expected context
4038
* @throws InvalidArgumentException Occurs when the arguments are not coherent or not supported
4139
* @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
4947
/**
5048
* Checks whether the given class is supported for denormalization by this normalizer.
5149
*
52-
* @param mixed $data Data to denormalize from
53-
* @param string $type The class to which the data should be denormalized
54-
* @param string $format The format being deserialized from
55-
* @param array $context Options available to the denormalizer
50+
* @param mixed $data Data to denormalize from
51+
* @param string $type The class to which the data should be denormalized
52+
* @param string $format The format being deserialized from
5653
*
57-
* @return bool
54+
* @return CacheableSupport|bool returning a boolean is deprecated
5855
*/
5956
public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */);
6057
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,10 @@ public function normalize(mixed $object, string $format = null, array $context =
4141
/**
4242
* Checks whether the given class is supported for normalization by this normalizer.
4343
*
44-
* @param mixed $data Data to normalize
45-
* @param string $format The format being (de-)serialized from or into
46-
* @param array $context Context options for the normalizer
44+
* @param mixed $data Data to normalize
45+
* @param string $format The format being (de-)serialized from or into
4746
*
48-
* @return bool
47+
* @return CacheableSupport|bool returning a boolean is deprecated
4948
*/
5049
public function supportsNormalization(mixed $data, string $format = null /* , array $context = [] */);
5150
}

src/Symfony/Component/Serializer/Serializer.php

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
2424
use Symfony\Component\Serializer\Exception\PartialDenormalizationException;
2525
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
26+
use Symfony\Component\Serializer\Normalizer\CacheableSupport;
2627
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
2728
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
2829
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
@@ -247,34 +248,56 @@ public function supportsDenormalization(mixed $data, string $type, string $forma
247248
private function getNormalizer(mixed $data, ?string $format, array $context): ?NormalizerInterface
248249
{
249250
$type = \is_object($data) ? $data::class : 'native-'.\gettype($data);
250-
251+
$minCached = CacheableSupport::SupportAlways;
252+
$minUncached = CacheableSupport::SupportNever;
251253
if (!isset($this->normalizerCache[$format][$type])) {
254+
$minCached = CacheableSupport::Support;
255+
$minUncached = CacheableSupport::SupportNot;
252256
$this->normalizerCache[$format][$type] = [];
253-
254257
foreach ($this->normalizers as $k => $normalizer) {
255258
if (!$normalizer instanceof NormalizerInterface) {
256259
continue;
257260
}
258261

259-
if (!$normalizer instanceof CacheableSupportsMethodInterface || !$normalizer->hasCacheableSupportsMethod()) {
260-
$this->normalizerCache[$format][$type][$k] = false;
261-
} elseif ($normalizer->supportsNormalization($data, $format, $context)) {
262-
$this->normalizerCache[$format][$type][$k] = true;
262+
$support = $this->supportsNormalizationWrapper($normalizer, $data, $format, $context);
263+
if (CacheableSupport::SupportNever === $support) {
264+
continue;
265+
}
266+
267+
$this->normalizerCache[$format][$type][$k] = $support;
268+
if (CacheableSupport::SupportAlways === $support) {
263269
break;
264270
}
265271
}
266272
}
267273

268274
foreach ($this->normalizerCache[$format][$type] as $k => $cached) {
269275
$normalizer = $this->normalizers[$k];
270-
if ($cached || $normalizer->supportsNormalization($data, $format, $context)) {
276+
if ($cached->value >= $minCached->value || ($cached->value > $minUncached->value && $this->supportsNormalizationWrapper($normalizer, $data, $format, $context)->value > CacheableSupport::SupportNot->value)) {
271277
return $normalizer;
272278
}
273279
}
274280

275281
return null;
276282
}
277283

284+
/**
285+
* Backwards-Compatibility layer for CacheableSupportsMethodInterface -> CacheableSupport.
286+
*/
287+
private function supportsNormalizationWrapper(NormalizerInterface $normalizer, mixed $data, ?string $format, array $context): CacheableSupport
288+
{
289+
$value = $normalizer->supportsNormalization($data, $format, $context);
290+
if (\is_bool($value)) {
291+
trigger_deprecation('symfony/serializer', '6.2', 'Returning boolean from "%s::%s" is deprecated, return "%s" instead.', NormalizerInterface::class, 'supports()', CacheableSupport::class);
292+
}
293+
294+
return match ($value) {
295+
true => $normalizer instanceof CacheableSupportsMethodInterface && $normalizer->hasCacheableSupportsMethod() ? CacheableSupport::SupportAlways : CacheableSupport::Support,
296+
false => $normalizer instanceof CacheableSupportsMethodInterface && $normalizer->hasCacheableSupportsMethod() ? CacheableSupport::SupportNever : CacheableSupport::SupportNot,
297+
default => $value
298+
};
299+
}
300+
278301
/**
279302
* Returns a matching denormalizer.
280303
*
@@ -285,33 +308,57 @@ private function getNormalizer(mixed $data, ?string $format, array $context): ?N
285308
*/
286309
private function getDenormalizer(mixed $data, string $class, ?string $format, array $context): ?DenormalizerInterface
287310
{
311+
$minCached = CacheableSupport::SupportAlways;
312+
$minUncached = CacheableSupport::SupportNever;
288313
if (!isset($this->denormalizerCache[$format][$class])) {
314+
$minCached = CacheableSupport::Support;
315+
$minUncached = CacheableSupport::SupportNot;
289316
$this->denormalizerCache[$format][$class] = [];
290317

291318
foreach ($this->normalizers as $k => $normalizer) {
292319
if (!$normalizer instanceof DenormalizerInterface) {
293320
continue;
294321
}
295322

296-
if (!$normalizer instanceof CacheableSupportsMethodInterface || !$normalizer->hasCacheableSupportsMethod()) {
297-
$this->denormalizerCache[$format][$class][$k] = false;
298-
} elseif ($normalizer->supportsDenormalization(null, $class, $format, $context)) {
299-
$this->denormalizerCache[$format][$class][$k] = true;
323+
$support = $this->supportsDenormalizationWrapper($normalizer, $data, $class, $format, $context);
324+
if (CacheableSupport::SupportNever === $support) {
325+
continue;
326+
}
327+
328+
$this->denormalizerCache[$format][$class][$k] = $support;
329+
if (CacheableSupport::SupportAlways === $support) {
300330
break;
301331
}
302332
}
303333
}
304334

305335
foreach ($this->denormalizerCache[$format][$class] as $k => $cached) {
306336
$normalizer = $this->normalizers[$k];
307-
if ($cached || $normalizer->supportsDenormalization($data, $class, $format, $context)) {
337+
if ($cached->value >= $minCached->value || ($cached->value > $minUncached->value && $this->supportsDenormalizationWrapper($normalizer, $data, $class, $format, $context)->value > CacheableSupport::SupportNot->value)) {
308338
return $normalizer;
309339
}
310340
}
311341

312342
return null;
313343
}
314344

345+
/**
346+
* Backwards-Compatibility layer for CacheableSupportsMethodInterface -> CacheableSupport.
347+
*/
348+
private function supportsDenormalizationWrapper(DenormalizerInterface $normalizer, mixed $data, string $class, ?string $format, array $context): CacheableSupport
349+
{
350+
$value = $normalizer->supportsDenormalization($data, $class, $format, $context);
351+
if (\is_bool($value)) {
352+
trigger_deprecation('symfony/serializer', '6.2', 'Returning boolean from "%s::%s" is deprecated, return "%s" instead.', DenormalizerInterface::class, 'supports()', CacheableSupport::class);
353+
}
354+
355+
return match ($value) {
356+
true => $normalizer instanceof CacheableSupportsMethodInterface && $normalizer->hasCacheableSupportsMethod() ? CacheableSupport::SupportAlways : CacheableSupport::Support,
357+
false => $normalizer instanceof CacheableSupportsMethodInterface && $normalizer->hasCacheableSupportsMethod() ? CacheableSupport::SupportNever : CacheableSupport::SupportNot,
358+
default => $value
359+
};
360+
}
361+
315362
final public function encode(mixed $data, string $format, array $context = []): string
316363
{
317364
return $this->encoder->encode($data, $format, $context);

src/Symfony/Component/Serializer/Tests/SerializerTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
3838
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
3939
use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer;
40+
use Symfony\Component\Serializer\Normalizer\CacheableSupport;
4041
use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
4142
use Symfony\Component\Serializer\Normalizer\DataUriNormalizer;
4243
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
@@ -1247,6 +1248,29 @@ public function provideCollectDenormalizationErrors()
12471248
[new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))],
12481249
];
12491250
}
1251+
1252+
public function testCacheableNormalizer()
1253+
{
1254+
$normalizer = $this->createMock(NormalizerInterface::class);
1255+
$serializer = new Serializer([$normalizer], []);
1256+
1257+
$normalizer
1258+
->expects($this->exactly(3))
1259+
->method('supportsNormalization')
1260+
->willReturnCallback(function ($data, $format, array $context = []): CacheableSupport {
1261+
if (!$data instanceof Bar) {
1262+
return CacheableSupport::SupportNever;
1263+
}
1264+
1265+
return ($context['TEST_CONTEXT'] ?? false) ? CacheableSupport::Support : CacheableSupport::SupportNot;
1266+
});
1267+
1268+
$this->assertTrue($serializer->supportsNormalization(new Bar(''), 'json', ['TEST_CONTEXT' => true]));
1269+
$this->assertFalse($serializer->supportsNormalization(new Bar(''), 'json'));
1270+
$this->assertFalse($serializer->supportsNormalization(new \stdClass(), 'json', ['TEST_CONTEXT' => true]));
1271+
$this->assertFalse($serializer->supportsNormalization(new \stdClass(), 'json', ['TEST_CONTEXT' => true]));
1272+
$this->assertFalse($serializer->supportsNormalization(new \stdClass(), 'json', ['TEST_CONTEXT' => true]));
1273+
}
12501274
}
12511275

12521276
class Model

0 commit comments

Comments
 (0)