Skip to content

Commit

Permalink
Merge pull request #11 from swaggest/issue-9
Browse files Browse the repository at this point in the history
resolves #9
  • Loading branch information
vearutop authored Mar 23, 2018
2 parents 81a403c + 59fb701 commit 8230d84
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 11 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ Adds operation to `JsonPatch`.
#### `apply`
Applies patch to `JSON`-decoded data.

#### `setFlags`
Alters default behavior.

Available flag:

* `JsonPatch::STRICT_MODE` Disallow converting empty array to object for key creation.

### `JsonPointer`

#### `escapeSegment`
Expand Down
26 changes: 22 additions & 4 deletions src/JsonPatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@
*/
class JsonPatch implements \JsonSerializable
{
/**
* Disallow converting empty array to object for key creation
* @see JsonPointer::STRICT_MODE
*/
const STRICT_MODE = 2;

private $flags = 0;

/**
* @param int $options
* @return $this
*/
public function setFlags($options)
{
$this->flags = $options;
return $this;
}

/** @var OpPath[] */
private $operations = array();

Expand Down Expand Up @@ -118,26 +136,26 @@ public function apply(&$original, $stopOnError = true)
$pathItems = JsonPointer::splitPath($operation->path);
switch (true) {
case $operation instanceof Add:
JsonPointer::add($original, $pathItems, $operation->value, false);
JsonPointer::add($original, $pathItems, $operation->value, $this->flags);
break;
case $operation instanceof Copy:
$fromItems = JsonPointer::splitPath($operation->from);
$value = JsonPointer::get($original, $fromItems);
JsonPointer::add($original, $pathItems, $value, false);
JsonPointer::add($original, $pathItems, $value, $this->flags);
break;
case $operation instanceof Move:
$fromItems = JsonPointer::splitPath($operation->from);
$value = JsonPointer::get($original, $fromItems);
JsonPointer::remove($original, $fromItems);
JsonPointer::add($original, $pathItems, $value, false);
JsonPointer::add($original, $pathItems, $value, $this->flags);
break;
case $operation instanceof Remove:
JsonPointer::remove($original, $pathItems);
break;
case $operation instanceof Replace:
JsonPointer::get($original, $pathItems);
JsonPointer::remove($original, $pathItems);
JsonPointer::add($original, $pathItems, $operation->value, false);
JsonPointer::add($original, $pathItems, $operation->value, $this->flags);
break;
case $operation instanceof Test:
$value = JsonPointer::get($original, $pathItems);
Expand Down
24 changes: 17 additions & 7 deletions src/JsonPointer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@

class JsonPointer
{
/**
* Create intermediate keys if they don't exist
*/
const RECURSIVE_KEY_CREATION = 1;

/**
* Disallow converting empty array to object for key creation
*/
const STRICT_MODE = 2;

/**
* @param string $key
* @param bool $isURIFragmentId
Expand Down Expand Up @@ -76,10 +86,10 @@ private static function splitPathJsonString(array $pathItems)
* @param mixed $holder
* @param string[] $pathItems
* @param mixed $value
* @param bool $recursively
* @param int $flags
* @throws Exception
*/
public static function add(&$holder, $pathItems, $value, $recursively = true)
public static function add(&$holder, $pathItems, $value, $flags = self::RECURSIVE_KEY_CREATION)
{
$ref = &$holder;
while (null !== $key = array_shift($pathItems)) {
Expand All @@ -89,7 +99,7 @@ public static function add(&$holder, $pathItems, $value, $recursively = true)
Exception::EMPTY_PROPERTY_NAME_UNSUPPORTED);
}

if ($recursively) {
if ($flags & self::RECURSIVE_KEY_CREATION) {
$ref = &$ref->$key;
} else {
if (!isset($ref->$key) && count($pathItems)) {
Expand All @@ -102,17 +112,17 @@ public static function add(&$holder, $pathItems, $value, $recursively = true)
$intKey = filter_var($key, FILTER_VALIDATE_INT);
if ($ref === null && (false === $intKey || $intKey !== 0)) {
$key = (string)$key;
if ($recursively) {
if ($flags & self::RECURSIVE_KEY_CREATION) {
$ref = new \stdClass();
$ref = &$ref->{$key};
} else {
throw new Exception('Non-existent path item: ' . $key);
}
} elseif ([] === $ref && false === $intKey && '-' !== $key) {
} elseif ([] === $ref && 0 === ($flags & self::STRICT_MODE) && false === $intKey && '-' !== $key) {
$ref = new \stdClass();
$ref = &$ref->{$key};
} else {
if ($recursively && $ref === null) $ref = array();
if ($flags & self::RECURSIVE_KEY_CREATION && $ref === null) $ref = array();
if ('-' === $key) {
$ref = &$ref[];
} else {
Expand All @@ -122,7 +132,7 @@ public static function add(&$holder, $pathItems, $value, $recursively = true)
if (false === $intKey) {
throw new Exception('Invalid key for array operation');
}
if ($intKey > count($ref) && !$recursively) {
if ($intKey > count($ref) && 0 === ($flags & self::RECURSIVE_KEY_CREATION)) {
throw new Exception('Index is greater than number of items in array');
} elseif ($intKey < 0) {
throw new Exception('Negative index');
Expand Down
6 changes: 6 additions & 0 deletions tests/src/Issues/Issue9Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace Swaggest\JsonDiff\Tests\Issues;

use Swaggest\JsonDiff\Exception;
use Swaggest\JsonDiff\JsonDiff;
use Swaggest\JsonDiff\JsonPatch;

/**
* @see https://github.com/swaggest/json-diff/issues/9
Expand All @@ -18,5 +20,9 @@ public function testPatchApply()
$this->assertNotEquals($new, $old);
$patch->apply($old);
$this->assertEquals($new, $old);

$old = json_decode(json_encode(["emptyObject" => []]));
$this->setExpectedException(get_class(new Exception()), 'Invalid key for array operation');
$patch->setFlags(JsonPatch::STRICT_MODE)->apply($old);
}
}

0 comments on commit 8230d84

Please sign in to comment.