parent
39b3a19cf7
commit
6c971c3028
|
@ -31,7 +31,7 @@ class JsonManipulator
|
||||||
if (!self::$RECURSE_BLOCKS) {
|
if (!self::$RECURSE_BLOCKS) {
|
||||||
self::$RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*';
|
self::$RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*';
|
||||||
self::$RECURSE_ARRAYS = '(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[[^\]]*\])*\])*\])*\]|'.self::$RECURSE_BLOCKS.')*';
|
self::$RECURSE_ARRAYS = '(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[(?:[^\]]*|\[[^\]]*\])*\])*\])*\]|'.self::$RECURSE_BLOCKS.')*';
|
||||||
self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"';
|
self::$JSON_STRING = '"(?:[^\0-\x09\x0a-\x1f\\\\"]+|\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4})*"';
|
||||||
self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\['.self::$RECURSE_ARRAYS.'\]|\{'.self::$RECURSE_BLOCKS.'\})';
|
self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\['.self::$RECURSE_ARRAYS.'\]|\{'.self::$RECURSE_BLOCKS.'\})';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,12 +139,20 @@ class JsonManipulator
|
||||||
}
|
}
|
||||||
|
|
||||||
// main node content not match-able
|
// main node content not match-able
|
||||||
$nodeRegex = '#("'.$mainNode.'":\s*\{)('.self::$RECURSE_BLOCKS.')(\})#s';
|
$nodeRegex = '{^(\s*\{\s*(?:'.self::$JSON_STRING.'\s*:\s*'.self::$JSON_VALUE.'\s*,\s*)*?)'.
|
||||||
|
'('.preg_quote(JsonFile::encode($mainNode)).'\s*:\s*\{)('.self::$RECURSE_BLOCKS.')(\})(.*)}s';
|
||||||
|
try {
|
||||||
if (!$this->pregMatch($nodeRegex, $this->contents, $match)) {
|
if (!$this->pregMatch($nodeRegex, $this->contents, $match)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
if ($e->getCode() === PREG_BACKTRACK_LIMIT_ERROR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
$children = $match[2];
|
$children = $match[3];
|
||||||
|
|
||||||
// invalid match due to un-regexable content, abort
|
// invalid match due to un-regexable content, abort
|
||||||
if (!@json_decode('{'.$children.'}')) {
|
if (!@json_decode('{'.$children.'}')) {
|
||||||
|
@ -184,7 +192,7 @@ class JsonManipulator
|
||||||
$children = $this->newline . $this->indent . $this->indent . JsonFile::encode($name).': '.$this->format($value, 1) . $children;
|
$children = $this->newline . $this->indent . $this->indent . JsonFile::encode($name).': '.$this->format($value, 1) . $children;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->contents = preg_replace($nodeRegex, addcslashes('${1}'.$children.'$3', '\\'), $this->contents);
|
$this->contents = preg_replace($nodeRegex, addcslashes('${1}${2}'.$children.'${4}${5}', '\\'), $this->contents);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -199,15 +207,23 @@ class JsonManipulator
|
||||||
}
|
}
|
||||||
|
|
||||||
// no node content match-able
|
// no node content match-able
|
||||||
$nodeRegex = '#("'.$mainNode.'":\s*\{)('.self::$RECURSE_BLOCKS.')(\})#s';
|
$nodeRegex = '{^(\s*\{\s*(?:'.self::$JSON_STRING.'\s*:\s*'.self::$JSON_VALUE.'\s*,\s*)*?)'.
|
||||||
|
'('.preg_quote(JsonFile::encode($mainNode)).'\s*:\s*\{)('.self::$RECURSE_BLOCKS.')(\})(.*)}s';
|
||||||
|
try {
|
||||||
if (!$this->pregMatch($nodeRegex, $this->contents, $match)) {
|
if (!$this->pregMatch($nodeRegex, $this->contents, $match)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
if ($e->getCode() === PREG_BACKTRACK_LIMIT_ERROR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
$children = $match[2];
|
$children = $match[3];
|
||||||
|
|
||||||
// invalid match due to un-regexable content, abort
|
// invalid match due to un-regexable content, abort
|
||||||
if (!@json_decode('{'.$children.'}')) {
|
if (!@json_decode('{'.$children.'}', true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +261,7 @@ class JsonManipulator
|
||||||
|
|
||||||
// no child data left, $name was the only key in
|
// no child data left, $name was the only key in
|
||||||
if (!trim($childrenClean)) {
|
if (!trim($childrenClean)) {
|
||||||
$this->contents = preg_replace($nodeRegex, '$1'.$this->newline.$this->indent.'}', $this->contents);
|
$this->contents = preg_replace($nodeRegex, '$1$2'.$this->newline.$this->indent.'$4$5', $this->contents);
|
||||||
|
|
||||||
// we have a subname, so we restore the rest of $name
|
// we have a subname, so we restore the rest of $name
|
||||||
if ($subName !== null) {
|
if ($subName !== null) {
|
||||||
|
@ -260,12 +276,12 @@ class JsonManipulator
|
||||||
$that = $this;
|
$that = $this;
|
||||||
$this->contents = preg_replace_callback($nodeRegex, function ($matches) use ($that, $name, $subName, $childrenClean) {
|
$this->contents = preg_replace_callback($nodeRegex, function ($matches) use ($that, $name, $subName, $childrenClean) {
|
||||||
if ($subName !== null) {
|
if ($subName !== null) {
|
||||||
$curVal = json_decode('{'.$matches[2].'}', true);
|
$curVal = json_decode('{'.$matches[3].'}', true);
|
||||||
unset($curVal[$name][$subName]);
|
unset($curVal[$name][$subName]);
|
||||||
$childrenClean = substr($that->format($curVal, 0), 1, -1);
|
$childrenClean = substr($that->format($curVal, 0), 1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $matches[1] . $childrenClean . $matches[3];
|
return $matches[1] . $matches[2] . $childrenClean . $matches[4] . $matches[5];
|
||||||
}, $this->contents);
|
}, $this->contents);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -352,17 +368,17 @@ class JsonManipulator
|
||||||
if ($count === false) {
|
if ($count === false) {
|
||||||
switch (preg_last_error()) {
|
switch (preg_last_error()) {
|
||||||
case PREG_NO_ERROR:
|
case PREG_NO_ERROR:
|
||||||
throw new \RuntimeException('Failed to execute regex: PREG_NO_ERROR');
|
throw new \RuntimeException('Failed to execute regex: PREG_NO_ERROR', PREG_NO_ERROR);
|
||||||
case PREG_INTERNAL_ERROR:
|
case PREG_INTERNAL_ERROR:
|
||||||
throw new \RuntimeException('Failed to execute regex: PREG_INTERNAL_ERROR');
|
throw new \RuntimeException('Failed to execute regex: PREG_INTERNAL_ERROR', PREG_INTERNAL_ERROR);
|
||||||
case PREG_BACKTRACK_LIMIT_ERROR:
|
case PREG_BACKTRACK_LIMIT_ERROR:
|
||||||
throw new \RuntimeException('Failed to execute regex: PREG_BACKTRACK_LIMIT_ERROR');
|
throw new \RuntimeException('Failed to execute regex: PREG_BACKTRACK_LIMIT_ERROR', PREG_BACKTRACK_LIMIT_ERROR);
|
||||||
case PREG_RECURSION_LIMIT_ERROR:
|
case PREG_RECURSION_LIMIT_ERROR:
|
||||||
throw new \RuntimeException('Failed to execute regex: PREG_RECURSION_LIMIT_ERROR');
|
throw new \RuntimeException('Failed to execute regex: PREG_RECURSION_LIMIT_ERROR', PREG_RECURSION_LIMIT_ERROR);
|
||||||
case PREG_BAD_UTF8_ERROR:
|
case PREG_BAD_UTF8_ERROR:
|
||||||
throw new \RuntimeException('Failed to execute regex: PREG_BAD_UTF8_ERROR');
|
throw new \RuntimeException('Failed to execute regex: PREG_BAD_UTF8_ERROR', PREG_BAD_UTF8_ERROR);
|
||||||
case PREG_BAD_UTF8_OFFSET_ERROR:
|
case PREG_BAD_UTF8_OFFSET_ERROR:
|
||||||
throw new \RuntimeException('Failed to execute regex: PREG_BAD_UTF8_OFFSET_ERROR');
|
throw new \RuntimeException('Failed to execute regex: PREG_BAD_UTF8_OFFSET_ERROR', PREG_BAD_UTF8_OFFSET_ERROR);
|
||||||
default:
|
default:
|
||||||
throw new \RuntimeException('Failed to execute regex: Unknown error');
|
throw new \RuntimeException('Failed to execute regex: Unknown error');
|
||||||
}
|
}
|
||||||
|
|
|
@ -576,18 +576,22 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase
|
||||||
),
|
),
|
||||||
'fails on deep arrays with borked texts' => array(
|
'fails on deep arrays with borked texts' => array(
|
||||||
'{
|
'{
|
||||||
"repositories": [{
|
"repositories": [
|
||||||
|
{
|
||||||
"package": { "bar": "ba[z" }
|
"package": { "bar": "ba[z" }
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
}',
|
}',
|
||||||
'bar',
|
'bar',
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
'fails on deep arrays with borked texts2' => array(
|
'fails on deep arrays with borked texts2' => array(
|
||||||
'{
|
'{
|
||||||
"repositories": [{
|
"repositories": [
|
||||||
|
{
|
||||||
"package": { "bar": "ba]z" }
|
"package": { "bar": "ba]z" }
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
}',
|
}',
|
||||||
'bar',
|
'bar',
|
||||||
false
|
false
|
||||||
|
@ -603,6 +607,9 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase
|
||||||
"package": {
|
"package": {
|
||||||
"require": {
|
"require": {
|
||||||
"this/should-not-end-up-in-root-require": "~2.0"
|
"this/should-not-end-up-in-root-require": "~2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"this/should-not-end-up-in-root-require-dev": "~2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,16 +618,23 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase
|
||||||
"package/a": "*",
|
"package/a": "*",
|
||||||
"package/b": "*",
|
"package/b": "*",
|
||||||
"package/c": "*"
|
"package/c": "*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"package/d": "*"
|
||||||
}
|
}
|
||||||
}');
|
}');
|
||||||
|
|
||||||
$this->assertTrue($manipulator->removeSubNode('require', 'package/c'));
|
$this->assertTrue($manipulator->removeSubNode('require', 'package/c'));
|
||||||
|
$this->assertTrue($manipulator->removeSubNode('require-dev', 'package/d'));
|
||||||
$this->assertEquals('{
|
$this->assertEquals('{
|
||||||
"repositories": [
|
"repositories": [
|
||||||
{
|
{
|
||||||
"package": {
|
"package": {
|
||||||
"require": {
|
"require": {
|
||||||
"this/should-not-end-up-in-root-require": "~2.0"
|
"this/should-not-end-up-in-root-require": "~2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"this/should-not-end-up-in-root-require-dev": "~2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -628,6 +642,60 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase
|
||||||
"require": {
|
"require": {
|
||||||
"package/a": "*",
|
"package/a": "*",
|
||||||
"package/b": "*"
|
"package/b": "*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
', $manipulator->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddSubNodeInRequire()
|
||||||
|
{
|
||||||
|
$manipulator = new JsonManipulator('{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"package": {
|
||||||
|
"require": {
|
||||||
|
"this/should-not-end-up-in-root-require": "~2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"this/should-not-end-up-in-root-require-dev": "~2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"package/a": "*",
|
||||||
|
"package/b": "*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"package/d": "*"
|
||||||
|
}
|
||||||
|
}');
|
||||||
|
|
||||||
|
$this->assertTrue($manipulator->addSubNode('require', 'package/c', '*'));
|
||||||
|
$this->assertTrue($manipulator->addSubNode('require-dev', 'package/e', '*'));
|
||||||
|
$this->assertEquals('{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"package": {
|
||||||
|
"require": {
|
||||||
|
"this/should-not-end-up-in-root-require": "~2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"this/should-not-end-up-in-root-require-dev": "~2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"package/a": "*",
|
||||||
|
"package/b": "*",
|
||||||
|
"package/c": "*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"package/d": "*",
|
||||||
|
"package/e": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
', $manipulator->getContents());
|
', $manipulator->getContents());
|
||||||
|
|
Loading…
Reference in New Issue