Skip to content

Commit 613b29b

Browse files
committed
Reject NULL values in arrays unless explicitly allowed
BC break! The `$bStrictNullTypes` configuration option did not cover NULL values in arrays, which is unexpected. This patch adds a new config option `$bStrictNullTypesInArrays` in JsonMapper, which defaults to `true` and rejects NULL values in arrays unless explicitly allowed by a nullable type declaration: `array[?int]`. This is a backwards compatibility break. The old behavior can be restored by setting the new configuration option to `false`. Resolves: #233 Related: #211
1 parent cf85ec1 commit 613b29b

File tree

4 files changed

+102
-4
lines changed

4 files changed

+102
-4
lines changed

README.rst

+9
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,15 @@ to make all your type definitions nullable, set:
336336
337337
$jm->bStrictNullTypes = false;
338338
339+
Since version 5.0.0, ``null`` values in arrays lead to a ``JsonMapper_Exception``
340+
unless the type is nullable - e.g. ``array[?string]`` or ``array[string|null]``.
341+
342+
To get the previous behavior back (allowing nulls even when not declared so) set:
343+
344+
.. code:: php
345+
346+
$jm->bStrictNullTypesInArrays = false;
347+
339348
340349
Logging
341350
=======

src/JsonMapper.php

+27-2
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,18 @@ class JsonMapper
7070
* Throw an exception, if null value is found
7171
* but the type of attribute does not allow nulls.
7272
*
73-
* @var bool
73+
* @var boolean
7474
*/
7575
public $bStrictNullTypes = true;
7676

77+
/**
78+
* Throw an exception if null value is found in an array
79+
* but the type of attribute does not allow nulls.
80+
*
81+
* @var boolean
82+
*/
83+
public $bStrictNullTypesInArrays = true;
84+
7785
/**
7886
* Allow mapping of private and protected properties.
7987
*
@@ -277,7 +285,8 @@ public function map($json, $object)
277285
'Empty type at property "'
278286
. $strClassName . '::$' . $key . '"'
279287
);
280-
} else if (strpos($type, '|')) {
288+
289+
} else if (strpos(str_replace('|null', '', $type), '|')) {
281290
throw new JsonMapper_Exception(
282291
'Cannot decide which of the union types shall be used: '
283292
. $type
@@ -316,9 +325,14 @@ public function map($json, $object)
316325
);
317326
}
318327

328+
$subtypeNullable = $this->isNullable($subtype);
319329
$cleanSubtype = $this->removeNullable($subtype);
320330
$subtype = $this->getFullNamespace($cleanSubtype, $strNs);
331+
if ($subtypeNullable) {
332+
$subtype = '?' . $subtype;
333+
}
321334
$child = $this->mapArray($jvalue, $array, $subtype, $key);
335+
322336
} else if ($this->isFlatType(gettype($jvalue))) {
323337
//use constructor parameter if we have a class
324338
// but only a flat type (i.e. string, int)
@@ -447,8 +461,19 @@ protected function removeUndefinedAttributes($object, $providedProperties)
447461
*/
448462
public function mapArray($json, $array, $class = null, $parent_key = '')
449463
{
464+
$isNullable = $this->isNullable($class);
465+
$class = $this->removeNullable($class);
450466
$originalClass = $class;
467+
451468
foreach ($json as $key => $jvalue) {
469+
if ($jvalue === null && !$isNullable && $this->bStrictNullTypesInArrays) {
470+
throw new JsonMapper_Exception(
471+
'JSON property'
472+
. ' "' . ($parent_key ? $parent_key : '?') . '[' . $key . ']"'
473+
. ' must not be NULL'
474+
);
475+
}
476+
452477
$class = $this->getMappedType($originalClass, $jvalue);
453478
if ($class === null) {
454479
$array[$key] = $jvalue;

tests/ArrayTest.php

+56-2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public function testMapTypedArray()
4848
public function testMapTypedSimpleArray()
4949
{
5050
$jm = new JsonMapper();
51+
$jm->bStrictNullTypesInArrays = false;
5152
$sn = $jm->map(
5253
json_decode('{"typedSimpleArray":["2014-01-02",null,"2014-05-07"]}'),
5354
new JsonMapperTest_Array()
@@ -138,10 +139,63 @@ public function testStrArrayV2()
138139
$this->assertSame(['str', '', '2.048'], $sn->strArrayV2);
139140
}
140141

141-
public function testNullArrayValue()
142+
/**
143+
* Test for an array of strings-or-null values - "@var array[string|null]"
144+
*/
145+
public function testStrMaybeNullArray()
146+
{
147+
$jm = new JsonMapper();
148+
$this->assertTrue($jm->bStrictNullTypesInArrays);
149+
$sn = $jm->map(
150+
json_decode('{"strMaybeNullArray":["str",null,2.048]}'),
151+
new JsonMapperTest_Array()
152+
);
153+
$this->assertSame(['str', null, '2.048'], $sn->strMaybeNullArray);
154+
}
155+
156+
/**
157+
* Test for an array of strings-or-null values - "@var array[?string]"
158+
* Alternative syntax
159+
*/
160+
public function testStrMaybeNullArrayV2()
161+
{
162+
$jm = new JsonMapper();
163+
$this->assertTrue($jm->bStrictNullTypesInArrays);
164+
$sn = $jm->map(
165+
json_decode('{"strMaybeNullArrayV2":["str",null,2.048]}'),
166+
new JsonMapperTest_Array()
167+
);
168+
$this->assertSame(['str', null, '2.048'], $sn->strMaybeNullArrayV2);
169+
}
170+
171+
public function testNullArrayValueStrict()
142172
{
173+
$this->expectException(JsonMapper_Exception::class);
174+
$this->expectExceptionMessage('JSON property "strArray[1]" must not be NULL');
175+
143176
$jm = new JsonMapper();
144-
$jm->bStrictNullTypes = true;
177+
$this->assertTrue(
178+
$jm->bStrictNullTypes,
179+
'Default value for bStrictNullTypes is wrong'
180+
);
181+
$this->assertTrue(
182+
$jm->bStrictNullTypesInArrays,
183+
'Default value for bStrictNullTypesInArrays is wrong'
184+
);
185+
$sn = $jm->map(
186+
json_decode('{"strArray":["a",null,"c"]}'),
187+
new JsonMapperTest_Array()
188+
);
189+
}
190+
191+
public function testNullArrayValueNonStrict()
192+
{
193+
$jm = new JsonMapper();
194+
$this->assertTrue(
195+
$jm->bStrictNullTypes,
196+
'Default value for bStrictNullTypes is wrong'
197+
);
198+
$jm->bStrictNullTypesInArrays = false;
145199
$sn = $jm->map(
146200
json_decode('{"strArray":["a",null,"c"]}'),
147201
new JsonMapperTest_Array()

tests/support/JsonMapperTest/Array.php

+10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ class JsonMapperTest_Array
3535
*/
3636
public $strArrayV2;
3737

38+
/**
39+
* @var array[string|null]
40+
*/
41+
public $strMaybeNullArray;
42+
43+
/**
44+
* @var array[?string]
45+
*/
46+
public $strMaybeNullArrayV2;
47+
3848
/**
3949
* @var JsonMapperTest_Simple[]
4050
* @see http://phpdoc.org/docs/latest/references/phpdoc/types.html#arrays

0 commit comments

Comments
 (0)