Completed
Push — master ( 9ad54b...895d73 )
by Federico
03:21
created

Scanner::scanAttributes()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 5
eloc 15
c 1
b 0
f 1
nc 4
nop 0
dl 0
loc 29
rs 8.439
1
<?php
2
3
namespace Jade\Lexer;
4
5
/**
6
 * Class Jade\Lexer\Scanner.
7
 */
8
abstract class Scanner extends MixinScanner
9
{
10
    /**
11
     *  Helper to create tokens.
12
     */
13
    protected function scan($regex, $type, $captureIndex = 1)
14
    {
15
        if (preg_match($regex, $this->input, $matches)) {
16
            $this->consume($matches[0]);
17
18
            return $this->token($type, isset($matches[$captureIndex]) && strlen($matches[$captureIndex]) > 0 ? $matches[$captureIndex] : '');
19
        }
20
    }
21
22
    /**
23
     * Scan comment from input & return it if found.
24
     *
25
     * @return object|null
26
     */
27
    protected function scanComment()
28
    {
29
        $indent = count($this->indentStack) ? $this->indentStack[0] : 0;
30
        if (preg_match('/^ *\/\/(-)?([^\n]*(\n+[ \t]{' . ($indent + 1) . ',}[^\n]*)*)/', $this->input, $matches)) {
31
            $this->consume($matches[0]);
32
            $value = isset($matches[2]) ? $matches[2] : '';
33
            if (isset($matches[3])) {
34
                $value .= "\n";
35
            }
36
            $token = $this->token('comment', $value);
37
            $token->buffer = '-' !== $matches[1];
38
39
            return $token;
40
        }
41
    }
42
43
    /**
44
     * @return object
45
     */
46
    protected function scanInterpolation()
47
    {
48
        return $this->scan('/^#{(.*?)}/', 'interpolation');
49
    }
50
51
    /**
52
     * @return object
53
     */
54
    protected function scanTag()
55
    {
56
        if (preg_match('/^(\w[-:\w]*)(\/?)/', $this->input, $matches)) {
57
            $this->consume($matches[0]);
58
            $name = $matches[1];
59
60
            if (':' === substr($name, -1) && ':' !== substr($name, -2, 1)) {
61
                $name = substr($name, 0, -1);
62
                $this->defer($this->token(':'));
63
64
                while (' ' === substr($this->input, 0, 1)) {
65
                    $this->consume(' ');
66
                }
67
            }
68
69
            $token = $this->token('tag', $name);
70
            $token->selfClosing = ($matches[2] === '/');
71
72
            return $token;
73
        }
74
    }
75
76
    /**
77
     * @return object
78
     */
79
    protected function scanFilter()
80
    {
81
        return $this->scan('/^(?<!:):(?!:)(\w+(?:-\w+)*)/', 'filter');
82
    }
83
84
    /**
85
     * @return object
86
     */
87
    protected function scanDoctype()
88
    {
89
        return $this->scan('/^(?:!!!|doctype) *([^\n]+)?/', 'doctype');
90
    }
91
92
    /**
93
     * @return object
94
     */
95
    protected function scanId()
96
    {
97
        return $this->scan('/^#([\w-]+)/', 'id');
98
    }
99
100
    /**
101
     * @return object
102
     */
103
    protected function scanClassName()
104
    {
105
        // http://www.w3.org/TR/CSS21/grammar.html#scanner
106
        //
107
        // ident:
108
        //      -?{nmstart}{nmchar}*
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
109
        // nmstart:
110
        //      [_a-z]|{nonascii}|{escape}
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
111
        // nonascii:
112
        //      [\240-\377]
113
        // escape:
114
        //      {unicode}|\\[^\r\n\f0-9a-f]
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
115
        // unicode:
116
        //      \\{h}{1,6}(\r\n|[ \t\r\n\f])?
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
117
        // nmchar:
118
        //      [_a-z0-9-]|{nonascii}|{escape}
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
119
        //
120
        // /^(-?(?!=[0-9-])(?:[_a-z0-9-]|[\240-\377]|\\{h}{1,6}(?:\r\n|[ \t\r\n\f])?|\\[^\r\n\f0-9a-f])+)/
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
121
        return $this->scan('/^\.([\w-]+)/', 'class');
122
    }
123
124
    /**
125
     * @return object
126
     */
127
    protected function scanText()
128
    {
129
        return $this->scan('/^(?:\| ?| ?)?([^\n]+)/', 'text');
130
    }
131
132
    /**
133
     * @return object
134
     */
135
    protected function scanAssignment()
136
    {
137
        if (preg_match('/^(\$?\w+) += *([^;\n]+|\'[^\']+\'|"[^"]+")( *;? *)/', $this->input, $matches)) {
138
            $this->consume($matches[0]);
139
140
            return $this->token('code', (substr($matches[1], 0, 1) === '$' ? '' : '$') . $matches[1] . '=' . $matches[2]);
141
        }
142
    }
143
144
    /**
145
     * @return object
146
     */
147
    protected function scanConditional()
148
    {
149
        if (preg_match('/^(if|unless|else if|elseif|else|while)\b([^\n]*)/', $this->input, $matches)) {
150
            $this->consume($matches[0]);
151
152
            /*switch ($matches[1]) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
153
                case 'if': $code = 'if (' . $matches[2] . '):'; break;
154
                case 'unless': $code = 'if (!(' . $matches[2] . ')):'; break;
155
                case 'else if': $code = 'elseif (' . $matches[2] . '):'; break;
156
                case 'else': $code = 'else (' . $matches[2] . '):'; break;
157
            }*/
158
            $code = $this->normalizeCode($matches[0]);
159
            $token = $this->token('code', $code);
160
            $token->buffer = false;
161
162
            return $token;
163
        }
164
    }
165
166
    /**
167
     * @return object
168
     */
169 View Code Duplication
    protected function scanEach()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
    {
171
        if (preg_match('/^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? +in *([^\n]+)/', $this->input, $matches)) {
172
            $this->consume($matches[0]);
173
174
            $token = $this->token('each', $matches[1]);
175
            $token->key = $matches[2];
176
            $token->code = $this->normalizeCode($matches[3]);
177
178
            return $token;
179
        }
180
    }
181
182
    /**
183
     * @return object
184
     */
185
    protected function scanCustomKeyword()
186
    {
187
        if (
188
            count($this->customKeywords) &&
0 ignored issues
show
Bug introduced by
The property customKeywords does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
189
            preg_match('/^([\w-]+)([^\n]*)/', $this->input, $matches) &&
190
            isset($this->customKeywords[$matches[1]]) &&
191
            is_callable($this->customKeywords[$matches[1]])
192
        ) {
193
            $this->consume($matches[0]);
194
195
            $token = $this->token('customKeyword', $matches[1]);
196
            $token->args = trim($matches[2]);
197
198
            return $token;
199
        }
200
    }
201
202
    /**
203
     * @return object
204
     */
205
    protected function scanCode()
206
    {
207
        if (preg_match('/^(!?=|-)([^\n]+)/', $this->input, $matches)) {
208
            $this->consume($matches[0]);
209
            $flags = $matches[1];
210
            $code = $this->normalizeCode($matches[2]);
211
212
            $token = $this->token('code', $code);
213
            $token->escape = $flags[0] === '=';
214
            $token->buffer = '=' === $flags[0] || (isset($flags[1]) && '=' === $flags[1]);
215
216
            return $token;
217
        }
218
    }
219
220
    /**
221
     * @throws \ErrorException
222
     *
223
     * @return object
224
     */
225
    protected function scanAttributes()
226
    {
227
        if (substr($this->input, 0, 1) === '(') {
228
            // cant use ^ anchor in the regex because the pattern is recursive
229
            // but this restriction is asserted by the if above
230
            //$this->input = preg_replace('/([a-zA-Z0-9\'"\\]\\}\\)])([\t ]+[a-zA-Z])/', '$1,$2', $this->input);
231
            if (!preg_match('/\((?:"(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\'|[^()\'"]++|(?R))*+\)/', $this->input, $matches)) {
232
                throw new \ErrorException('Unable to find attributes closing parenthesis.', 21);
233
            }
234
            $this->consume($matches[0]);
235
236
            //$str = preg_replace('/()([a-zA-Z0-9_\\x7f-\\xff\\)\\]\\}"\'])(\s+[a-zA-Z_])/', '$1,$2', $str);
237
238
            $token = $this->token('attributes');
239
            $token->attributes = array();
240
            $token->escaped = array();
241
            $token->selfClosing = false;
242
243
            $parser = new Attributes($token);
244
            $parser->parseWith(substr($matches[0], 1, strlen($matches[0]) - 2));
245
246
            if ($this->length() && '/' === $this->input[0]) {
247
                $this->consume(1);
248
                $token->selfClosing = true;
249
            }
250
251
            return $token;
252
        }
253
    }
254
255
    /**
256
     * @return object
257
     */
258
    protected function scanPipelessText()
259
    {
260
        if ($this->pipeless && "\n" !== substr($this->input, 0, 1)) {
0 ignored issues
show
Bug introduced by
The property pipeless does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
261
            $pos = strpos($this->input, "\n");
262
263
            if ($pos === false) {
264
                $pos = $this->length();
265
            }
266
267
            $str = substr($this->input, 0, $pos); // do not include the \n char
268
269
            $this->consume($str);
270
271
            return $this->token('text', ltrim($str));
272
        }
273
    }
274
275
    /**
276
     * @return object
277
     */
278
    protected function scanColon()
279
    {
280
        return $this->scan('/^:(?!:) */', ':');
281
    }
282
283
    /**
284
     * @return object
285
     */
286
    protected function scanAndAttributes()
287
    {
288
        return $this->scan('/^&attributes(\(((?>[^()]+|(?1))*)\))/', '&attributes', 2);
289
    }
290
}
291