From 99fea88d3258c31f120c131170250901fd32fd5f Mon Sep 17 00:00:00 2001 From: Robin van der Vliet Date: Mon, 8 Apr 2024 19:21:49 +0200 Subject: [PATCH] Support "?type" syntax for nullable types In addition to "int|null", JsonMapper now also supports "?int" - a syntax that was added to PHP in versino 7.1.0 ("Nullable type syntactic sugar"). Resolves: https://github.com/cweiske/jsonmapper/pull/235 --- README.rst | 6 +-- src/JsonMapper.php | 5 ++- tests/SimpleTest.php | 60 ++++++++++++++++++++----- tests/support/JsonMapperTest/Simple.php | 7 ++- 4 files changed, 62 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index 477098838..bda4b5daf 100644 --- a/README.rst +++ b/README.rst @@ -251,9 +251,9 @@ Supported type names - ``Suit:string|Suit:int`` - exception will be thrown if the JSON value is not present in the enum - Nullable types: - - ``int|null`` - will be ``null`` if the value in JSON is + - ``int|null`` or ``?int`` - will be ``null`` if the value in JSON is ``null``, otherwise it will be an integer - - ``Contact|null`` - will be ``null`` if the value in JSON is + - ``Contact|null`` or ``?Contact`` - will be ``null`` if the value in JSON is ``null``, otherwise it will be an object of type ``Contact`` ArrayObjects and extending classes are treated as arrays. @@ -327,7 +327,7 @@ parameters into the call. Nullables --------- JsonMapper throws an exception when a JSON property is ``null``, -unless the PHP class property has a nullable type - e.g. ``Contact|null``. +unless the PHP class property has a nullable type - e.g. ``Contact|null`` or ``?Contact``. If your API contains many fields that may be ``null`` and you do not want to make all your type definitions nullable, set: diff --git a/src/JsonMapper.php b/src/JsonMapper.php index c377d4569..d9ce33fd8 100644 --- a/src/JsonMapper.php +++ b/src/JsonMapper.php @@ -857,7 +857,8 @@ protected function hasVariadicArrayType($accessor) */ protected function isNullable($type) { - return stripos('|' . $type . '|', '|null|') !== false; + return stripos('|' . $type . '|', '|null|') !== false + || strpos('|' . $type, '|?') !== false; } /** @@ -873,7 +874,7 @@ protected function removeNullable($type) return null; } return substr( - str_ireplace('|null|', '|', '|' . $type . '|'), + str_ireplace(['|null|', '|?'], '|', '|' . $type . '|'), 1, -1 ); } diff --git a/tests/SimpleTest.php b/tests/SimpleTest.php index ce9eb2f26..ed79fe70e 100644 --- a/tests/SimpleTest.php +++ b/tests/SimpleTest.php @@ -122,40 +122,80 @@ public function testMapSimpleMixed() /** * Test for "@var int|null" with int value */ - public function testMapSimpleNullableInt() + public function testMapSimpleNullableIntWithInt() { $jm = new JsonMapper(); $sn = $jm->map( - json_decode('{"pnullable":0}'), + json_decode('{"pnullableInt":0}'), new JsonMapperTest_Simple() ); - $this->assertSame(0, $sn->pnullable); + $this->assertSame(0, $sn->pnullableInt); } /** * Test for "@var int|null" with null value */ - public function testMapSimpleNullableNull() + public function testMapSimpleNullableIntWithNull() { $jm = new JsonMapper(); $sn = $jm->map( - json_decode('{"pnullable":null}'), + json_decode('{"pnullableInt":null}'), new JsonMapperTest_Simple() ); - $this->assertNull($sn->pnullable); + $this->assertNull($sn->pnullableInt); } /** - * Test for "@var int|null" with string value + * Test for "@var int|null" with string value (force cast) */ - public function testMapSimpleNullableWrong() + public function testMapSimpleNullableIntWithWrongType() { $jm = new JsonMapper(); $sn = $jm->map( - json_decode('{"pnullable":"12345"}'), + json_decode('{"pnullableInt":"12345"}'), new JsonMapperTest_Simple() ); - $this->assertSame(12345, $sn->pnullable); + $this->assertSame(12345, $sn->pnullableInt); + } + + /** + * Test for "@var ?string" with string value + */ + public function testMapSimpleNullableStringWithString() + { + $jm = new JsonMapper(); + $sn = $jm->map( + json_decode('{"pnullableString":"test"}'), + new JsonMapperTest_Simple() + ); + $this->assertSame('test', $sn->pnullableString); + } + + /** + * Test for "@var ?string" with null value + */ + public function testMapSimpleNullableStringWithNull() + { + $jm = new JsonMapper(); + $sn = $jm->map( + json_decode('{"pnullableString":null}'), + new JsonMapperTest_Simple() + ); + $this->assertNull($sn->pnullableString); + $this->assertEquals(null, $sn->pnullableString); + } + + /** + * Test for "@var ?string" with int value (force cast) + */ + public function testMapSimpleNullableStringWithWrongType() + { + $jm = new JsonMapper(); + $sn = $jm->map( + json_decode('{"pnullableString":0}'), + new JsonMapperTest_Simple() + ); + $this->assertSame('0', $sn->pnullableString); } /** diff --git a/tests/support/JsonMapperTest/Simple.php b/tests/support/JsonMapperTest/Simple.php index 8656d0670..51b80dc22 100644 --- a/tests/support/JsonMapperTest/Simple.php +++ b/tests/support/JsonMapperTest/Simple.php @@ -43,7 +43,12 @@ class JsonMapperTest_Simple /** * @var int|null */ - public $pnullable; + public $pnullableInt; + + /** + * @var ?string + */ + public $pnullableString; /** * @var float