Skip to content

Commit

Permalink
Merge pull request #4 from swaggest/v2-patch
Browse files Browse the repository at this point in the history
passing additional spec tests
  • Loading branch information
vearutop authored Jan 29, 2018
2 parents 16f0445 + cf093c8 commit 2058926
Show file tree
Hide file tree
Showing 15 changed files with 2,170 additions and 45 deletions.
18 changes: 11 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ cache:

# execute any number of scripts before the test run, custom env's are available as variables
before_script:
- ls -la $HOME/.composer/cache
- test -f $HOME/.composer/cache/composer.lock.$(phpenv version-name) && cp $HOME/.composer/cache/composer.lock.$(phpenv version-name) ./composer.lock || echo "No composer.lock cached"
- composer install --dev --no-interaction --prefer-dist
# - cat composer.lock
- test -f $HOME/.composer/cache/composer.lock.$(phpenv version-name) || cp ./composer.lock $HOME/.composer/cache/composer.lock.$(phpenv version-name)
- if [[ $(phpenv version-name) =~ 7.2 ]] ; then test -f $HOME/.composer/cache/phpstan.phar || wget https://github.com/phpstan/phpstan/releases/download/0.9.1/phpstan.phar -O $HOME/.composer/cache/phpstan.phar; fi
- if [[ $(phpenv version-name) =~ 7.2 ]] ; then test -f $HOME/.composer/cache/ocular.phar || wget https://scrutinizer-ci.com/ocular.phar -O $HOME/.composer/cache/ocular.phar; fi
- if [[ $(phpenv version-name) =~ 7.2 ]] ; then test -f $HOME/.composer/cache/cctr || wget https://codeclimate.com/downloads/test-reporter/test-reporter-0.1.4-linux-amd64 -O $HOME/.composer/cache/cctr && chmod +x $HOME/.composer/cache/cctr; fi
- if [[ $(phpenv version-name) =~ 7.2 ]] ; then $HOME/.composer/cache/cctr before-build; fi

matrix:
allow_failures:
Expand All @@ -29,11 +35,9 @@ matrix:
fast_finish: true

script:
- mkdir -p build/logs
- ./vendor/bin/phpunit -v --configuration phpunit.xml --coverage-clover build/logs/clover.xml
- ./vendor/bin/phpunit -v --configuration phpunit.xml --coverage-text --coverage-clover clover.xml
- if [[ $(phpenv version-name) =~ 7.2 ]] ; then php $HOME/.composer/cache/phpstan.phar analyze -l 7 ./src; fi

after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover build/logs/coverage.clover
- if [[ $(phpenv version-name) =~ 7.1 ]] ; then php vendor/bin/coveralls -v; fi
- if [[ $(phpenv version-name) =~ 7.1 ]] ; then php vendor/bin/test-reporter; fi
- if [[ $(phpenv version-name) =~ 7.2 ]] ; then php $HOME/.composer/cache/ocular.phar code-coverage:upload --format=php-clover clover.xml; fi
- if [[ $(phpenv version-name) =~ 7.2 ]] ; then $HOME/.composer/cache/cctr after-build --exit-code $TRAVIS_TEST_RESULT; fi
2 changes: 1 addition & 1 deletion src/Cli/Apply.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ public function performAction()
$patch = JsonPatch::import(json_decode($patchJson));
$base = json_decode($baseJson);
$patch->apply($base);
$this->out = $base;
} catch (Exception $e) {
$this->response->error($e->getMessage());
}
$this->out = $base;

$this->postPerform();
}
Expand Down
8 changes: 7 additions & 1 deletion src/Cli/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Swaggest\JsonDiff\Cli;


use Swaggest\JsonDiff\Exception;
use Swaggest\JsonDiff\JsonDiff;
use Yaoi\Command;

Expand Down Expand Up @@ -48,7 +49,12 @@ protected function prePerform()
if ($this->rearrangeArrays) {
$options += JsonDiff::REARRANGE_ARRAYS;
}
$this->diff = new JsonDiff(json_decode($originalJson), json_decode($newJson), $options);
try {
$this->diff = new JsonDiff(json_decode($originalJson), json_decode($newJson), $options);
} catch (Exception $e) {
$this->response->error($e->getMessage());
return;
}

$this->out = '';
}
Expand Down
2 changes: 1 addition & 1 deletion src/Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@

