Skip to content

Commit cf093c8

Browse files
committed
passing additional spec tests
1 parent 16f0445 commit cf093c8

15 files changed

+2170
-45
lines changed

Diff for: .travis.yml

+11-7
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,14 @@ cache:
1919

2020
# execute any number of scripts before the test run, custom env's are available as variables
2121
before_script:
22+
- ls -la $HOME/.composer/cache
23+
- 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"
2224
- composer install --dev --no-interaction --prefer-dist
23-
# - cat composer.lock
25+
- test -f $HOME/.composer/cache/composer.lock.$(phpenv version-name) || cp ./composer.lock $HOME/.composer/cache/composer.lock.$(phpenv version-name)
26+
- 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
27+
- 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
28+
- 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
29+
- if [[ $(phpenv version-name) =~ 7.2 ]] ; then $HOME/.composer/cache/cctr before-build; fi
2430

2531
matrix:
2632
allow_failures:
@@ -29,11 +35,9 @@ matrix:
2935
fast_finish: true
3036

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

3541
after_script:
36-
- wget https://scrutinizer-ci.com/ocular.phar
37-
- php ocular.phar code-coverage:upload --format=php-clover build/logs/coverage.clover
38-
- if [[ $(phpenv version-name) =~ 7.1 ]] ; then php vendor/bin/coveralls -v; fi
39-
- if [[ $(phpenv version-name) =~ 7.1 ]] ; then php vendor/bin/test-reporter; fi
42+
- if [[ $(phpenv version-name) =~ 7.2 ]] ; then php $HOME/.composer/cache/ocular.phar code-coverage:upload --format=php-clover clover.xml; fi
43+
- if [[ $(phpenv version-name) =~ 7.2 ]] ; then $HOME/.composer/cache/cctr after-build --exit-code $TRAVIS_TEST_RESULT; fi

Diff for: src/Cli/Apply.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ public function performAction()
4949
$patch = JsonPatch::import(json_decode($patchJson));
5050
$base = json_decode($baseJson);
5151
$patch->apply($base);
52+
$this->out = $base;
5253
} catch (Exception $e) {
5354
$this->response->error($e->getMessage());
5455
}
55-
$this->out = $base;
5656

5757
$this->postPerform();
5858
}

Diff for: src/Cli/Base.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Swaggest\JsonDiff\Cli;
44

55

6+
use Swaggest\JsonDiff\Exception;
67
use Swaggest\JsonDiff\JsonDiff;
78
use Yaoi\Command;
89

@@ -48,7 +49,12 @@ protected function prePerform()
4849
if ($this->rearrangeArrays) {
4950
$options += JsonDiff::REARRANGE_ARRAYS;
5051
}
51-
$this->diff = new JsonDiff(json_decode($originalJson), json_decode($newJson), $options);
52+
try {
53+
$this->diff = new JsonDiff(json_decode($originalJson), json_decode($newJson), $options);
54+
} catch (Exception $e) {
55+
$this->response->error($e->getMessage());
56+
return;
57+
}
5258

5359
$this->out = '';
5460
}

Diff for: src/Exception.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55

66
class Exception extends \Exception
77
{
8-
8+
const EMPTY_PROPERTY_NAME_UNSUPPORTED = 1;
99
}

Diff for: src/JsonDiff.php

+29-12
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ class JsonDiff
4545

4646
/**
4747
* Processor constructor.
48-
* @param $original
49-
* @param $new
48+
* @param mixed $original
49+
* @param mixed $new
5050
* @param int $options
51+
* @throws Exception
5152
*/
5253
public function __construct($original, $new, $options = 0)
5354
{
@@ -181,11 +182,21 @@ public function getPatch()
181182
return $this->jsonPatch;
182183
}
183184

185+
/**
186+
* @return array|null|object|\stdClass
187+
* @throws Exception
188+
*/
184189
private function rearrange()
185190
{
186191
return $this->process($this->original, $this->new);
187192
}
188193

