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