Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
38.78% covered (danger)
38.78%
38 / 98
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Less_Exception_Chunk
38.78% covered (danger)
38.78%
38 / 98
50.00% covered (danger)
50.00%
2 / 4
576.76
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 Chunks
31.40% covered (danger)
31.40%
27 / 86
0.00% covered (danger)
0.00%
0 / 1
669.12
 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    }
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}