Skip to content

Commit 3e2c03c

Browse files
committed
feat(comment): support trimming trailing comma at end of array or object
1 parent f2e2cad commit 3e2c03c

File tree

1 file changed

+37
-10
lines changed

1 file changed

+37
-10
lines changed

src/Comment.php

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,24 @@
1212
namespace Ahc\Json;
1313

1414
/**
15-
* JSON comment stripper.
15+
* JSON comment and trailing comma stripper.
1616
*
1717
* @author Jitendra Adhikari <jiten.adhikary@gmail.com>
1818
*/
1919
class Comment
2020
{
2121
/** @var int The current index being scanned */
22-
protected $index = -1;
22+
protected $index = -1;
2323

2424
/** @var bool If current char is within a string */
25-
protected $inStr = false;
25+
protected $inStr = false;
2626

2727
/** @var int Lines of comments 0 = no comment, 1 = single line, 2 = multi lines */
2828
protected $comment = 0;
2929

30+
/** @var int Holds the backtace position of a possibly trailing comma */
31+
protected $commaPos = -1;
32+
3033
/**
3134
* Strip comments from JSON string.
3235
*
@@ -36,7 +39,7 @@ class Comment
3639
*/
3740
public function strip($json)
3841
{
39-
if (!\preg_match('%\/(\/|\*)%', $json)) {
42+
if (!\preg_match('%\/(\/|\*)%', $json) && !\preg_match('/,\s*(\}|\])/', $json)) {
4043
return $json;
4144
}
4245

@@ -59,6 +62,8 @@ protected function doStrip($json)
5962
while (isset($json[++$this->index])) {
6063
list($prev, $char, $next) = $this->getSegments($json);
6164

65+
$return = $this->checkTrail($char, $return);
66+
6267
if ($this->inStringOrCommentEnd($prev, $char, $char . $next)) {
6368
$return .= $char;
6469

@@ -85,9 +90,31 @@ protected function getSegments($json)
8590
];
8691
}
8792

88-
protected function inStringOrCommentEnd($prev, $char, $charnext)
93+
protected function checkTrail($char, $json)
94+
{
95+
if ($char === ',' || $this->commaPos === -1) {
96+
$this->commaPos = $this->commaPos + ($char === ',' ? 1 : 0);
97+
98+
return $json;
99+
}
100+
101+
if (\ctype_digit($char) || \strpbrk($char, '"tfn{[')) {
102+
$this->commaPos = -1;
103+
} elseif ($char === ']' || $char === '}') {
104+
$pos = \strlen($json) - $this->commaPos - 1;
105+
$json = \substr($json, 0, $pos) . \ltrim(\substr($json, $pos), ',');
106+
107+
$this->commaPos = -1;
108+
} else {
109+
$this->commaPos += 1;
110+
}
111+
112+
return $json;
113+
}
114+
115+
protected function inStringOrCommentEnd($prev, $char, $next)
89116
{
90-
return $this->inString($char, $prev) || $this->inCommentEnd($charnext);
117+
return $this->inString($char, $prev) || $this->inCommentEnd($next);
91118
}
92119

93120
protected function inString($char, $prev)
@@ -99,19 +126,19 @@ protected function inString($char, $prev)
99126
return $this->inStr;
100127
}
101128

102-
protected function inCommentEnd($charnext)
129+
protected function inCommentEnd($next)
103130
{
104131
if (!$this->inStr && 0 === $this->comment) {
105-
$this->comment = $charnext === '//' ? 1 : ($charnext === '/*' ? 2 : 0);
132+
$this->comment = $next === '//' ? 1 : ($next === '/*' ? 2 : 0);
106133
}
107134

108135
return 0 === $this->comment;
109136
}
110137

111-
protected function hasCommentEnded($char, $charnext)
138+
protected function hasCommentEnded($char, $next)
112139
{
113140
$singleEnded = $this->comment === 1 && $char == "\n";
114-
$multiEnded = $this->comment === 2 && $charnext == '*/';
141+
$multiEnded = $this->comment === 2 && $next == '*/';
115142

116143
if ($singleEnded || $multiEnded) {
117144
$this->comment = 0;

0 commit comments

Comments
 (0)