class Exception extends \Exception
{

const EMPTY_PROPERTY_NAME_UNSUPPORTED = 1;
}
41 changes: 29 additions & 12 deletions src/JsonDiff.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ class JsonDiff

/**
* Processor constructor.
* @param $original
* @param $new
* @param mixed $original
* @param mixed $new
* @param int $options
* @throws Exception
*/
public function __construct($original, $new, $options = 0)
{
Expand Down Expand Up @@ -181,11 +182,21 @@ public function getPatch()
return $this->jsonPatch;
}

/**
* @return array|null|object|\stdClass
* @throws Exception
*/
private function rearrange()
{
return $this->process($this->original, $this->new);
}

/**
* @param mixed $original
* @param mixed $new
* @return array|null|object|\stdClass
* @throws Exception
*/
private function process($original, $new)
{
if (
Expand All @@ -194,6 +205,9 @@ private function process($original, $new)
) {
if ($original !== $new) {
$this->modifiedCnt++;
if ($this->options & self::STOP_ON_DIFF) {
return null;
}
$this->modifiedPaths [] = $this->path;

$this->jsonPatch->op(new Test($this->path, $original));
Expand All @@ -202,9 +216,6 @@ private function process($original, $new)
JsonPointer::add($this->modifiedOriginal, $this->pathItems, $original);
JsonPointer::add($this->modifiedNew, $this->pathItems, $new);

if ($this->options & self::STOP_ON_DIFF) {
return null;
}
}
return $new;
}
Expand All @@ -222,6 +233,12 @@ private function process($original, $new)
$originalKeys = $original instanceof \stdClass ? get_object_vars($original) : $original;

foreach ($originalKeys as $key => $originalValue) {
if ($this->options & self::STOP_ON_DIFF) {
if ($this->modifiedCnt || $this->addedCnt || $this->removedCnt) {
return null;
}
}

$path = $this->path;
$pathItems = $this->pathItems;
$this->path .= '/' . JsonPointer::escapeSegment($key, $this->options & self::JSON_URI_FRAGMENT_ID);
Expand All @@ -232,34 +249,34 @@ private function process($original, $new)
unset($newArray[$key]);
} else {
$this->removedCnt++;
if ($this->options & self::STOP_ON_DIFF) {
return null;
}
$this->removedPaths [] = $this->path;

$this->jsonPatch->op(new Remove($this->path));

JsonPointer::add($this->removed, $this->pathItems, $originalValue);
if ($this->options & self::STOP_ON_DIFF) {
return null;
}
}
$this->path = $path;
$this->pathItems = $pathItems;
}

// additions
foreach ($newArray as $key => $value) {
$this->addedCnt++;
if ($this->options & self::STOP_ON_DIFF) {
return null;
}
$newOrdered[$key] = $value;
$path = $this->path . '/' . JsonPointer::escapeSegment($key, $this->options & self::JSON_URI_FRAGMENT_ID);
$pathItems = $this->pathItems;
$pathItems[] = $key;
JsonPointer::add($this->added, $pathItems, $value);
$this->addedCnt++;
$this->addedPaths [] = $path;

$this->jsonPatch->op(new Add($path, $value));

if ($this->options & self::STOP_ON_DIFF) {
return null;
}
}

return is_array($new) ? $newOrdered : (object)$newOrdered;
Expand Down
14 changes: 8 additions & 6 deletions src/JsonPatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public function jsonSerialize()
}

