Skip to content

Commit e9ed1e8

Browse files
[Serializer] Add support of PHP backed enumerations
1 parent 0c1343f commit e9ed1e8

File tree

6 files changed

+257
-0
lines changed

6 files changed

+257
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml

+5
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@
4747
<tag name="serializer.normalizer" priority="-915" />
4848
</service>
4949

50+
<service id="serializer.normalizer.backed_enum" class="Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer">
51+
<!-- Run before serializer.normalizer.object -->
52+
<tag name="serializer.normalizer" priority="-915" />
53+
</service>
54+
5055
<service id="serializer.normalizer.data_uri" class="Symfony\Component\Serializer\Normalizer\DataUriNormalizer">
5156
<argument type="service" id="mime_types" on-invalid="null" />
5257
<!-- Run before serializer.normalizer.object -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
15+
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
16+
17+
/**
18+
* Normalizes a {@see \BackedEnum} enumeration to a string or an integer.
19+
*
20+
* @author Alexandre Daubois <alex.daubois@gmail.com>
21+
*/
22+
final class BackedEnumNormalizer implements NormalizerInterface, DenormalizerInterface, CacheableSupportsMethodInterface
23+
{
24+
/**
25+
* {@inheritdoc}
26+
*
27+
* @throws InvalidArgumentException
28+
*
29+
* @return int|string
30+
*/
31+
public function normalize($object, $format = null, array $context = [])
32+
{
33+
if (!$object instanceof \BackedEnum) {
34+
throw new InvalidArgumentException('The data must belong to a backed enumeration.');
35+
}
36+
37+
return $object->value;
38+
}
39+
40+
/**
41+
* {@inheritdoc}
42+
*/
43+
public function supportsNormalization($data, $format = null)
44+
{
45+
return $data instanceof \BackedEnum;
46+
}
47+
48+
/**
49+
* {@inheritdoc}
50+
*
51+
* @throws NotNormalizableValueException
52+
*/
53+
public function denormalize($data, $type, $format = null, array $context = [])
54+
{
55+
if (!is_subclass_of($type, \BackedEnum::class)) {
56+
throw new InvalidArgumentException('The data must belong to a backed enumeration.');
57+
}
58+
59+
if (!\is_int($data) && !\is_string($data)) {
60+
throw new NotNormalizableValueException('The data is neither an integer nor a string, you should pass an integer or a string that can be parsed as an enumeration case of type '.$type.'.');
61+
}
62+
63+
try {
64+
return $type::from($data);
65+
} catch (\ValueError $e) {
66+
throw new NotNormalizableValueException($e->getMessage(), $e->getCode(), $e);
67+
}
68+
}
69+
70+
/**
71+
* {@inheritdoc}
72+
*/
73+
public function supportsDenormalization($data, $type, $format = null)
74+
{
75+
return is_subclass_of($type, \BackedEnum::class);
76+
}
77+
78+
/**
79+
* {@inheritdoc}
80+
*/
81+
public function hasCacheableSupportsMethod(): bool
82+
{
83+
return true;
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Symfony\Component\Serializer\Tests\Fixtures;
4+
5+
enum IntegerBackedEnumDummy: int
6+
{
7+
case SUCCESS = 200;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Symfony\Component\Serializer\Tests\Fixtures;
4+
5+
enum StringBackedEnumDummy: string
6+
{
7+
case GET = 'GET';
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Symfony\Component\Serializer\Tests\Fixtures;
4+
5+
enum UnitEnumDummy
6+
{
7+
case GET;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Tests\Normalizer;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
16+
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
17+
use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer;
18+
use Symfony\Component\Serializer\Tests\Fixtures\IntegerBackedEnumDummy;
19+
use Symfony\Component\Serializer\Tests\Fixtures\StringBackedEnumDummy;
20+
use Symfony\Component\Serializer\Tests\Fixtures\UnitEnumDummy;
21+
22+
/**
23+
* @author Alexandre Daubois <alex.daubois@gmail.com>
24+
*/
25+
class BackedEnumNormalizerTest extends TestCase
26+
{
27+
/**
28+
* @var BackedEnumNormalizer
29+
*/
30+
private $normalizer;
31+
32+
protected function setUp(): void
33+
{
34+
$this->normalizer = new BackedEnumNormalizer();
35+
}
36+
37+
/**
38+
* @requires PHP 8.1
39+
*/
40+
public function testSupportsNormalization()
41+
{
42+
$this->assertTrue($this->normalizer->supportsNormalization(StringBackedEnumDummy::GET));
43+
$this->assertTrue($this->normalizer->supportsNormalization(IntegerBackedEnumDummy::SUCCESS));
44+
$this->assertFalse($this->normalizer->supportsNormalization(UnitEnumDummy::GET));
45+
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
46+
}
47+
48+
/**
49+
* @requires PHP 8.1
50+
*/
51+
public function testNormalize()
52+
{
53+
$this->assertSame('GET', $this->normalizer->normalize(StringBackedEnumDummy::GET));
54+
$this->assertSame(200, $this->normalizer->normalize(IntegerBackedEnumDummy::SUCCESS));
55+
}
56+
57+
/**
58+
* @requires PHP 8.1
59+
*/
60+
public function testNormalizeBadObjectTypeThrowsException()
61+
{
62+
$this->expectException(InvalidArgumentException::class);
63+
$this->normalizer->normalize(new \stdClass());
64+
}
65+
66+
/**
67+
* @requires PHP 8.1
68+
*/
69+
public function testSupportsDenormalization()
70+
{
71+
$this->assertTrue($this->normalizer->supportsDenormalization(null, StringBackedEnumDummy::class));
72+
$this->assertTrue($this->normalizer->supportsDenormalization(null, IntegerBackedEnumDummy::class));
73+
$this->assertFalse($this->normalizer->supportsDenormalization(null, UnitEnumDummy::class));
74+
$this->assertFalse($this->normalizer->supportsDenormalization(null, \stdClass::class));
75+
}
76+
77+
/**
78+
* @requires PHP 8.1
79+
*/
80+
public function testDenormalize()
81+
{
82+
$this->assertSame(StringBackedEnumDummy::GET, $this->normalizer->denormalize('GET', StringBackedEnumDummy::class));
83+
$this->assertSame(IntegerBackedEnumDummy::SUCCESS, $this->normalizer->denormalize(200, IntegerBackedEnumDummy::class));
84+
}
85+
86+
/**
87+
* @requires PHP 8.1
88+
*/
89+
public function testDenormalizeNullValueThrowsException()
90+
{
91+
$this->expectException(NotNormalizableValueException::class);
92+
$this->normalizer->denormalize(null, StringBackedEnumDummy::class);
93+
}
94+
95+
/**
96+
* @requires PHP 8.1
97+
*/
98+
public function testDenormalizeBooleanValueThrowsException()
99+
{
100+
$this->expectException(NotNormalizableValueException::class);
101+
$this->normalizer->denormalize(true, StringBackedEnumDummy::class);
102+
}
103+
104+
/**
105+
* @requires PHP 8.1
106+
*/
107+
public function testDenormalizeObjectThrowsException()
108+
{
109+
$this->expectException(NotNormalizableValueException::class);
110+
$this->normalizer->denormalize(new \stdClass(), StringBackedEnumDummy::class);
111+
}
112+
113+
/**
114+
* @requires PHP 8.1
115+
*/
116+
public function testDenormalizeBadBackingValueThrowsException()
117+
{
118+
$this->expectException(NotNormalizableValueException::class);
119+
$this->expectExceptionMessage('"POST" is not a valid backing value for enum "'.StringBackedEnumDummy::class.'"');
120+
$this->normalizer->denormalize('POST', StringBackedEnumDummy::class);
121+
}
122+
123+
public function testNormalizeShouldThrowExceptionForNonEnumObjects()
124+
{
125+
$this->expectException(\InvalidArgumentException::class);
126+
$this->expectExceptionMessage('The data must belong to a backed enumeration.');
127+
128+
$this->normalizer->normalize(\stdClass::class);
129+
}
130+
131+
public function testDenormalizeShouldThrowExceptionForNonEnumObjects()
132+
{
133+
$this->expectException(\InvalidArgumentException::class);
134+
$this->expectExceptionMessage('The data must belong to a backed enumeration.');
135+
136+
$this->normalizer->denormalize('GET', \stdClass::class);
137+
}
138+
139+
public function testSupportsNormalizationShouldFailOnAnyPHPVersionForNonEnumObjects()
140+
{
141+
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
142+
}
143+
}

0 commit comments

Comments
 (0)