Passed
Push — master ( e702bd...da1a23 )
by Jitendra
01:56
created

Comment::inString()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 3
nc 2
nop 2
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Ahc\Json;
4
5
/**
6
 * JSON comment stripper.
7
 *
8
 * @author Jitendra Adhikari <[email protected]>
9
 */
10
class Comment
11
{
12
    /** @var int The current index being scanned */
13
    protected $index   = -1;
14
15
    /** @var bool If current char is within a string */
16
    protected $inStr   = false;
17
18
    /** @var int Lines of comments 0 = no comment, 1 = single line, 2 = multi lines */
19
    protected $comment = 0;
20
21
    /**
22
     * Strip comments from JSON string.
23
     *
24
     * @param string $json
25
     *
26
     * @return string The comment stripped JSON.
27
     */
28
    public function strip($json)
29
    {
30
        if (!\preg_match('%\/(\/|\*)%', $json)) {
31
            return $json;
32
        }
33
34
        $this->reset();
35
36
        return $this->doStrip($json);
37
    }
38
39
    protected function reset()
40
    {
41
        $this->index   = -1;
42
        $this->inStr   = false;
43
        $this->comment = 0;
44
    }
45
46
    protected function doStrip($json)
47
    {
48
        $return = '';
49
50
        while (isset($json[++$this->index])) {
51
            list($prev, $char, $next) = $this->getSegments($json);
52
53
            if ($this->inStringOrCommentEnd($prev, $char, $char . $next)) {
54
                $return .= $char;
55
56
                continue;
57
            }
58
59
            $wasSingle = 1 === $this->comment;
60
            if ($this->hasCommentEnded($char, $char . $next) && $wasSingle) {
61
                $return = \rtrim($return) . $char;
62
            }
63
64
            $this->index += $char . $next === '*/' ? 1 : 0;
65
        }
66
67
        return $return;
68
    }
69
70
    protected function getSegments($json)
71
    {
72
        return [
73
            isset($json[$this->index - 1]) ? $json[$this->index - 1] : '',
74
            $json[$this->index],
75
            isset($json[$this->index + 1]) ? $json[$this->index + 1] : '',
76
        ];
77
    }
78
79
    protected function inStringOrCommentEnd($prev, $char, $charnext)
80
    {
81
        return $this->inString($char, $prev) || $this->inCommentEnd($charnext);
82
    }
83
84
    protected function inString($char, $prev)
85
    {
86
        if (0 === $this->comment && $char === '"' && $prev !== '\\') {
87
            $this->inStr = !$this->inStr;
88
        }
89
90
        return $this->inStr;
91
    }
92
93
    protected function inCommentEnd($charnext)
94
    {
95
        if (!$this->inStr && 0 === $this->comment) {
96
            $this->comment = $charnext === '//' ? 1 : ($charnext === '/*' ? 2 : 0);
97
        }
98
99
        return 0 === $this->comment;
100
    }
101
102
    protected function hasCommentEnded($char, $charnext)
103
    {
104
        $singleEnded = $this->comment === 1 && $char == "\n";
105
        $multiEnded  = $this->comment === 2 && $charnext == '*/';
106
107
        if ($singleEnded || $multiEnded) {
108
            $this->comment = 0;
109
110
            return true;
111
        }
112
113
        return false;
114
    }
115
116
    /**
117
     * Strip comments and decode JSON string.
118
     *
119
     * @param string    $json
120
     * @param bool|bool $assoc
121
     * @param int|int   $depth
122
     * @param int|int   $options
123
     *
124
     * @see http://php.net/json_decode [JSON decode native function]
125
     *
126
     * @throws \RuntimeException When decode fails.
127
     *
128
     * @return mixed
129
     */
130
    public function decode($json, $assoc = false, $depth = 512, $options = 0)
131
    {
132
        $decoded = \json_decode($this->strip($json), $assoc, $depth, $options);
133
134
        if (\JSON_ERROR_NONE !== $err = \json_last_error()) {
135
            $msg = 'JSON decode failed';
136
137
            if (\function_exists('json_last_error_msg')) {
138
                $msg .= ': ' . \json_last_error_msg();
139
            }
140
141
            throw new \RuntimeException($msg, $err);
142
        }
143
144
        return $decoded;
145
    }
146
147
    /**
148
     * Static alias of decode().
149
     */
150
    public static function parse($json, $assoc = false, $depth = 512, $options = 0)
151
    {
152
        static $parser;
153
154
        if (!$parser) {
155
            $parser = new static;
156
        }
157
158
        return $parser->decode($json, $assoc, $depth, $options);
159
    }
160
}
161