Skip to content

Commit 00b56c4

Browse files
committed
Add ability to attach to existing document
* Move JSON loading from constructor to JsonBrowser::loadJSON() * Add JsonBrowser::attach()
1 parent 360d883 commit 00b56c4

14 files changed

+152
-73
lines changed

src/Context.php

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
namespace JsonBrowser;
44

5-
use Seld\JsonLint\JsonParser;
6-
75
/**
86
* Document context
97
*
@@ -31,33 +29,13 @@ class Context
3129
*
3230
* @since 1.5.0
3331
*
34-
* @param JsonBrowser $root JsonBrowser for root node
35-
* @param string $json JSON-encoded data
32+
* @param mixed $document Reference to the target document
3633
* @param int $options Configuration options (bitmask)
3734
*/
38-
public function __construct(string $json, int $options = 0)
35+
public function __construct(&$document, int $options = 0)
3936
{
40-
// set options
37+
$this->document = &$document;
4138
$this->options = $options;
42-
43-
// decode document
44-
Exception::wrap(function () use ($json) {
45-
try {
46-
// decode via json_decode for speed
47-
$this->document = json_decode($json);
48-
if (json_last_error() != \JSON_ERROR_NONE) {
49-
throw new \Exception(json_last_error_msg(), json_last_error());
50-
}
51-
} catch (\Throwable $e) {
52-
// if decoding fails, then lint using JsonParser
53-
$parser = new JsonParser();
54-
if (!is_null($parserException = $parser->lint($json))) {
55-
throw $parserException;
56-
}
57-
// if JsonParser can decode successfully, but json_decode() cannot, complain loudly
58-
throw new \Exception('Unknown JSON decoding error'); // @codeCoverageIgnore
59-
}
60-
}, JsonBrowser::ERR_DECODING_ERROR, 'Unable to decode JSON data: %s');
6139
}
6240

6341
/**

src/JsonBrowser.php

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace JsonBrowser;
44

5+
use Seld\JsonLint\JsonParser;
6+
57
/**
68
* Helper class for working with JSON-encoded data
79
*
@@ -76,13 +78,13 @@ class JsonBrowser implements \IteratorAggregate
7678
*
7779
* @since 1.0.0
7880
*
79-
* @param string $json JSON-encoded data
8081
* @param int $options Configuration options (bitmask)
8182
*/
82-
public function __construct(string $json, int $options = 0)
83+
public function __construct(int $options = 0)
8384
{
85+
$defaultDocument = null;
8486
$this->options = $options;
85-
$this->context = new Context($json, $options);
87+
$this->context = new Context($defaultDocument, $options);
8688
}
8789

8890
/**
@@ -119,7 +121,7 @@ public function __set($key, $value)
119121
/**
120122
* Get the current node as a document root
121123
*
122-
* @since 1.6.0
124+
* @since 2.0.0
123125
*
124126
* @return self A new JsonBrowser instance pointing to the current node
125127
*/
@@ -132,6 +134,18 @@ public function asRoot() : self
132134
return $root;
133135
}
134136