/**
* @param $original
* @param mixed $original
* @throws Exception
*/
public function apply(&$original)
Expand All @@ -117,32 +117,34 @@ public function apply(&$original)
$pathItems = JsonPointer::splitPath($operation->path);
switch (true) {
case $operation instanceof Add:
JsonPointer::add($original, $pathItems, $operation->value);
JsonPointer::add($original, $pathItems, $operation->value, false);
break;
case $operation instanceof Copy:
$fromItems = JsonPointer::splitPath($operation->from);
$value = JsonPointer::get($original, $fromItems);
JsonPointer::add($original, $pathItems, $value);
JsonPointer::add($original, $pathItems, $value, false);
break;
case $operation instanceof Move:
$fromItems = JsonPointer::splitPath($operation->from);
$value = JsonPointer::get($original, $fromItems);
JsonPointer::add($original, $pathItems, $value);
JsonPointer::remove($original, $fromItems);
JsonPointer::add($original, $pathItems, $value, false);
break;
case $operation instanceof Remove:
JsonPointer::remove($original, $pathItems);
break;
case $operation instanceof Replace:
JsonPointer::get($original, $pathItems);
JsonPointer::add($original, $pathItems, $operation->value);
JsonPointer::remove($original, $pathItems);
JsonPointer::add($original, $pathItems, $operation->value, false);
break;
case $operation instanceof Test:
$value = JsonPointer::get($original, $pathItems);
$diff = new JsonDiff($operation->value, $value,
JsonDiff::STOP_ON_DIFF);
if ($diff->getDiffCnt() !== 0) {
throw new Exception('Test operation ' . json_encode($operation) . ' failed: ' . json_encode($value));
throw new Exception('Test operation ' . json_encode($operation, JSON_UNESCAPED_SLASHES)
. ' failed: ' . json_encode($value));
}
break;
}
Expand Down
58 changes: 46 additions & 12 deletions src/JsonPointer.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ public static function splitPath($path)
$result = array();
if ($first === '#') {
foreach ($pathItems as $key) {
$key = str_replace(array('~0', '~1'), array('~', '/'), urldecode($key));
$key = str_replace(array('~1', '~0'), array('/', '~'), urldecode($key));
$result[] = $key;
}
} else {
if ($first !== '') {
throw new Exception('Path must start with "/": ' . $path);
}
foreach ($pathItems as $key) {
$key = str_replace(array('~0', '~1'), array('~', '/'), $key);
$key = str_replace(array('~1', '~0'), array('/', '~'), $key);
$result[] = $key;
}
}
Expand All @@ -50,22 +50,50 @@ public static function splitPath($path)
* @param mixed $holder
* @param string[] $pathItems
* @param mixed $value
* @param bool $recursively
* @throws Exception
*/
public static function add(&$holder, $pathItems, $value)
public static function add(&$holder, $pathItems, $value, $recursively = true)
{
$ref = &$holder;
while (null !== $key = array_shift($pathItems)) {
if ($ref instanceof \stdClass) {
if (PHP_VERSION_ID < 71000 && '' === $key) {
throw new Exception('Empty property name is not supported by PHP <7.1',
Exception::EMPTY_PROPERTY_NAME_UNSUPPORTED);
}

$ref = &$ref->$key;
} elseif ($ref === null
&& !is_int($key)
&& false === filter_var($key, FILTER_VALIDATE_INT)
) {
$key = (string)$key;
$ref = new \stdClass();
$ref = &$ref->{$key};
} else {
$ref = &$ref[$key];
} else { // null or array
$intKey = filter_var($key, FILTER_VALIDATE_INT);
if ($ref === null && (false === $intKey || $intKey !== 0)) {
$key = (string)$key;
if ($recursively) {
$ref = new \stdClass();
$ref = &$ref->{$key};
} else {
throw new Exception('Non-existent path');
}
} else {
if ($recursively && $ref === null) $ref = array();
if ('-' === $key) {
$ref = &$ref[];
} else {
if (is_array($ref) && array_key_exists($key, $ref) && empty($pathItems)) {
array_splice($ref, $key, 0, array($value));
}
if (false === $intKey) {
throw new Exception('Invalid key for array operation');
}
if ($intKey > count($ref) && !$recursively) {
throw new Exception('Index is greater than number of items in array');
} elseif ($intKey < 0) {
throw new Exception('Negative index');
}

$ref = &$ref[$intKey];
}
}
}
}
$ref = $value;
Expand Down Expand Up @@ -108,6 +136,11 @@ public static function get($holder, $pathItems)
$ref = $holder;
while (null !== $key = array_shift($pathItems)) {
if ($ref instanceof \stdClass) {
if (PHP_VERSION_ID < 71000 && '' === $key) {
throw new Exception('Empty property name is not supported by PHP <7.1',
Exception::EMPTY_PROPERTY_NAME_UNSUPPORTED);
}

$vars = (array)$ref;
if (self::arrayKeyExists($key, $vars)) {
$ref = self::arrayGet($key, $vars);
Expand Down Expand Up @@ -159,6 +192,7 @@ public static function remove(&$holder, $pathItems)
unset($parent->$refKey);
} else {
unset($parent[$refKey]);
$parent = array_values($parent);
}
}
return $ref;
Expand Down
Loading

0 comments on commit 2058926

Please sign in to comment.