Passed
Push — master ( fca849...e702bd )
by Jitendra
05:03
created

Comment::getSegments()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 4
nop 1
dl 0
loc 6
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
        if (0 === $this->comment && $char === '"' && $prev !== '\\') {
82
            $this->inStr = !$this->inStr;
83
        }
84
85
        if (!$this->inStr && 0 === $this->comment) {
86
            $this->comment = $charnext === '//' ? 1 : ($charnext === '/*' ? 2 : 0);
87
        }
88
89
        return $this->inStr || 0 === $this->comment;
90
    }
91
92
    protected function hasCommentEnded($char, $charnext)
93
    {
94
        $singleEnded = $this->comment === 1 && $char == "\n";
95
        $multiEnded  = $this->comment === 2 && $charnext == '*/';
96
97
        if ($singleEnded || $multiEnded) {
98
            $this->comment = 0;
99
100
            return true;
101
        }
102
103
        return false;
104
    }
105
106
    /**
107
     * Strip comments and decode JSON string.
108
     *
109
     * @param string    $json
110
     * @param bool|bool $assoc
111
     * @param int|int   $depth
112
     * @param int|int   $options
113
     *
114
     * @see http://php.net/json_decode [JSON decode native function]
115
     *
116
     * @throws \RuntimeException When decode fails.
117
     *
118
     * @return mixed
119
     */
120
    public function decode($json, $assoc = false, $depth = 512, $options = 0)
121
    {
122
        $decoded = \json_decode($this->strip($json), $assoc, $depth, $options);
123
124
        if (\JSON_ERROR_NONE !== $err = \json_last_error()) {
125
            $msg = 'JSON decode failed';
126
127
            if (\function_exists('json_last_error_msg')) {
128
                $msg .= ': ' . \json_last_error_msg();
129
            }
130
131
            throw new \RuntimeException($msg, $err);
132
        }
133
134
        return $decoded;
135
    }
136
137
    /**
138
     * Static alias of decode().
139
     */
140
    public static function parse($json, $assoc = false, $depth = 512, $options = 0)
141
    {
142
        static $parser;
143
144
        if (!$parser) {
145
            $parser = new static;
146
        }
147
148
        return $parser->decode($json, $assoc, $depth, $options);
149
    }
150
}
151