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

Skip to content

Commit 694b252

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

8 files changed

+139
-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: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@
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;
2930
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
3031
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
3132
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
3233
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
34+
use Symfony\Component\Uid\AbstractUid;
3335

3436
/**
3537
* Serializer serializes and deserializes data.
@@ -247,34 +249,56 @@ public function supportsDenormalization(mixed $data, string $type, string $forma
247249
private function getNormalizer(mixed $data, ?string $format, array $context): ?NormalizerInterface
248250
{
249251
$type = \is_object($data) ? $data::class : 'native-'.\gettype($data);
250-
252+
$minCached = CacheableSupport::SupportAlways;
253+
$minUncached = CacheableSupport::SupportNever;
251254
if (!isset($this->normalizerCache[$format][$type])) {
255+
$minCached = CacheableSupport::Support;
256+
$minUncached = CacheableSupport::SupportNot;
252257
$this->normalizerCache[$format][$type] = [];
253-
254258
foreach ($this->normalizers as $k => $normalizer) {
255259
if (!$normalizer instanceof NormalizerInterface) {
256260
continue;
257261
}
258262

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;
263+
$support = $this->supportsNormalizationWrapper($normalizer, $data, $format, $context);
264+
if (CacheableSupport::SupportNever === $support) {
265+
continue;
266+
}
267+
268+
$this->normalizerCache[$format][$type][$k] = $support;
269+
if (CacheableSupport::SupportAlways === $support) {
263270
break;
264271
}
265272
}
266273
}
267274

268275
foreach ($this->normalizerCache[$format][$type] as $k => $cached) {
269276
$normalizer = $this->normalizers[$k];
270-
if ($cached || $normalizer->supportsNormalization($data, $format, $context)) {
277+
if ($cached->value >= $minCached->value || ($cached->value > $minUncached->value && $this->supportsNormalizationWrapper($normalizer, $data, $format, $context)->value > CacheableSupport::SupportNot->value)) {
271278
return $normalizer;
272279
}
273280
}
274281

275282
return null;
276283
}
277284

285+
/**
286+
* Backwards-Compatibility layer for CacheableSupportsMethodInterface -> CacheableSupport.
287+
*/
288+
private function supportsNormalizationWrapper(NormalizerInterface $normalizer, mixed $data, ?string $format, array $context): CacheableSupport
289+
{
290+
$value = $normalizer->supportsNormalization($data, $format, $context);
291+
if (\is_bool($value)) {
292+
trigger_deprecation('symfony/serializer', '6.2', 'Returning boolean from "%s::%s" is deprecated, return "%s" instead.', NormalizerInterface::class, 'supports()', CacheableSupport::class);
293+
}
294+
295+
return match ($value) {
296+
true => $normalizer instanceof CacheableSupportsMethodInterface && $normalizer->hasCacheableSupportsMethod() ? CacheableSupport::SupportAlways : CacheableSupport::Support,
297+
false => $normalizer instanceof CacheableSupportsMethodInterface && $normalizer->hasCacheableSupportsMethod() ? CacheableSupport::SupportNever : CacheableSupport::SupportNot,
298+
default => $value
299+
};
300+
}
301+
278302
/**
279303
* Returns a matching denormalizer.
280304
*
@@ -285,33 +309,57 @@ private function getNormalizer(mixed $data, ?string $format, array $context): ?N
285309
*/
286310
private function getDenormalizer(mixed $data, string $class, ?string $format, array $context): ?DenormalizerInterface
287311
{
312+
$minCached = CacheableSupport::SupportAlways;
313+
$minUncached = CacheableSupport::SupportNever;
288314
if (!isset($this->denormalizerCache[$format][$class])) {
315+
$minCached = CacheableSupport::Support;
316+
$minUncached = CacheableSupport::SupportNot;
289317
$this->denormalizerCache[$format][$class] = [];
290318

291319
foreach ($this->normalizers as $k => $normalizer) {
292320
if (!$normalizer instanceof DenormalizerInterface) {
293321
continue;
294322
}
295323

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;
324+
$support = $this->supportsDenormalizationWrapper($normalizer, $data, $class, $format, $context);
325+
if (CacheableSupport::SupportNever === $support) {
326+
continue;
327+
}
328+
329+
$this->denormalizerCache[$format][$class][$k] = $support;
330+
if (CacheableSupport::SupportAlways === $support) {
300331
break;
301332
}
302333
}
303334
}
304335

305336
foreach ($this->denormalizerCache[$format][$class] as $k => $cached) {
306337
$normalizer = $this->normalizers[$k];
307-
if ($cached || $normalizer->supportsDenormalization($data, $class, $format, $context)) {
338+
if ($cached->value >= $minCached->value || ($cached->value > $minUncached->value && $this->supportsDenormalizationWrapper($normalizer, $data, $class, $format, $context)->value > CacheableSupport::SupportNot->value)) {
308339
return $normalizer;
309340
}
310341
}
311342

312343
return null;
313344
}
314345

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