Allow JsonManipulator::addMainKey to update top level keys as well
parent
7f7d13450e
commit
3bd6af690d
src/Composer/Json
tests/Composer/Test/Json
|
@ -17,7 +17,9 @@ namespace Composer\Json;
|
||||||
*/
|
*/
|
||||||
class JsonManipulator
|
class JsonManipulator
|
||||||
{
|
{
|
||||||
private static $RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*';
|
private static $RECURSE_BLOCKS;
|
||||||
|
private static $JSON_VALUE;
|
||||||
|
private static $JSON_STRING;
|
||||||
|
|
||||||
private $contents;
|
private $contents;
|
||||||
private $newline;
|
private $newline;
|
||||||
|
@ -25,6 +27,12 @@ class JsonManipulator
|
||||||
|
|
||||||
public function __construct($contents)
|
public function __construct($contents)
|
||||||
{
|
{
|
||||||
|
if (!self::$RECURSE_BLOCKS) {
|
||||||
|
self::$RECURSE_BLOCKS = '(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{(?:[^{}]*|\{[^{}]*\})*\})*\})*\})*';
|
||||||
|
self::$JSON_STRING = '"(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"';
|
||||||
|
self::$JSON_VALUE = '(?:[0-9.]+|null|true|false|'.self::$JSON_STRING.'|\[[^\]]*\]|\{'.self::$RECURSE_BLOCKS.'\})';
|
||||||
|
}
|
||||||
|
|
||||||
$contents = trim($contents);
|
$contents = trim($contents);
|
||||||
if (!preg_match('#^\{(.*)\}$#s', $contents)) {
|
if (!preg_match('#^\{(.*)\}$#s', $contents)) {
|
||||||
throw new \InvalidArgumentException('The json file must be an object ({})');
|
throw new \InvalidArgumentException('The json file must be an object ({})');
|
||||||
|
@ -43,7 +51,7 @@ class JsonManipulator
|
||||||
{
|
{
|
||||||
// no link of that type yet
|
// no link of that type yet
|
||||||
if (!preg_match('#"'.$type.'":\s*\{#', $this->contents)) {
|
if (!preg_match('#"'.$type.'":\s*\{#', $this->contents)) {
|
||||||
$this->addMainKey($type, $this->format(array($package => $constraint)));
|
$this->addMainKey($type, array($package => $constraint));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +108,7 @@ class JsonManipulator
|
||||||
{
|
{
|
||||||
// no main node yet
|
// no main node yet
|
||||||
if (!preg_match('#"'.$mainNode.'":\s*\{#', $this->contents)) {
|
if (!preg_match('#"'.$mainNode.'":\s*\{#', $this->contents)) {
|
||||||
$this->addMainKey(''.$mainNode.'', $this->format(array($name => $value)));
|
$this->addMainKey(''.$mainNode.'', array($name => $value));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -126,8 +134,8 @@ class JsonManipulator
|
||||||
$that = $this;
|
$that = $this;
|
||||||
|
|
||||||
// child exists
|
// child exists
|
||||||
if (preg_match('{("'.preg_quote($name).'"\s*:\s*)([0-9.]+|null|true|false|"[^"]+"|\[[^\]]*\]|\{'.self::$RECURSE_BLOCKS.'\})(,?)}', $children, $matches)) {
|
if (preg_match('{("'.preg_quote($name).'"\s*:\s*)('.self::$JSON_VALUE.')(,?)}', $children, $matches)) {
|
||||||
$children = preg_replace_callback('{("'.preg_quote($name).'"\s*:\s*)([0-9.]+|null|true|false|"[^"]+"|\[[^\]]*\]|\{'.self::$RECURSE_BLOCKS.'\})(,?)}', function ($matches) use ($name, $subName, $value, $that) {
|
$children = preg_replace_callback('{("'.preg_quote($name).'"\s*:\s*)('.self::$JSON_VALUE.')(,?)}', function ($matches) use ($name, $subName, $value, $that) {
|
||||||
if ($subName !== null) {
|
if ($subName !== null) {
|
||||||
$curVal = json_decode($matches[2], true);
|
$curVal = json_decode($matches[2], true);
|
||||||
$curVal[$subName] = $value;
|
$curVal[$subName] = $value;
|
||||||
|
@ -194,7 +202,7 @@ class JsonManipulator
|
||||||
// try and find a match for the subkey
|
// try and find a match for the subkey
|
||||||
if (preg_match('{"'.preg_quote($name).'"\s*:}i', $children)) {
|
if (preg_match('{"'.preg_quote($name).'"\s*:}i', $children)) {
|
||||||
// find best match for the value of "name"
|
// find best match for the value of "name"
|
||||||
if (preg_match_all('{"'.preg_quote($name).'"\s*:\s*(?:[0-9.]+|null|true|false|"[^"]+"|\[[^\]]*\]|\{'.self::$RECURSE_BLOCKS.'\})}', $children, $matches)) {
|
if (preg_match_all('{"'.preg_quote($name).'"\s*:\s*(?:'.self::$JSON_VALUE.')}', $children, $matches)) {
|
||||||
$bestMatch = '';
|
$bestMatch = '';
|
||||||
foreach ($matches[0] as $match) {
|
foreach ($matches[0] as $match) {
|
||||||
if (strlen($bestMatch) < strlen($match)) {
|
if (strlen($bestMatch) < strlen($match)) {
|
||||||
|
@ -241,19 +249,41 @@ class JsonManipulator
|
||||||
|
|
||||||
public function addMainKey($key, $content)
|
public function addMainKey($key, $content)
|
||||||
{
|
{
|
||||||
|
$content = $this->format($content);
|
||||||
|
|
||||||
|
// key exists already
|
||||||
|
$regex = '{^(\s*\{\s*(?:'.self::$JSON_STRING.'\s*:\s*'.self::$JSON_VALUE.'\s*,\s*)*?)'.
|
||||||
|
'('.preg_quote(JsonFile::encode($key)).'\s*:\s*'.self::$JSON_VALUE.')(.*)}s';
|
||||||
|
if (preg_match($regex, $this->contents, $matches)) {
|
||||||
|
// invalid match due to un-regexable content, abort
|
||||||
|
if (!json_decode('{'.$matches[2].'}')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->contents = $matches[1] . JsonFile::encode($key).': '.$content . $matches[3];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// append at the end of the file and keep whitespace
|
||||||
if (preg_match('#[^{\s](\s*)\}$#', $this->contents, $match)) {
|
if (preg_match('#[^{\s](\s*)\}$#', $this->contents, $match)) {
|
||||||
$this->contents = preg_replace(
|
$this->contents = preg_replace(
|
||||||
'#'.$match[1].'\}$#',
|
'#'.$match[1].'\}$#',
|
||||||
addcslashes(',' . $this->newline . $this->indent . JsonFile::encode($key). ': '. $content . $this->newline . '}', '\\'),
|
addcslashes(',' . $this->newline . $this->indent . JsonFile::encode($key). ': '. $content . $this->newline . '}', '\\'),
|
||||||
$this->contents
|
$this->contents
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
$this->contents = preg_replace(
|
return true;
|
||||||
'#\}$#',
|
|
||||||
addcslashes($this->indent . JsonFile::encode($key). ': '.$content . $this->newline . '}', '\\'),
|
|
||||||
$this->contents
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// append at the end of the file
|
||||||
|
$this->contents = preg_replace(
|
||||||
|
'#\}$#',
|
||||||
|
addcslashes($this->indent . JsonFile::encode($key). ': '.$content . $this->newline . '}', '\\'),
|
||||||
|
$this->contents
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function format($data, $depth = 0)
|
public function format($data, $depth = 0)
|
||||||
|
|
|
@ -611,6 +611,57 @@ class JsonManipulatorTest extends \PHPUnit_Framework_TestCase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
', $manipulator->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddMainKey()
|
||||||
|
{
|
||||||
|
$manipulator = new JsonManipulator('{
|
||||||
|
"foo": "bar"
|
||||||
|
}');
|
||||||
|
|
||||||
|
$this->assertTrue($manipulator->addMainKey('bar', 'baz'));
|
||||||
|
$this->assertEquals('{
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": "baz"
|
||||||
|
}
|
||||||
|
', $manipulator->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdateMainKey()
|
||||||
|
{
|
||||||
|
$manipulator = new JsonManipulator('{
|
||||||
|
"foo": "bar"
|
||||||
|
}');
|
||||||
|
|
||||||
|
$this->assertTrue($manipulator->addMainKey('foo', 'baz'));
|
||||||
|
$this->assertEquals('{
|
||||||
|
"foo": "baz"
|
||||||
|
}
|
||||||
|
', $manipulator->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdateMainKey2()
|
||||||
|
{
|
||||||
|
$manipulator = new JsonManipulator('{
|
||||||
|
"a": {
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "qux"
|
||||||
|
},
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "bar"
|
||||||
|
}');
|
||||||
|
|
||||||
|
$this->assertTrue($manipulator->addMainKey('foo', 'baz'));
|
||||||
|
$this->assertTrue($manipulator->addMainKey('baz', 'quux'));
|
||||||
|
$this->assertEquals('{
|
||||||
|
"a": {
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "qux"
|
||||||
|
},
|
||||||
|
"foo": "baz",
|
||||||
|
"baz": "quux"
|
||||||
|
}
|
||||||
', $manipulator->getContents());
|
', $manipulator->getContents());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue