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 449dcbd

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

File tree

6 files changed

+213
-0
lines changed
Filter options

6 files changed

+213
-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
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,6 +1933,11 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
19331933
$container->removeDefinition('serializer.normalizer.mime_message');
19341934
}
19351935

1936+
// BC layer Serializer < 7.3
1937+
if (!class_exists(NumberNormalizer::class)) {
1938+
$container->removeDefinition('serializer.normalizer.number');
1939+
}
1940+
19361941
// BC layer Serializer < 7.2
19371942
if (!class_exists(SnakeCaseToCamelCaseNameConverter::class)) {
19381943
$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
@@ -5,6 +5,7 @@ CHANGELOG
55
---
66

77
* Deprecate the `CompiledClassMetadataFactory` and `CompiledClassMetadataCacheWarmer` classes
8+
* Add `NumberNormalizer` to normalize `BcMath\Number` as `string`
89

910
7.2
1011
---
+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.