diff --git a/src/main/php/text/json/patch/ArrayEnd.class.php b/src/main/php/text/json/patch/ArrayEnd.class.php index 70e4f36..8f1d88e 100755 --- a/src/main/php/text/json/patch/ArrayEnd.class.php +++ b/src/main/php/text/json/patch/ArrayEnd.class.php @@ -39,6 +39,7 @@ public function remove() { if ($this->parent->exists) { if (array_key_exists('-', $this->parent->reference)) { unset($this->parent->reference['-']); + empty($this->parent->reference) && $this->parent->reference= (object)[]; return Applied::$CLEANLY; } else if (0 === key($this->parent->reference)) { array_pop($this->parent->reference); @@ -56,7 +57,9 @@ public function remove() { */ public function add($value) { if ($this->parent->exists) { - if (empty($this->parent->reference) || 0 === key($this->parent->reference)) { + if (is_object($this->parent->reference)) { + $this->parent->reference= ['-' => $value] + (array)$this->parent->reference; + } else if (empty($this->parent->reference) || 0 === key($this->parent->reference)) { $this->parent->reference[]= $value; return Applied::$CLEANLY; } else { diff --git a/src/main/php/text/json/patch/ArrayIndex.class.php b/src/main/php/text/json/patch/ArrayIndex.class.php index f0f5cdb..9868c84 100755 --- a/src/main/php/text/json/patch/ArrayIndex.class.php +++ b/src/main/php/text/json/patch/ArrayIndex.class.php @@ -11,6 +11,8 @@ class ArrayIndex extends Address { */ public function __construct($pos, parent $parent) { $this->pos= $pos; + is_object($parent->reference) && $parent->reference= (array)$parent->reference; + if (is_array($parent->reference) && array_key_exists($this->pos, $parent->reference)) { parent::__construct($parent->reference[$this->pos], $parent); } else { diff --git a/src/main/php/text/json/patch/ObjectMember.class.php b/src/main/php/text/json/patch/ObjectMember.class.php index 295caa2..1d0d56d 100755 --- a/src/main/php/text/json/patch/ObjectMember.class.php +++ b/src/main/php/text/json/patch/ObjectMember.class.php @@ -11,6 +11,8 @@ class ObjectMember extends Address { */ public function __construct($name, parent $parent) { $this->name= $name; + is_object($parent->reference) && $parent->reference= (array)$parent->reference; + if (is_array($parent->reference) && array_key_exists($this->name, $parent->reference)) { parent::__construct($parent->reference[$this->name], $parent); } else { @@ -44,6 +46,7 @@ public function modify($value) { public function remove() { if ($this->exists) { unset($this->parent->reference[$this->name]); + empty($this->parent->reference) && $this->parent->reference= (object)[]; return Applied::$CLEANLY; } else { return new PathDoesNotExist($this->path()); @@ -58,7 +61,9 @@ public function remove() { */ public function add($value) { if ($this->parent->exists) { - if (0 === key($this->parent->reference)) { + if (is_object($this->parent->reference)) { + $this->parent->reference= []; + } else if (0 === key($this->parent->reference)) { return new TypeConflict('Object operation on array target'); } diff --git a/src/test/php/text/json/patch/unittest/ChangesTest.class.php b/src/test/php/text/json/patch/unittest/ChangesTest.class.php index 5c2f38e..81a6fd9 100755 --- a/src/test/php/text/json/patch/unittest/ChangesTest.class.php +++ b/src/test/php/text/json/patch/unittest/ChangesTest.class.php @@ -1,8 +1,8 @@ 'original']; (new Pointer('/text'))->resolve($value)->modify('modified'); Assert::equals(['text' => 'modified'], $value); } + #[Test] + public function modify_object_member_on_object() { + $value= (object)['text' => 'original']; + (new Pointer('/text'))->resolve($value)->modify('modified'); + Assert::equals(['text' => 'modified'], $value); + } + #[Test] public function modify_non_existant_array_index() { $value= []; @@ -147,10 +154,24 @@ public function remove_array_index() { } #[Test] - public function remove_object_member() { + public function remove_last_object_membery() { $value= ['text' => 'original']; (new Pointer('/text'))->resolve($value)->remove(); - Assert::equals([], $value); + Assert::equals((object)[], $value); + } + + #[Test] + public function remove_object_member_from_array() { + $value= ['text' => 'original', 'key' => 'value']; + (new Pointer('/text'))->resolve($value)->remove(); + Assert::equals(['key' => 'value'], $value); + } + + #[Test] + public function remove_object_member_from_object() { + $value= (object)['text' => 'original', 'key' => 'value']; + (new Pointer('/text'))->resolve($value)->remove(); + Assert::equals(['key' => 'value'], $value); } #[Test] @@ -186,12 +207,47 @@ public function add_at_end_of_array() { } #[Test] - public function add_object_member() { + public function add_object_member_to_array() { $value= ['a' => 'original']; (new Pointer('/b'))->resolve($value)->add('added'); Assert::equals(['a' => 'original', 'b' => 'added'], $value); } + #[Test] + public function add_object_member_to_object() { + $value= (object)['a' => 'original']; + (new Pointer('/b'))->resolve($value)->add('added'); + Assert::equals(['a' => 'original', 'b' => 'added'], $value); + } + + #[Test] + public function add_object_member_to_empty() { + $value= (object)[]; + (new Pointer('/a'))->resolve($value)->add('added'); + Assert::equals(['a' => 'added'], $value); + } + + #[Test] + public function dash_for_object() { + $value= (object)['key' => 'value']; + (new Pointer('/-'))->resolve($value)->add('added'); + Assert::equals(['key' => 'value', '-' => 'added'], $value); + } + + #[Test] + public function modify_array_index_of_object() { + $value= (object)['1' => 'test']; + (new Pointer('/1'))->resolve($value)->modify('changed'); + Assert::equals(['1' => 'changed'], $value); + } + + #[Test] + public function dash_for_empty_object() { + $value= (object)[]; + (new Pointer('/-'))->resolve($value)->add('added'); + Assert::equals(['-' => 'added'], $value); + } + #[Test] public function cannot_add_object_member_to_array() { $value= [1, 2, 3]; @@ -209,4 +265,14 @@ public function cannot_add_to_nonexistant_object() { $value= []; Assert::instance('text.json.patch.PathDoesNotExist', (new Pointer('/non-existant/member'))->resolve($value)->add('test')); } + + #[Test] + public function remove_then_add_back_object_member() { + $value= ['text' => 'original']; + $ptr= new Pointer('/text'); + $ptr->resolve($value)->remove(); + $ptr->resolve($value)->add('changed'); + + Assert::equals(['text' => 'changed'], $value); + } } \ No newline at end of file diff --git a/src/test/php/text/json/patch/unittest/RemoveOperationTest.class.php b/src/test/php/text/json/patch/unittest/RemoveOperationTest.class.php index 0ee1795..243293e 100755 --- a/src/test/php/text/json/patch/unittest/RemoveOperationTest.class.php +++ b/src/test/php/text/json/patch/unittest/RemoveOperationTest.class.php @@ -1,8 +1,7 @@ 'bar'], $value); } + #[Test] + public function removing_last_object_member() { + $operation= new RemoveOperation('/baz'); + + $value= ['baz' => 'qux']; + Assert::equals(Applied::$CLEANLY, $operation->applyTo($value)); + Assert::equals((object)[], $value); + } + #[Test] public function removing_an_array_element() { $operation= new RemoveOperation('/foo/1'); @@ -54,7 +62,7 @@ public function dash_has_no_special_meaning_for_non_arrays() { $value= ['-' => 4]; Assert::equals(Applied::$CLEANLY, $operation->applyTo($value)); - Assert::equals([], $value); + Assert::equals((object)[], $value); } #[Test]