194+
/**
195+
* @param mixed $original
196+
* @param mixed $new
197+
* @return array|null|object|\stdClass
198+
* @throws Exception
199+
*/
189200
private function process($original, $new)
190201
{
191202
if (
@@ -194,6 +205,9 @@ private function process($original, $new)
194205
) {
195206
if ($original !== $new) {
196207
$this->modifiedCnt++;
208+
if ($this->options & self::STOP_ON_DIFF) {
209+
return null;
210+
}
197211
$this->modifiedPaths [] = $this->path;
198212

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

205-
if ($this->options & self::STOP_ON_DIFF) {
206-
return null;
207-
}
208219
}
209220
return $new;
210221
}
@@ -222,6 +233,12 @@ private function process($original, $new)
222233
$originalKeys = $original instanceof \stdClass ? get_object_vars($original) : $original;
223234

224235
foreach ($originalKeys as $key => $originalValue) {
236+
if ($this->options & self::STOP_ON_DIFF) {
237+
if ($this->modifiedCnt || $this->addedCnt || $this->removedCnt) {
238+
return null;
239+
}
240+
}
241+
225242
$path = $this->path;
226243
$pathItems = $this->pathItems;
227244
$this->path .= '/' . JsonPointer::escapeSegment($key, $this->options & self::JSON_URI_FRAGMENT_ID);
@@ -232,34 +249,34 @@ private function process($original, $new)
232249
unset($newArray[$key]);
233250
} else {
234251
$this->removedCnt++;
252+
if ($this->options & self::STOP_ON_DIFF) {
253+
return null;
254+
}
235255
$this->removedPaths [] = $this->path;
236256

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

239259
JsonPointer::add($this->removed, $this->pathItems, $originalValue);
240-
if ($this->options & self::STOP_ON_DIFF) {
241-
return null;
242-
}
243260
}
244261
$this->path = $path;
245262
$this->pathItems = $pathItems;
246263
}
247264

248265
// additions
249266
foreach ($newArray as $key => $value) {
267+
$this->addedCnt++;
268+
if ($this->options & self::STOP_ON_DIFF) {
269+
return null;
270+
}
250271
$newOrdered[$key] = $value;
251272
$path = $this->path . '/' . JsonPointer::escapeSegment($key, $this->options & self::JSON_URI_FRAGMENT_ID);
252273
$pathItems = $this->pathItems;
253274
$pathItems[] = $key;
254275
JsonPointer::add($this->added, $pathItems, $value);
255-
$this->addedCnt++;
256276
$this->addedPaths [] = $path;
257277

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

260-
if ($this->options & self::STOP_ON_DIFF) {
261-
return null;
262-
}
263280
}
264281

265282
return is_array($new) ? $newOrdered : (object)$newOrdered;

Diff for: src/JsonPatch.php

+8-6
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public function jsonSerialize()
108108
}
109109

110110
/**
111-
* @param $original
111+
* @param mixed $original
112112
* @throws Exception
113113
*/
114114
public function apply(&$original)
@@ -117,32 +117,34 @@ public function apply(&$original)
117117
$pathItems = JsonPointer::splitPath($operation->path);
118118
switch (true) {
119119
case $operation instanceof Add:
120-
JsonPointer::add($original, $pathItems, $operation->value);
120+
JsonPointer::add($original, $pathItems, $operation->value, false);
121121
break;
122122
case $operation instanceof Copy:
123123
$fromItems = JsonPointer::splitPath($operation->from);
124124
$value = JsonPointer::get($original, $fromItems);
125-
JsonPointer::add($original, $pathItems, $value);
125+
JsonPointer::add($original, $pathItems, $value, false);
126126
break;
127127
case $operation instanceof Move:
128128
$fromItems = JsonPointer::splitPath($operation->from);
129129
$value = JsonPointer::get($original, $fromItems);
130-
JsonPointer::add($original, $pathItems, $value);
131130
JsonPointer::remove($original, $fromItems);
131+
JsonPointer::add($original, $pathItems, $value, false);
132132
break;
133133
case $operation instanceof Remove:
134134
JsonPointer::remove($original, $pathItems);
135135
break;
136136
case $operation instanceof Replace:
137137
JsonPointer::get($original, $pathItems);
138-
JsonPointer::add($original, $pathItems, $operation->value);
138+
JsonPointer::remove($original, $pathItems);
139+
JsonPointer::add($original, $pathItems, $operation->value, false);
139140
break;
140141
case $operation instanceof Test:
141142
$value = JsonPointer::get($original, $pathItems);
142143
$diff = new JsonDiff($operation->value, $value,
143144
JsonDiff::STOP_ON_DIFF);
144145
if ($diff->getDiffCnt() !== 0) {
145-
throw new Exception('Test operation ' . json_encode($operation) . ' failed: ' . json_encode($value));
146+
throw new Exception('Test operation ' . json_encode($operation, JSON_UNESCAPED_SLASHES)
147+
. ' failed: ' . json_encode($value));
146148
}
147149
break;
148150
}

