Skip to content

Commit a068f7c

Browse files
Sergey Danilchenkocweiske
authored andcommitted
Fix mapping objects implementing ArrayAccess (not collections)
Only handle objects as array when they implement both ArrayAccess and Traversable. BC break! ---- Originally, JsonMapper handled objects extending ArrayObject as arrays. Extending own collection classes from ArrayObject is not always feasible (issue #175, #175), so a way was sought to rely on interfaces only. Patch #197 (#197) changed the implementation to check for the ArrayAccess interface instead of ArrayObject. This unfortunately breaks objects-that-allow-array-access-but-are-not-traversable-arrays (issue #224, #224), for example when you allow array access to properties stored in some internal variable. The correct solution is to check that the object implements ArrayAcces *and* Traversable - then we can be sure the object is intended to be used with e.g. foreach(). Resolves: #224
1 parent 8e76efb commit a068f7c

File tree

5 files changed

+81
-3
lines changed

5 files changed

+81
-3
lines changed

src/JsonMapper.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,9 @@ public function map($json, $object)
301301
$array = array();
302302
$subtype = $type;
303303
} else {
304-
if (is_a($type, 'ArrayAccess', true)) {
304+
if (is_a($type, 'ArrayAccess', true)
305+
&& is_a($type, 'Traversable', true)
306+
) {
305307
$array = $this->createInstance($type, false, $jvalue);
306308
}
307309
}

tests/ArrayTest.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,24 @@ public function testMapSimpleArrayObject()
204204
$this->assertSame(1, $sn->pSimpleArrayObject['zwei']);
205205
}
206206

207-
public function testMapSimpleArrayAccess()
207+
public function testMapArrayAccessObject()
208+
{
209+
$jm = new JsonMapper();
210+
$sn = $jm->map(
211+
json_decode(
212+
'{"pArrayAccessObject":{"eins": 1,"zwei": "two","valueObject":{"value": 1, "publicValue": 2}}}'
213+
),
214+
new JsonMapperTest_Array()
215+
);
216+
$this->assertInstanceOf(ArrayAccess::class, $sn->pArrayAccessObject);
217+
$this->assertSame(1, $sn->pArrayAccessObject['eins']);
218+
$this->assertSame('two', $sn->pArrayAccessObject['zwei']);
219+
$this->assertInstanceOf(JsonMapperTest_ValueObject::class, $sn->pArrayAccessObject['valueObject']);
220+
$this->assertSame(2, $sn->pArrayAccessObject['valueObject']->publicValue);
221+
}
222+
223+
public function testMapArrayAccessCollection()
224+
208225
{
209226
$jm = new JsonMapper();
210227
$sn = $jm->map(
@@ -214,6 +231,7 @@ public function testMapSimpleArrayAccess()
214231
new JsonMapperTest_Array()
215232
);
216233
$this->assertInstanceOf(ArrayAccess::class, $sn->pArrayAccessCollection);
234+
$this->assertInstanceOf(Traversable::class, $sn->pArrayAccessCollection);
217235
$this->assertSame(1, $sn->pArrayAccessCollection['eins']);
218236
$this->assertSame('two', $sn->pArrayAccessCollection['zwei']);
219237
}

tests/support/JsonMapperTest/Array.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ class JsonMapperTest_Array
8181
*/
8282
public $pArrayObjectList;
8383

84+
/**
85+
* @var JsonMapperTest_ArrayAccessObject
86+
*/
87+
public $pArrayAccessObject;
88+
8489
/**
8590
* @var JsonMapperTest_ArrayAccessCollection
8691
*/

tests/support/JsonMapperTest/ArrayAccessCollection.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
class JsonMapperTest_ArrayAccessCollection implements \ArrayAccess
5+
class JsonMapperTest_ArrayAccessCollection implements \ArrayAccess, \IteratorAggregate
66
{
77
/**
88
* @var array
@@ -32,4 +32,10 @@ public function offsetUnset($offset)
3232
{
3333
unset($this->data[$offset]);
3434
}
35+
36+
#[\ReturnTypeWillChange]
37+
public function getIterator()
38+
{
39+
return new \ArrayIterator($this->data);
40+
}
3541
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
require_once __DIR__ . '/ValueObject.php';
6+
7+
class JsonMapperTest_ArrayAccessObject implements \ArrayAccess
8+
{
9+
/**
10+
* @var int
11+
*/
12+
public $eins;
13+
14+
/**
15+
* @var string
16+
*/
17+
public $zwei;
18+
19+
/**
20+
* @var JsonMapperTest_ValueObject
21+
*/
22+
public $valueObject;
23+
24+
#[\ReturnTypeWillChange]
25+
public function offsetExists($offset)
26+
{
27+
return isset($this->$offset);
28+
}
29+
30+
#[\ReturnTypeWillChange]
31+
public function offsetGet($offset)
32+
{
33+
return $this->$offset ?? null;
34+
}
35+
36+
#[\ReturnTypeWillChange]
37+
public function offsetSet($offset, $value)
38+
{
39+
$this->$offset = $value;
40+
}
41+
42+
#[\ReturnTypeWillChange]
43+
public function offsetUnset($offset)
44+
{
45+
unset($this->$offset);
46+
}
47+
}

0 commit comments

Comments
 (0)