137+
/**
138+
* Attach to an existing decoded document
139+
*
140+
* @since 2.0.0
141+
*
142+
* @param mixed $document Reference to the target document
143+
*/
144+
public function attach(&$document)
145+
{
146+
$this->context = new Context($document, $this->options);
147+
}
148+
135149
/**
136150
* Check whether a child element exists
137151
*
@@ -446,6 +460,39 @@ public function isType(int $types, bool $all = false) : bool
446460
return (bool)($this->getType() & $types);
447461
}
448462

463+
/**
464+
* Load document from a JSON string
465+
*
466+
* @since 2.0.0
467+
*
468+
* @param string $json JSON-encoded document
469+
*/
470+
public function loadJSON(string $json)
471+
{
472+
$document = null;
473+
474+
// decode document
475+
Exception::wrap(function () use ($json, &$document) {
476+
try {
477+
// decode via json_decode for speed
478+
$document = json_decode($json);
479+
if (json_last_error() != \JSON_ERROR_NONE) {
480+
throw new \Exception(json_last_error_msg(), json_last_error());
481+
}
482+
} catch (\Throwable $e) {
483+
// if decoding fails, then lint using JsonParser
484+
$parser = new JsonParser();
485+
if (!is_null($parserException = $parser->lint($json))) {
486+
throw $parserException;
487+
}
488+
// if JsonParser can decode successfully, but json_decode() cannot, complain loudly
489+
throw new \Exception('Unknown JSON decoding error'); // @codeCoverageIgnore
490+
}
491+
}, JsonBrowser::ERR_DECODING_ERROR, 'Unable to decode JSON data: %s');
492+
493+
$this->attach($document);
494+
}
495+
449496
/**
450497
* Check whether the current node exists in the parent document
451498
*

tests/AttachTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace JsonBrowser\Tests;
4+
5+
use JsonBrowser\JsonBrowser;
6+
use JsonBrowser\Exception;
7+
8+
/*
9+
* Test attachment to pre-existing document
10+
*
11+
* @package baacode/json-browser
12+
* @copyright (c) 2017-2018-2018 Erayd LTD
13+
* @author Steve Gilberd <steve@erayd.net>
14+
* @license ISC
15+
*/
16+
class AttachTest extends \PHPUnit\Framework\TestCase
17+
{
18+
public function testAttach()
19+
{
20+
$document = json_decode('{"childOne": "valueOne"}');
21+
$browser = new JsonBrowser();
22+
$browser->attach($document);
23+
$browser->getChild('childOne')->setValue('valueTwo');
24+
$this->assertEquals('valueTwo', $document->childOne);
25+
}
26+
}

