Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7d3be64

Browse filesBrowse files
valtzumtarld
andcommitted
Add NumberNormalizer
Co-authored-by: Mathias Arlaud <mathias.arlaud@gmail.com>
1 parent 7e9ecaf commit 7d3be64
Copy full SHA for 7d3be64

File tree

6 files changed

+214
-0
lines changed
Filter options

6 files changed

+214
-0
lines changed

‎psalm.xml

Copy file name to clipboardExpand all lines: psalm.xml
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
<referencedClass name="UnitEnum"/>
3333
<!-- These classes have been added in PHP 8.2 -->
3434
<referencedClass name="Random\*"/>
35+
<!-- These classes have been added in PHP 8.4 -->
36+
<referencedClass name="BcMath\Number"/>
3537
</errorLevel>
3638
</UndefinedClass>
3739
<UndefinedDocblockClass>

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
use Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter;
171171
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
172172
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
173+
use Symfony\Component\Serializer\Normalizer\NumberNormalizer;
173174
use Symfony\Component\Serializer\Serializer;
174175
use Symfony\Component\Stopwatch\Stopwatch;
175176
use Symfony\Component\String\LazyString;
@@ -1933,6 +1934,11 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
19331934
$container->removeDefinition('serializer.normalizer.mime_message');
19341935
}
19351936

1937+
// BC layer Serializer < 7.3
1938+
if (!class_exists(NumberNormalizer::class)) {
1939+
$container->removeDefinition('serializer.normalizer.number');
1940+
}
1941+
19361942
// BC layer Serializer < 7.2
19371943
if (!class_exists(SnakeCaseToCamelCaseNameConverter::class)) {
19381944
$container->removeDefinition('serializer.name_converter.snake_case_to_camel_case');

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer;
4545
use Symfony\Component\Serializer\Normalizer\MimeMessageNormalizer;
4646
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
47+
use Symfony\Component\Serializer\Normalizer\NumberNormalizer;
4748
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
4849
use Symfony\Component\Serializer\Normalizer\ProblemNormalizer;
4950
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
@@ -221,5 +222,8 @@
221222

222223
->set('serializer.normalizer.backed_enum', BackedEnumNormalizer::class)
223224
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915])
225+
226+
->set('serializer.normalizer.number', NumberNormalizer::class)
227+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915])
224228
;
225229
};

‎src/Symfony/Component/Serializer/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Deprecate the `CompiledClassMetadataFactory` and `CompiledClassMetadataCacheWarmer` classes
88
* Register `NormalizerInterface` and `DenormalizerInterface` aliases for named serializers
9+
* Add `NumberNormalizer` to normalize `BcMath\Number` as `string`
910

