From 2d8070860171a4da972bd27be684ff5126e32ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Gerritsen?= Date: Thu, 28 Oct 2021 15:53:42 +0200 Subject: [PATCH 1/3] add test with problematic floats to type hinted array serialiyation test --- tests/Serializer/JsonSerializationTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Serializer/JsonSerializationTest.php b/tests/Serializer/JsonSerializationTest.php index fc37f3c5a..67d67a089 100644 --- a/tests/Serializer/JsonSerializationTest.php +++ b/tests/Serializer/JsonSerializationTest.php @@ -372,6 +372,9 @@ public function getTypeHintedArrays() [['a', 'b'], '{"0":"a","1":"b"}', SerializationContext::create()->setInitialType('array')], [['a' => 'a', 'b' => 'b'], '{"a":"a","b":"b"}', SerializationContext::create()->setInitialType('array')], + + [[15.6, 2], '[15.6,2.0]', SerializationContext::create()->setInitialType('array')], + [[5.2*3, 2], '[15.6,2.0]', SerializationContext::create()->setInitialType('array')], ]; } From 142152b7373a7f66e9d0f3e4d2ab51b5b8c1fa9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Gerritsen?= Date: Thu, 28 Oct 2021 17:06:15 +0200 Subject: [PATCH 2/3] add percision and rounding mode parameter to float type --- .gitignore | 1 + doc/reference/annotations.rst | 6 ++ src/AbstractVisitor.php | 20 ++++++ src/JsonSerializationVisitor.php | 10 ++- src/XmlSerializationVisitor.php | 7 ++ tests/Fixtures/ObjectWithFloatProperty.php | 84 ++++++++++++++++++++++ tests/Serializer/JsonSerializationTest.php | 27 ++++++- tests/Serializer/XmlSerializationTest.php | 26 +++++++ 8 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 tests/Fixtures/ObjectWithFloatProperty.php diff --git a/.gitignore b/.gitignore index dc0d257f5..46075989f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ phpstan.xml .phpcs-cache /doc/_build/* /.phpunit.result.cache +**/.DS_STORE diff --git a/doc/reference/annotations.rst b/doc/reference/annotations.rst index 0a855951b..0808b1366 100644 --- a/doc/reference/annotations.rst +++ b/doc/reference/annotations.rst @@ -354,6 +354,12 @@ Available Types: +------------------------------------------------------------+--------------------------------------------------+ | double or float | Primitive double | +------------------------------------------------------------+--------------------------------------------------+ +| double<2> or float<2> | Primitive double with percision | ++------------------------------------------------------------+--------------------------------------------------+ +| double<2, 'HALF_DOWN'> or float<2, 'HALF_DOWN'> | Primitive double with percision and | +| | Rounding Mode. | +| | (HALF_UP, HALF_DOWN, HALF_EVEN HALF_ODD) | ++------------------------------------------------------------+--------------------------------------------------+ | string | Primitive string | +------------------------------------------------------------+--------------------------------------------------+ | array | An array with arbitrary keys, and values. | diff --git a/src/AbstractVisitor.php b/src/AbstractVisitor.php index e56906b77..6e86ea81d 100644 --- a/src/AbstractVisitor.php +++ b/src/AbstractVisitor.php @@ -86,4 +86,24 @@ protected function assertValueCanCastToFloat($value): void throw new NonFloatCastableTypeException($value); } } + + protected function mapRoundMode(?string $roundMode = null): int + { + switch ($roundMode) { + case 'HALF_DOWN': + $roundMode = PHP_ROUND_HALF_DOWN; + break; + case 'HALF_EVEN': + $roundMode = PHP_ROUND_HALF_EVEN; + break; + case 'HALF_ODD': + $roundMode = PHP_ROUND_HALF_ODD; + break; + case 'HALF_UP': + default: + $roundMode = PHP_ROUND_HALF_UP; + } + + return $roundMode; + } } diff --git a/src/JsonSerializationVisitor.php b/src/JsonSerializationVisitor.php index fbee58249..72c67746b 100644 --- a/src/JsonSerializationVisitor.php +++ b/src/JsonSerializationVisitor.php @@ -70,7 +70,15 @@ public function visitInteger(int $data, array $type) */ public function visitDouble(float $data, array $type) { - return $data; + $percision = $type['params'][0] ?? null; + if (!is_int($percision)) { + return $data; + } + + $roundMode = $type['params'][1] ?? null; + $roundMode = $this->mapRoundMode($roundMode); + + return round($data, $percision, $roundMode); } /** diff --git a/src/XmlSerializationVisitor.php b/src/XmlSerializationVisitor.php index a5edbece5..bdf090932 100644 --- a/src/XmlSerializationVisitor.php +++ b/src/XmlSerializationVisitor.php @@ -179,6 +179,13 @@ public function visitInteger(int $data, array $type) */ public function visitDouble(float $data, array $type) { + $percision = $type['params'][0] ?? null; + if (is_int($percision)) { + $roundMode = $type['params'][1] ?? null; + $roundMode = $this->mapRoundMode($roundMode); + $data = round($data, $percision, $roundMode); + } + return $this->document->createTextNode(var_export((float) $data, true)); } diff --git a/tests/Fixtures/ObjectWithFloatProperty.php b/tests/Fixtures/ObjectWithFloatProperty.php new file mode 100644 index 000000000..c16d9b849 --- /dev/null +++ b/tests/Fixtures/ObjectWithFloatProperty.php @@ -0,0 +1,84 @@ +") + * @var float + */ + #[Type(name: 'float<2, "HALF_DOWN">')] + private $floatingPointHalfDown; + + /** + * @Type("double<1, 'HALF_EVEN'>") + * @var float + */ + #[Type(name: 'double<1, "HALF_EVEN">')] + private $floatingPointHalfEven; + + /** + * @Type("float<1, 'HALF_ODD'>") + * @var float + */ + #[Type(name: 'float<1, "HALF_ODD">')] + private $floatingPointHalfOdd; + + /** + * @Type("double<2>") + * @var float + */ + #[Type(name: 'double<2, "HALF_UP">')] + private $floatingPointHalfUp; + + public function __construct( + float $floatingPointUnchanged, + float $floatingPointHalfDown, + float $floatingPointHalfEven, + float $floatingPointHalfOdd, + float $floatingPointHalfUp + ) { + $this->floatingPointUnchanged = $floatingPointUnchanged; + $this->floatingPointHalfDown = $floatingPointHalfDown; + $this->floatingPointHalfEven = $floatingPointHalfEven; + $this->floatingPointHalfOdd = $floatingPointHalfOdd; + $this->floatingPointHalfUp = $floatingPointHalfUp; + } + + public function getFloatingPointUnchanged(): float + { + return $this->floatingPointUnchanged; + } + + public function getFloatingPointHalfDown(): float + { + return $this->floatingPointHalfDown; + } + + public function getFloatingPointHalfEven(): float + { + return $this->floatingPointHalfEven; + } + + public function getFloatingPointHalfOdd(): float + { + return $this->floatingPointHalfOdd; + } + + public function getFloatingPointHalfUp(): float + { + return $this->floatingPointHalfUp; + } +} diff --git a/tests/Serializer/JsonSerializationTest.php b/tests/Serializer/JsonSerializationTest.php index 67d67a089..7df805a99 100644 --- a/tests/Serializer/JsonSerializationTest.php +++ b/tests/Serializer/JsonSerializationTest.php @@ -14,6 +14,7 @@ use JMS\Serializer\Tests\Fixtures\AuthorList; use JMS\Serializer\Tests\Fixtures\FirstClassMapCollection; use JMS\Serializer\Tests\Fixtures\ObjectWithEmptyArrayAndHash; +use JMS\Serializer\Tests\Fixtures\ObjectWithFloatProperty; use JMS\Serializer\Tests\Fixtures\ObjectWithInlineArray; use JMS\Serializer\Tests\Fixtures\Tag; use JMS\Serializer\Visitor\Factory\JsonSerializationVisitorFactory; @@ -374,7 +375,7 @@ public function getTypeHintedArrays() [['a' => 'a', 'b' => 'b'], '{"a":"a","b":"b"}', SerializationContext::create()->setInitialType('array')], [[15.6, 2], '[15.6,2.0]', SerializationContext::create()->setInitialType('array')], - [[5.2*3, 2], '[15.6,2.0]', SerializationContext::create()->setInitialType('array')], + [[5.2 * 3, 2], '[15.6,2.0]', SerializationContext::create()->setInitialType('array')], ]; } @@ -431,6 +432,30 @@ public function testTypeHintedArrayAndStdClassSerialization(array $array, $expec self::assertEquals($expected, $this->serialize($array, $context)); } + public function testSerialisationWithPercisionForFloat(): void + { + $objectWithFloat = new ObjectWithFloatProperty( + 1.555555555, + 1.555, + 1.15, + 1.15, + 1.555 + ); + + $result = $this->serialize($objectWithFloat, SerializationContext::create()); + + static::assertEquals( + '{' + . '"floating_point_unchanged":1.555555555,' + . '"floating_point_half_down":1.55,' + . '"floating_point_half_even":1.2,' + . '"floating_point_half_odd":1.1,' + . '"floating_point_half_up":1.56' + . '}', + $result + ); + } + protected function getFormat() { return 'json'; diff --git a/tests/Serializer/XmlSerializationTest.php b/tests/Serializer/XmlSerializationTest.php index da8b0a389..9f725ea82 100644 --- a/tests/Serializer/XmlSerializationTest.php +++ b/tests/Serializer/XmlSerializationTest.php @@ -26,6 +26,7 @@ use JMS\Serializer\Tests\Fixtures\Discriminator\ObjectWithXmlNotCDataDiscriminatorParent; use JMS\Serializer\Tests\Fixtures\Input; use JMS\Serializer\Tests\Fixtures\InvalidUsageOfXmlValue; +use JMS\Serializer\Tests\Fixtures\ObjectWithFloatProperty; use JMS\Serializer\Tests\Fixtures\ObjectWithNamespacesAndList; use JMS\Serializer\Tests\Fixtures\ObjectWithNamespacesAndNestedList; use JMS\Serializer\Tests\Fixtures\ObjectWithVirtualXmlProperties; @@ -569,6 +570,31 @@ public function testDoubleEncoding() setlocale(LC_ALL, $locale); } + public function testSerialisationWithPercisionForFloat(): void + { + $objectWithFloat = new ObjectWithFloatProperty( + 1.555555555, + 1.555, + 1.15, + 1.15, + 1.555 + ); + + $result = $this->serialize($objectWithFloat, SerializationContext::create()); + + static::assertXmlStringEqualsXmlString( + ' + + 1.555555555 + 1.55 + 1.2 + 1.1 + 1.56 + ', + $result + ); + } + private function xpathFirstToString(\SimpleXMLElement $xml, $xpath) { $nodes = $xml->xpath($xpath); From 9c3e9f2516df3d09ff208b2b2331ea2ec7caa078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Gerritsen?= Date: Thu, 28 Oct 2021 17:54:40 +0200 Subject: [PATCH 3/3] removed ignored phpstan error from phpstan.neon --- phpstan.neon.dist | 1 - 1 file changed, 1 deletion(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 95c672822..0b55a2831 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -5,7 +5,6 @@ parameters: - '~Class Doctrine\\Common\\Persistence\\Proxy not found~' - '~Class Doctrine\\ODM\\MongoDB\\PersistentCollection not found~' - '~Class Symfony\\Component\\Translation\\TranslatorInterface not found~' - - '#Instantiated class Doctrine\\Common\\Cache\\FilesystemCache not found\.#' - '#Constructor of class JMS\\Serializer\\Annotation\\.*? has an unused parameter#' paths: