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
2/**
3 * @private
4 */
5class 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        $this->getFinalMessage();
36    }
37
38    /**
39     * See less.js chunks()
40     * We don't actually need the chunks
41     */
42    protected function Chunks() {
43        $level = 0;
44        $parenLevel = 0;
45        $lastMultiCommentEndBrace = null;
46        $lastOpening = null;
47        $lastMultiComment = null;
48        $lastParen = null;
49
50        // phpcs:ignore Generic.CodeAnalysis.JumbledIncrementer
51        for ( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ) {
52            $cc = $this->CharCode( $this->parserCurrentIndex );
53            if ( ( ( $cc >= 97 ) && ( $cc <= 122 ) ) || ( $cc < 34 ) ) {
54                // a-z or whitespace
55                continue;
56            }
57
58            switch ( $cc ) {
59
60                // (
61                case 40:
62                    $parenLevel++;
63                    $lastParen = $this->parserCurrentIndex;
64                    break;
65
66                // )
67                case 41:
68                    $parenLevel--;
69                    if ( $parenLevel < 0 ) {
70                        return $this->fail( "missing opening `(`" );
71                    }
72                    break;
73
74                // ;
75                case 59:
76                    // if (!$parenLevel) { $this->emitChunk();   }
77                    break;
78
79                // {
80                case 123:
81                    $level++;
82                    $lastOpening = $this->parserCurrentIndex;
83                    break;
84
85                // }
86                case 125:
87                    $level--;
88                    if ( $level < 0 ) {
89                        return $this->fail( "missing opening `{`" );
90
91                    }
92                    // if (!$level && !$parenLevel) { $this->emitChunk(); }
93                    break;
94                // \
95                case 92:
96                    if ( $this->parserCurrentIndex < $this->input_len - 1 ) {
97                        $this->parserCurrentIndex++;
98                        break;
99                    }
100                    return $this->fail( "unescaped `\\`" );
101
102                // ", ' and `
103                case 34:
104                case 39:
105                case 96:
106                    $matched = 0;
107                    $currentChunkStartIndex = $this->parserCurrentIndex;
108                    for ( $this->parserCurrentIndex += 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ) {
109                        $cc2 = $this->CharCode( $this->parserCurrentIndex );
110                        if ( $cc2 > 96 ) {
111                            continue;
112                        }
113                        if ( $cc2 == $cc ) {
114                            $matched = 1;
115                            break;
116                        }
117                        if ( $cc2 == 92 ) {        // \
118                            if ( $this->parserCurrentIndex == $this->input_len - 1 ) {
119                                return $this->fail( "unescaped `\\`" );
120                            }
121                            $this->parserCurrentIndex++;
122                        }
123                    }
124                    if ( $matched ) {
125                        break;
126                    }
127                    return $this->fail( "unmatched `" . chr( $cc ) . "`", $currentChunkStartIndex );
128
129                // /, check for comment
130                case 47:
131                    if ( $parenLevel || ( $this->parserCurrentIndex == $this->input_len - 1 ) ) {
132                        break;
133                    }
134                    $cc2 = $this->CharCode( $this->parserCurrentIndex + 1 );
135                    if ( $cc2 == 47 ) {
136                        // //, find lnfeed
137                        for ( $this->parserCurrentIndex += 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ) {
138                            $cc2 = $this->CharCode( $this->parserCurrentIndex );
139                            if ( ( $cc2 <= 13 ) && ( ( $cc2 == 10 ) || ( $cc2 == 13 ) ) ) {
140                                break;
141                            }
142                        }
143                    } elseif ( $cc2 == 42 ) {
144                        // /*, find */
145                        $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;
146                        for ( $this->parserCurrentIndex += 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++ ) {
147                            $cc2 = $this->CharCode( $this->parserCurrentIndex );
148                            if ( $cc2 == 125 ) {
149                                $lastMultiCommentEndBrace = $this->parserCurrentIndex;
150                            }
151                            if ( $cc2 != 42 ) {
152                                continue;
153                            }
154                            if ( $this->CharCode( $this->parserCurrentIndex + 1 ) == 47 ) {
155                                break;
156                            }
157                        }
158                        if ( $this->parserCurrentIndex == $this->input_len - 1 ) {
159                            return $this->fail( "missing closing `*/`", $currentChunkStartIndex );
160                        }
161                    }
162                    break;
163
164                // *, check for unmatched */
165                case 42:
166                    if ( ( $this->parserCurrentIndex < $this->input_len - 1 ) && ( $this->CharCode( $this->parserCurrentIndex + 1 ) == 47 ) ) {
167                        return $this->fail( "unmatched `/*`" );
168                    }
169                    break;
170            }
171        }
172
173        if ( $level !== 0 ) {
174            if ( ( $lastMultiComment > $lastOpening ) && ( $lastMultiCommentEndBrace > $lastMultiComment ) ) {
175                return $this->fail( "missing closing `}` or `*/`", $lastOpening );
176            } else {
177                return $this->fail( "missing closing `}`", $lastOpening );
178            }
179        } elseif ( $parenLevel !== 0 ) {
180            return $this->fail( "missing closing `)`", $lastParen );
181        }
182
183        // chunk didn't fail
184
185        //$this->emitChunk(true);
186    }
187
188    public function CharCode( $pos ) {
189        return ord( $this->input[$pos] );
190    }
191
192    public function fail( $msg, $index = null ) {
193        if ( !$index ) {
194            $this->index = $this->parserCurrentIndex;
195        } else {
196            $this->index = $index;
197        }
198        $this->message = 'ParseError: ' . $msg;
199    }
200
201    /*
202    function emitChunk( $force = false ){
203        $len = $this->parserCurrentIndex - $this->emitFrom;
204        if ((($len < 512) && !$force) || !$len) {
205            return;
206        }
207        $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom );
208        $this->emitFrom = $this->parserCurrentIndex + 1;
209    }
210    */
211
212}