1011
7.2
1112
---
+68Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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 BcMath\Number;
15+
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
16+
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
17+
18+
/**
19+
* Normalizes a {@see Number} to a string.
20+
*/
21+
final class NumberNormalizer implements NormalizerInterface, DenormalizerInterface
22+
{
23+
public function getSupportedTypes(?string $format): array
24+
{
25+
return [
26+
Number::class => true,
27+
];
28+
}
29+
30+
public function normalize(mixed $data, ?string $format = null, array $context = []): string
31+
{
32+
if (!$data instanceof Number) {
33+
throw new InvalidArgumentException(\sprintf('The data must be an instance of "%s".', Number::class));
34+
}
35+
36+
return (string) $data;
37+
}
38+
39+
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
40+
{
41+
return $data instanceof Number;
42+
}
43+
44+
/**
45+
* @throws NotNormalizableValueException
46+
*/
47+
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): Number
48+
{
49+
if (Number::class !== $type) {
50+
throw new InvalidArgumentException(\sprintf('Only "%s" type is supported.', Number::class));
51+
}
52+
53+
if (!\is_string($data) && !\is_int($data)) {
54+
throw NotNormalizableValueException::createForUnexpectedDataType('The data is neither a "string" nor an "int", you should pass a "string" that represents a decimal number, or an "int".', $data, ['string', 'int'], $context['deserialization_path'] ?? null, true);
55+
}
56+
57+
try {
58+
return new Number($data);
59+
} catch (\ValueError $e) {
60+
throw NotNormalizableValueException::createForUnexpectedDataType('The data must represent a decimal number', $data, ['string', 'int'], $context['deserialization_path'] ?? null, true, 0, $e);
61+
}
62+
}
63+
64+
public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool
65+
{
66+
return Number::class === $type && null !== $data;
67+
}
68+
}
+133Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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 BcMath\Number;
15+
use PHPUnit\Framework\TestCase;
16+
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
17+
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
18+
use Symfony\Component\Serializer\Normalizer\NumberNormalizer;
19+
20+
/**
21+
* @requires PHP 8.4
22+
* @requires extension bcmath
23+
*/
24+
class NumberNormalizerTest extends TestCase
25+
{
26+
private NumberNormalizer $normalizer;
27+
28+
protected function setUp(): void
29+
{
30+
$this->normalizer = new NumberNormalizer();
31+
}
32+
33+
/**
34+
* @dataProvider supportsNormalizationProvider
35+
*/
36+
public function testSupportsNormalization(mixed $data, bool $expected)
37+
{
38+
$this->assertSame($expected, $this->normalizer->supportsNormalization($data));
39+
}
40+
41+
public static function supportsNormalizationProvider(): iterable
42+
{
43+
yield 'Number object' => [new Number('1.23'), true];
44+
yield 'object with similar properties as Number' => [(object) ['value' => '1.23', 'scale' => 2], false];
45+
yield 'stdClass' => [new \stdClass(), false];
46+
yield 'string' => ['1.23', false];
47+
yield 'float' => [1.23, false];
48+
yield 'null' => [null, false];
49+
}
50+
51+
/**
52+
* @dataProvider normalizeGoodValueProvider
53+
*/
54+
public function testNormalize(mixed $data, mixed $expected)
55+
{
56+
$this->assertSame($expected, $this->normalizer->normalize($data));
57+
}
58+
59+
public static function normalizeGoodValueProvider(): iterable
60+
{
61+
yield 'Number with scale=2' => [new Number('1.23'), '1.23'];
62+
yield 'Number with scale=0' => [new Number('1'), '1'];
63+
yield 'Number with integer' => [new Number(123), '123'];
64+
}
65+
66+
/**
67+
* @dataProvider normalizeBadValueProvider
68+
*/
69+
public function testNormalizeBadValueThrows(mixed $data)
70+
{
71+
$this->expectException(InvalidArgumentException::class);
72+
$this->expectExceptionMessage('The data must be an instance of "BcMath\Number".');
73+
74+
$this->normalizer->normalize($data);
75+
}
76+
77+
public static function normalizeBadValueProvider(): iterable
78+
{
79+
yield 'stdClass' => [new \stdClass()];
80+
yield 'string' => ['1.23'];
81+
yield 'null' => [null];
82+
}
83+
84+
/**
85+
* @dataProvider supportsDenormalizationProvider
86+
*/
87+
public function testSupportsDenormalization(mixed $data, string $type, bool $expected)
88+
{
89+
$this->assertSame($expected, $this->normalizer->supportsDenormalization($data, $type));
90+
}
91+
92+
public static function supportsDenormalizationProvider(): iterable
93+
{
94+
yield 'null value, matching type' => [null, Number::class, false];
95+
yield 'null value, unmatching type' => [null, \stdClass::class, false];
96+
}
97+
98+
/**
99+
* @dataProvider denormalizeGoodValueProvider
100+
*/
101+
public function testDenormalize(mixed $data, string $type, mixed $expected)
102+
{
103+
$this->assertEquals($expected, $this->normalizer->denormalize($data, $type));
104+
}
105+
106+
public static function denormalizeGoodValueProvider(): iterable
107+
{
108+
yield 'string with decimal point' => ['1.23', Number::class, new Number('1.23')];
109+
yield 'integer as string' => ['123', Number::class, new Number('123')];
110+
yield 'integer' => [123, Number::class, new Number('123')];
111+
}
112+
113+
/**
114+
* @dataProvider denormalizeBadValueProvider
115+
*/
116+
public function testDenormalizeBadValueThrows(mixed $data, string $type, string $expectedException, string $expectedExceptionMessage)
117+
{
118+
$this->expectException($expectedException);
119+
$this->expectExceptionMessage($expectedExceptionMessage);
120+
121+
$this->normalizer->denormalize($data, $type);
122+
}
123+
124+
public static function denormalizeBadValueProvider(): iterable
125+
{
126+
yield 'null' => [null, Number::class, NotNormalizableValueException::class, 'The data is neither a "string" nor an "int", you should pass a "string" that represents a decimal number, or an "int".'];
127+
yield 'boolean' => [true, Number::class, NotNormalizableValueException::class, 'The data is neither a "string" nor an "int", you should pass a "string" that represents a decimal number, or an "int".'];
128+
yield 'object' => [new \stdClass(), Number::class, NotNormalizableValueException::class, 'The data is neither a "string" nor an "int", you should pass a "string" that represents a decimal number, or an "int".'];
129+
yield 'non-numeric string' => ['foobar', Number::class, NotNormalizableValueException::class, 'The data must represent a decimal number'];
130+
yield 'unsupported type' => ['1.23', \stdClass::class, InvalidArgumentException::class, 'Only "BcMath\Number" type is supported.'];
131+
yield 'float' => [1.23, Number::class, NotNormalizableValueException::class, 'The data is neither a "string" nor an "int", you should pass a "string" that represents a decimal number, or an "int".'];
132+
}
133+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.