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