tests/ChildTest.php

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,35 @@ class ChildTest extends \PHPUnit\Framework\TestCase
1717
{
1818
public function testChildExists()
1919
{
20-
$browser = new JsonBrowser('{"childOne": "valueOne", "2": "valueTwo"}');
20+
$browser = new JsonBrowser();
21+
$browser->loadJSON('{"childOne": "valueOne", "2": "valueTwo"}');
2122
$this->assertTrue($browser->childExists('childOne'));
2223
$this->assertTrue($browser->childExists('2'));
2324
$this->assertTrue($browser->childExists(2)); // array_key_exists() allows sloppy typing
2425
$this->assertFalse($browser->childExists('childThree'));
2526
$this->assertFalse($browser->childExists(3));
2627

27-
$browser = new JsonBrowser('["valueOne", "valueTwo"]');
28+
$browser = new JsonBrowser();
29+
$browser->loadJSON('["valueOne", "valueTwo"]');
2830
$this->assertTrue($browser->childExists(0));
2931
$this->assertTrue($browser->childExists('1'));
3032
$this->assertFalse($browser->childExists(2));
3133
$this->assertFalse($browser->childExists("childThree"));
3234

33-
$browser = new JsonBrowser('"stringValue"');
35+
$browser = new JsonBrowser();
36+
$browser->loadJSON('"stringValue"');
3437
$this->assertFalse($browser->childExists('childOne'));
3538
}
3639

3740
public function testGetChild()
3841
{
39-
$array = new JsonBrowser('["valueOne"]');
42+
$array = new JsonBrowser();
43+
$array->loadJSON('["valueOne"]');
4044
$this->assertEquals('valueOne', $array->getChild(0)->getValue());
4145
$this->assertNull($array->getChild(1)->getValue());
4246

43-
$root = new JsonBrowser('{"childOne": "valueOne", "childTwo": {"childThree": "valueThree"}}');
47+
$root = new JsonBrowser();
48+
$root->loadJSON('{"childOne": "valueOne", "childTwo": {"childThree": "valueThree"}}');
4449
$childOne = $root->getChild('childOne');
4550
$childTwo = $root->getChild('childTwo');
4651
$childThree = $childTwo->getChild('childThree');
@@ -56,15 +61,17 @@ public function testGetChild()
5661
$this->assertNull($childFour->getValue());
5762
$this->assertNull($childFive->getValue());
5863

59-
$root = new JsonBrowser('{"childOne": "valueOne"}', JsonBrowser::OPT_NONEXISTENT_EXCEPTIONS);
64+
$root = new JsonBrowser(JsonBrowser::OPT_NONEXISTENT_EXCEPTIONS);
65+
$root->loadJSON('{"childOne": "valueOne"}');
6066
$this->assertEquals('valueOne', $root->getChild('childOne')->getValue());
6167
$this->expectException(Exception::class);
6268
$root->getChild('childTwo');
6369
}
6470

6571
public function testGetRoot()
6672
{
67-
$root = new JsonBrowser('{"childOne": {"childTwo": "valueTwo"}}');
73+
$root = new JsonBrowser();
74+
$root->loadJSON('{"childOne": {"childTwo": "valueTwo"}}');
6875
$childOne = $root->getChild('childOne');
6976
$childTwo = $childOne->getChild('childTwo');
7077

@@ -75,7 +82,8 @@ public function testGetRoot()
7582

7683
public function testGetParent()
7784
{
78-
$root = new JsonBrowser('{"childOne": {"childTwo": "valueTwo"}}');
85+
$root = new JsonBrowser();
86+
$root->loadJSON('{"childOne": {"childTwo": "valueTwo"}}');
7987
$childOne = $root->getChild('childOne');
8088
$childTwo = $childOne->getChild('childTwo');
8189

@@ -86,7 +94,8 @@ public function testGetParent()
8694

8795
public function testGetSet()
8896
{
89-
$root = new JsonBrowser('{"childOne": "valueOne"}');
97+
$root = new JsonBrowser();
98+
$root->loadJSON('{"childOne": "valueOne"}');
9099
$this->assertEquals('valueOne', $root->childOne->getValue());
91100

92101
$root->childTwo = 'valueTwo';
@@ -95,7 +104,8 @@ public function testGetSet()
95104

96105
public function testDynamicGetValue()
97106
{
98-
$root = new JsonBrowser('{"childOne": "valueOne"}', JsonBrowser::OPT_GET_VALUE);
107+
$root = new JsonBrowser(JsonBrowser::OPT_GET_VALUE);
108+
$root->loadJSON('{"childOne": "valueOne"}');
99109
$this->assertEquals('valueOne', $root->childOne);
100110
}
101111
}

tests/CreateTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ public function testCreate($json, $expectSuccess)
4141
$this->expectException(Exception::class);
4242
}
4343

44-
$browser = new JsonBrowser($json);
44+
$browser = new JsonBrowser();
45+
$browser->loadJSON($json);
4546
$this->assertInstanceOf(JsonBrowser::class, $browser);
4647

4748
if ($expectSuccess) {
@@ -51,7 +52,8 @@ public function testCreate($json, $expectSuccess)
5152

5253
public function testExists()
5354
{
54-
$browser = new JsonBrowser('{"propertyOne": {"propertyTwo": "valueTwo"}}');
55+
$browser = new JsonBrowser();
56+
$browser->loadJSON('{"propertyOne": {"propertyTwo": "valueTwo"}}');
5557

5658
$this->assertTrue($browser->nodeExists());
5759
$this->assertTrue($browser->getNodeAt('#/propertyOne')->nodeExists());

tests/EqualityTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ public function dataIsEqual() : array
3434
/** @dataProvider dataIsEqual */
3535
public function testIsEqual(string $json, $compareTo, bool $isEqual)
3636
{
37-
$browser = new JsonBrowser($json);
37+
$browser = new JsonBrowser();
38+
$browser->loadJSON($json);
3839
$this->assertTrue($browser->isEqualTo($browser));
3940
$this->assertEquals($isEqual, $browser->isEqualTo($compareTo));
4041
}

tests/ExceptionTest.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,9 @@ public function testWrapException()
4848

4949
public function getExceptionBrowser()
5050
{
51-
return new JsonBrowser(
52-
'{"childOne": "valueOne", "childTwo": ["valueTwo"]}',
53-
JsonBrowser::OPT_NONEXISTENT_EXCEPTIONS
54-
);
51+
$browser = new JsonBrowser(JsonBrowser::OPT_NONEXISTENT_EXCEPTIONS);
52+
$browser->loadJSON('{"childOne": "valueOne", "childTwo": ["valueTwo"]}');
53+
return $browser;
5554
}
5655

5756
public function testNoExceptionOnValidNode()
@@ -103,7 +102,7 @@ public function testNonExistentNodeException()
103102

104103
public function testNonExistentChildValueException()
105104
{
106-
$browser = new JsonBrowser('{}', JsonBrowser::OPT_NONEXISTENT_EXCEPTIONS | JsonBrowser::OPT_GET_VALUE);
105+
$browser = new JsonBrowser(JsonBrowser::OPT_NONEXISTENT_EXCEPTIONS | JsonBrowser::OPT_GET_VALUE);
107106
$this->expectException(Exception::class);
108107
$browser->childThree;
109108
}

tests/IteratorTest.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ class IteratorTest extends \PHPUnit\Framework\TestCase
1717
{
1818
public function testEmpty()
1919
{
20-
$browser = new JsonBrowser('"notAContainer"');
20+
$browser = new JsonBrowser();
21+
$browser->loadJSON('"notAContainer"');
2122

2223
foreach ($browser as $child) {
2324
$this->assertTrue(false); // should never execute
@@ -27,7 +28,8 @@ public function testEmpty()
2728

2829
public function testArray()
2930
{
30-
$browser = new JsonBrowser('["valueOne", "valueTwo"]');
31+
$browser = new JsonBrowser();
32+
$browser->loadJSON('["valueOne", "valueTwo"]');
3133
$count = 0;
3234
foreach ($browser as $key => $child) {
3335
$this->assertEquals($count++, $key);
@@ -43,7 +45,8 @@ public function testArray()
4345

4446
public function testObject()
4547
{
46-
$browser = new JsonBrowser('{"childOne": "valueOne", "childTwo": {"childThree": "valueThree"}}');
48+
$browser = new JsonBrowser();
49+
$browser->loadJSON('{"childOne": "valueOne", "childTwo": {"childThree": "valueThree"}}');
4750

4851
$count = 0;
4952
foreach ($browser as $key => $child) {

tests/JSONTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ class JSONTest extends \PHPUnit\Framework\TestCase
1717
{
1818
public function testGetJSON()
1919
{
20-
$browser = new JsonBrowser('{"childOne": {"childTwo": ["valueThree", "valueFour"]}}');
20+
$browser = new JsonBrowser();
21+
$browser->loadJSON('{"childOne": {"childTwo": ["valueThree", "valueFour"]}}');
2122
$childTwo = $browser->getNodeAt('#/childOne/childTwo');
2223
$this->assertEquals("[\n \"valueThree\",\n \"valueFour\"\n]", $childTwo->getJSON());
2324
}

tests/PathTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ class PathTest extends \PHPUnit\Framework\TestCase
1717
{
1818
public function testGetPath()
1919
{
20-
$root = new JsonBrowser('{"child~/One": {"child%Two": ["valueThree", "valueFour"]}}');
20+
$root = new JsonBrowser();
21+
$root->loadJSON('{"child~/One": {"child%Two": ["valueThree", "valueFour"]}}');
2122
$childFour = $root->getChild('child~/One')->getChild('child%Two')->getChild(1);
2223
$childFive = $root->getChild('child~/One')->getChild(5);
2324

@@ -34,15 +35,15 @@ public function testGetPath()
3435

3536
public function testNodeByPathException()
3637
{
37-
$root = new JsonBrowser('{}', JsonBrowser::OPT_NONEXISTENT_EXCEPTIONS);
38+
$root = new JsonBrowser(JsonBrowser::OPT_NONEXISTENT_EXCEPTIONS);
3839

3940
$this->expectException(Exception::class);
4041
$root->getNodeAt('#/this/path/does/not/exist');
4142
}
4243

4344
public function testValueByPathException()
4445
{
45-
$root = new JsonBrowser('{}', JsonBrowser::OPT_NONEXISTENT_EXCEPTIONS);
46+
$root = new JsonBrowser(JsonBrowser::OPT_NONEXISTENT_EXCEPTIONS);
4647

4748
$this->expectException(Exception::class);
4849
$root->getValueAt('#/this/path/does/not/exist');

0 commit comments

Comments
 (0)