Diff for: src/JsonPointer.php

+46-12
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ public static function splitPath($path)
3131
$result = array();
3232
if ($first === '#') {
3333
foreach ($pathItems as $key) {
34-
$key = str_replace(array('~0', '~1'), array('~', '/'), urldecode($key));
34+
$key = str_replace(array('~1', '~0'), array('/', '~'), urldecode($key));
3535
$result[] = $key;
3636
}
3737
} else {
3838
if ($first !== '') {
3939
throw new Exception('Path must start with "/": ' . $path);
4040
}
4141
foreach ($pathItems as $key) {
42-
$key = str_replace(array('~0', '~1'), array('~', '/'), $key);
42+
$key = str_replace(array('~1', '~0'), array('/', '~'), $key);
4343
$result[] = $key;
4444
}
4545
}
@@ -50,22 +50,50 @@ public static function splitPath($path)
5050
* @param mixed $holder
5151
* @param string[] $pathItems
5252
* @param mixed $value
53+
* @param bool $recursively
54+
* @throws Exception
5355
*/
54-
public static function add(&$holder, $pathItems, $value)
56+
public static function add(&$holder, $pathItems, $value, $recursively = true)
5557
{
5658
$ref = &$holder;
5759
while (null !== $key = array_shift($pathItems)) {
5860
if ($ref instanceof \stdClass) {
61+
if (PHP_VERSION_ID < 71000 && '' === $key) {
62+
throw new Exception('Empty property name is not supported by PHP <7.1',
63+
Exception::EMPTY_PROPERTY_NAME_UNSUPPORTED);
64+
}
65+
5966
$ref = &$ref->$key;
60-
} elseif ($ref === null
61-
&& !is_int($key)
62-
&& false === filter_var($key, FILTER_VALIDATE_INT)
63-
) {
64-
$key = (string)$key;
65-
$ref = new \stdClass();
66-
$ref = &$ref->{$key};
67-
} else {
68-
$ref = &$ref[$key];
67+
} else { // null or array
68+
$intKey = filter_var($key, FILTER_VALIDATE_INT);
69+
if ($ref === null && (false === $intKey || $intKey !== 0)) {
70+
$key = (string)$key;
71+
if ($recursively) {
72+
$ref = new \stdClass();
73+
$ref = &$ref->{$key};
74+
} else {
75+
throw new Exception('Non-existent path');
76+
}
77+
} else {
78+
if ($recursively && $ref === null) $ref = array();
79+
if ('-' === $key) {
80+
$ref = &$ref[];
81+
} else {
82+
if (is_array($ref) && array_key_exists($key, $ref) && empty($pathItems)) {
83+
array_splice($ref, $key, 0, array($value));
84+
}
85+
if (false === $intKey) {
86+
throw new Exception('Invalid key for array operation');
87+
}
88+
if ($intKey > count($ref) && !$recursively) {
89+
throw new Exception('Index is greater than number of items in array');
90+
} elseif ($intKey < 0) {
91+
throw new Exception('Negative index');
92+
}
93+
94+
$ref = &$ref[$intKey];
95+
}
96+
}
6997
}
7098
}
7199
$ref = $value;
@@ -108,6 +136,11 @@ public static function get($holder, $pathItems)
108136
$ref = $holder;
109137
while (null !== $key = array_shift($pathItems)) {
110138
if ($ref instanceof \stdClass) {
139+
if (PHP_VERSION_ID < 71000 && '' === $key) {
140+
throw new Exception('Empty property name is not supported by PHP <7.1',
141+
Exception::EMPTY_PROPERTY_NAME_UNSUPPORTED);
142+
}
143+
111144
$vars = (array)$ref;
112145
if (self::arrayKeyExists($key, $vars)) {
113146
$ref = self::arrayGet($key, $vars);
@@ -159,6 +192,7 @@ public static function remove(&$holder, $pathItems)
159192
unset($parent->$refKey);
160193
} else {
161194
unset($parent[$refKey]);
195+
$parent = array_values($parent);
162196
}
163197
}
164198
return $ref;

0 commit comments

Comments
 (0)