Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
38.78% |
38 / 98 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
Less_Exception_Chunk | |
38.78% |
38 / 98 |
|
50.00% |
2 / 4 |
576.76 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
Chunks | |
31.40% |
27 / 86 |
|
0.00% |
0 / 1 |
669.12 | |||
CharCode | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
fail | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 |
1 | <?php |
2 | /** |
3 | * @private |
4 | */ |
5 | class Less_Exception_Chunk extends Less_Exception_Parser { |
6 | |
7 | /** @var int */ |
8 | protected $parserCurrentIndex = 0; |
9 | |
10 | /** @var int */ |
11 | protected $emitFrom = 0; |
12 | |
13 | /** @var int */ |
14 | protected $input_len; |
15 | |
16 | /** |
17 | * @param string $input |
18 | * @param Exception|null $previous Previous exception |
19 | * @param int|null $index The current parser index |
20 | * @param array|null $currentFile The file |
21 | * @param int $code The exception code |
22 | */ |
23 | public function __construct( $input, ?Exception $previous = null, $index = null, $currentFile = null, $code = 0 ) { |
24 | $this->message = 'ParseError: Unexpected input'; // default message |
25 | |
26 | $this->index = $index; |
27 | |
28 | $this->currentFile = $currentFile; |
29 | |
30 | $this->input = $input; |
31 | $this->input_len = strlen( $input ); |
32 | |
33 | $this->Chunks(); |
34 | $this->genMessage(); |
35 | } |
36 | |
37 | /** |
38 | * See less.js chunks() |
39 | * We don't actually need the chunks |
40 | */ |
41 | protected function Chunks() { |
42 | $level = 0; |
43 | $parenLevel = 0; |
44 | $lastMultiCommentEndBrace = null; |
45 | $lastOpening = null; |
46 | $lastMultiComment = null; |
47 | $lastParen = null; |
48 | |
49 | // phpcs:ignore Generic.CodeAnalysis.JumbledIncrementer |
50 | for ( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ) { |
51 | $cc = $this->CharCode( $this->parserCurrentIndex ); |
52 | if ( ( ( $cc >= 97 ) && ( $cc <= 122 ) ) || ( $cc < 34 ) ) { |
53 | // a-z or whitespace |
54 | continue; |
55 | } |
56 | |
57 | switch ( $cc ) { |
58 | |
59 | // ( |
60 | case 40: |
61 | $parenLevel++; |
62 | $lastParen = $this->parserCurrentIndex; |
63 | break; |
64 | |
65 | // ) |
66 | case 41: |
67 | $parenLevel--; |
68 | if ( $parenLevel < 0 ) { |
69 | return $this->fail( "missing opening `(`" ); |
70 | } |
71 | break; |
72 | |
73 | // ; |
74 | case 59: |
75 | // if (!$parenLevel) { $this->emitChunk(); } |
76 | break; |
77 | |
78 | // { |
79 | case 123: |
80 | $level++; |
81 | $lastOpening = $this->parserCurrentIndex; |
82 | break; |
83 | |
84 | // } |
85 | case 125: |
86 | $level--; |
87 | if ( $level < 0 ) { |
88 | return $this->fail( "missing opening `{`" ); |
89 | |
90 | } |
91 | // if (!$level && !$parenLevel) { $this->emitChunk(); } |
92 | break; |
93 | // \ |
94 | case 92: |
95 | if ( $this->parserCurrentIndex < $this->input_len - 1 ) { |
96 | $this->parserCurrentIndex++; |
97 | break; |
98 | } |
99 | return $this->fail( "unescaped `\\`" ); |
100 | |
101 | // ", ' and ` |
102 | case 34: |
103 | case 39: |
104 | case 96: |
105 | $matched = 0; |
106 | $currentChunkStartIndex = $this->parserCurrentIndex; |
107 | for ( $this->parserCurrentIndex += 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ) { |
108 | $cc2 = $this->CharCode( $this->parserCurrentIndex ); |
109 | if ( $cc2 > 96 ) { |
110 | continue; |
111 | } |
112 | if ( $cc2 == $cc ) { |
113 | $matched = 1; |
114 | break; |
115 | } |
116 | if ( $cc2 == 92 ) { // \ |
117 | if ( $this->parserCurrentIndex == $this->input_len - 1 ) { |
118 | return $this->fail( "unescaped `\\`" ); |
119 | } |
120 | $this->parserCurrentIndex++; |
121 | } |
122 | } |
123 | if ( $matched ) { |
124 | break; |
125 | } |
126 | return $this->fail( "unmatched `" . chr( $cc ) . "`", $currentChunkStartIndex ); |
127 | |
128 | // /, check for comment |
129 | case 47: |
130 | if ( $parenLevel || ( $this->parserCurrentIndex == $this->input_len - 1 ) ) { |
131 | break; |
132 | } |
133 | $cc2 = $this->CharCode( $this->parserCurrentIndex + 1 ); |
134 | if ( $cc2 == 47 ) { |
135 | // //, find lnfeed |
136 | for ( $this->parserCurrentIndex += 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ) { |
137 | $cc2 = $this->CharCode( $this->parserCurrentIndex ); |
138 | if ( ( $cc2 <= 13 ) && ( ( $cc2 == 10 ) || ( $cc2 == 13 ) ) ) { |
139 | break; |
140 | } |
141 | } |
142 | } elseif ( $cc2 == 42 ) { |
143 | // /*, find */ |
144 | $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex; |
145 | for ( $this->parserCurrentIndex += 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++ ) { |
146 | $cc2 = $this->CharCode( $this->parserCurrentIndex ); |
147 | if ( $cc2 == 125 ) { |
148 | $lastMultiCommentEndBrace = $this->parserCurrentIndex; |
149 | } |
150 | if ( $cc2 != 42 ) { |
151 | continue; |
152 | } |
153 | if ( $this->CharCode( $this->parserCurrentIndex + 1 ) == 47 ) { |
154 | break; |
155 | } |
156 | } |
157 | if ( $this->parserCurrentIndex == $this->input_len - 1 ) { |
158 | return $this->fail( "missing closing `*/`", $currentChunkStartIndex ); |
159 | } |
160 | } |
161 | break; |
162 | |
163 | // *, check for unmatched */ |
164 | case 42: |
165 | if ( ( $this->parserCurrentIndex < $this->input_len - 1 ) && ( $this->CharCode( $this->parserCurrentIndex + 1 ) == 47 ) ) { |
166 | return $this->fail( "unmatched `/*`" ); |
167 | } |
168 | break; |
169 | } |
170 | } |
171 | |
172 | if ( $level !== 0 ) { |
173 | if ( ( $lastMultiComment > $lastOpening ) && ( $lastMultiCommentEndBrace > $lastMultiComment ) ) { |
174 | return $this->fail( "missing closing `}` or `*/`", $lastOpening ); |
175 | } else { |
176 | return $this->fail( "missing closing `}`", $lastOpening ); |
177 | } |
178 | } elseif ( $parenLevel !== 0 ) { |
179 | return $this->fail( "missing closing `)`", $lastParen ); |
180 | } |
181 | |
182 | // chunk didn't fail |
183 | |
184 | //$this->emitChunk(true); |
185 | } |
186 | |
187 | public function CharCode( $pos ) { |
188 | return ord( $this->input[$pos] ); |
189 | } |
190 | |
191 | public function fail( $msg, $index = null ) { |
192 | if ( !$index ) { |
193 | $this->index = $this->parserCurrentIndex; |
194 | } else { |
195 | $this->index = $index; |
196 | } |
197 | $this->message = 'ParseError: ' . $msg; |
198 | } |
199 | |
200 | /* |
201 | function emitChunk( $force = false ){ |
202 | $len = $this->parserCurrentIndex - $this->emitFrom; |
203 | if ((($len < 512) && !$force) || !$len) { |
204 | return; |
205 | } |
206 | $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom ); |
207 | $this->emitFrom = $this->parserCurrentIndex + 1; |
208 | } |
209 | */ |
210 | |
211 | } |