GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( e8c6fd...b08971 )
by Tomasz
06:48
created

RegularParser::tokenize()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 22
Code Lines 16

Duplication

Lines 6
Ratio 27.27 %

Code Coverage

Tests 15
CRAP Score 8

Importance

Changes 6
Bugs 1 Features 0
Metric Value
c 6
b 1
f 0
dl 6
loc 22
ccs 15
cts 15
cp 1
rs 6.6037
cc 8
eloc 16
nc 8
nop 1
crap 8
1
<?php
2
namespace Thunder\Shortcode\Parser;
3
4
use Thunder\Shortcode\Shortcode\ParsedShortcode;
5
use Thunder\Shortcode\Shortcode\Shortcode;
6
use Thunder\Shortcode\Syntax\CommonSyntax;
7
use Thunder\Shortcode\Syntax\SyntaxInterface;
8
9
/**
10
 * @author Tomasz Kowalczyk <[email protected]>
11
 */
12
final class RegularParser implements ParserInterface
13
{
14
    private $lexerRegex;
15
    private $tokens;
16
    private $tokensCount;
17
    private $position;
18
    private $backtracks;
19
20
    const TOKEN_OPEN = 1;
21
    const TOKEN_CLOSE = 2;
22
    const TOKEN_MARKER = 3;
23
    const TOKEN_SEPARATOR = 4;
24
    const TOKEN_DELIMITER = 5;
25
    const TOKEN_STRING = 6;
26
    const TOKEN_WS = 7;
27
28 4
    public function __construct(SyntaxInterface $syntax = null)
29
    {
30 4
        $this->lexerRegex = $this->getTokenizerRegex($syntax ?: new CommonSyntax());
31 4
    }
32
33
    /**
34
     * @param string $text
35
     *
36
     * @return ParsedShortcode[]
37
     */
38 40
    public function parse($text)
39
    {
40 40
        $this->tokens = $this->tokenize($text);
41 40
        $this->backtracks = array();
42 40
        $this->position = 0;
43 40
        $this->tokensCount = count($this->tokens);
44
45 40
        $shortcodes = array();
46 40
        while($this->position < $this->tokensCount) {
47 39
            while($this->position < $this->tokensCount && !$this->lookahead(self::TOKEN_OPEN)) {
48 16
                $this->position++;
49
            }
50 39
            $names = array();
51 39
            $this->beginBacktrack();
52 39
            $matches = $this->shortcode($names);
53 39
            if(is_array($matches)) {
54 32
                foreach($matches as $shortcode) {
55 32
                    $shortcodes[] = $shortcode;
56
                }
57
            }
58
        }
59
60 40
        return $shortcodes;
61
    }
62
63 32
    private function getObject($name, $parameters, $bbCode, $offset, $content, $text)
64
    {
65 32
        return new ParsedShortcode(new Shortcode($name, $parameters, $content, $bbCode), $text, $offset);
66
    }
67
68
    /* --- RULES ----------------------------------------------------------- */
69
70 39
    private function shortcode(array &$names)
71
    {
72 39
        $name = null;
73 39
        $offset = null;
74
75
        $setName = function(array $token) use(&$name) { $name = $token[1]; };
76
        $setOffset = function(array $token) use(&$offset) { $offset = $token[2]; };
77
78 39
        if(!$this->match(self::TOKEN_OPEN, $setOffset, true)) { return false; }
79 39
        if(!$this->match(self::TOKEN_STRING, $setName, false)) { return false; }
80 36
        if($this->lookahead(self::TOKEN_STRING, null)) { return false; }
81 36
        if(!preg_match_all('/^[a-zA-Z0-9-]+$/', $name, $matches)) { return false; }
82 35
        $this->match(self::TOKEN_WS);
83 35
        if(false === ($bbCode = $this->bbCode())) { return false; }
84 35
        if(false === ($parameters = $this->parameters())) { return false; }
85
86
        // self-closing
87 33
        if($this->match(self::TOKEN_MARKER, null, true)) {
88 12
            if(!$this->match(self::TOKEN_CLOSE)) { return false; }
89
90 11
            return array($this->getObject($name, $parameters, $bbCode, $offset, null, $this->getBacktrack()));
91
        }
92
93
        // just-closed or with-content
94 23
        if(!$this->match(self::TOKEN_CLOSE)) { return false; }
95 23
        $this->beginBacktrack();
96 23
        $names[] = $name;
97 23
        list($content, $shortcodes, $closingName) = $this->content($names);
98 23
        if(null !== $closingName && $closingName !== $name) {
99 1
            array_pop($names);
100 1
            array_pop($this->backtracks);
101 1
            array_pop($this->backtracks);
102
103 1
            return $closingName;
104
        }
105 23
        if(false === $content || $closingName !== $name) {
106 14
            $this->backtrack(false);
107 14
            $text = $this->backtrack(false);
108
109 14
            return array_merge(array($this->getObject($name, $parameters, $bbCode, $offset, null, $text)), $shortcodes);
110
        }
111 10
        $content = $this->getBacktrack();
112 10
        if(!$this->close($names)) { return false; }
113
114 10
        return array($this->getObject($name, $parameters, $bbCode, $offset, $content, $this->getBacktrack()));
115
    }
116
117 23
    private function content(array &$names)
118
    {
119 23
        $content = null;
120 23
        $shortcodes = array();
121 23
        $closingName = null;
122
        $appendContent = function(array $token) use(&$content) { $content .= $token[1]; };
123
124 23
        while($this->position < $this->tokensCount) {
125 15
            while($this->match(array(self::TOKEN_STRING, self::TOKEN_WS), $appendContent)) {
126 14
                continue;
127
            }
128
129 15
            $this->beginBacktrack();
130 15
            $matchedShortcodes = $this->shortcode($names);
131 15
            if(is_string($matchedShortcodes)) {
132 1
                $closingName = $matchedShortcodes;
133 1
                break;
134
            }
135 15
            if(false !== $matchedShortcodes) {
136 6
                $shortcodes = array_merge($shortcodes, $matchedShortcodes);
137 6
                continue;
138
            }
139 13
            $this->backtrack();
140
141 13
            $this->beginBacktrack();
142 13
            if(false !== ($closingName = $this->close($names))) {
143 10
                if(null === $content) { $content = ''; }
144 10
                $this->backtrack();
145 10
                $shortcodes = array();
146 10
                break;
147
            }
148 4
            $closingName = null;
149 4
            $this->backtrack();
150 4
            if($this->position < $this->tokensCount) {
151 1
                $shortcodes = array();
152 1
                break;
153
            }
154
155 3
            $this->match(null, $appendContent);
156
        }
157
158 23
        return array($this->position < $this->tokensCount ? $content : false, $shortcodes, $closingName);
159
    }
160
161 13
    private function close(array &$names)
162
    {
163 13
        $closingName = null;
164
        $setName = function(array $token) use(&$closingName) { $closingName = $token[1]; };
165
166 13
        if(!$this->match(self::TOKEN_OPEN, null, true)) { return false; }
167 11
        if(!$this->match(self::TOKEN_MARKER, null, true)) { return false; }
168 11
        if(!$this->match(self::TOKEN_STRING, $setName, true)) { return false; }
169 11
        if(!$this->match(self::TOKEN_CLOSE)) { return false; }
170
171 11
        return in_array($closingName, $names) ? $closingName : false;
172
    }
173
174 35
    private function bbCode()
175
    {
176 35
        return $this->match(self::TOKEN_SEPARATOR, null, true) ? $this->value() : null;
177
    }
178
179 35
    private function parameters()
180
    {
181 35
        $parameters = array();
182
        $setName = function(array $token) use(&$name) { $name = $token[1]; };
183
184 35
        while(true) {
185 35
            $name = null;
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $name, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
186
187 35
            $this->match(self::TOKEN_WS);
188 35
            if($this->lookahead(array(self::TOKEN_MARKER, self::TOKEN_CLOSE))) { break; }
189 22
            if(!$this->match(self::TOKEN_STRING, $setName, true)) { return false; }
190 21
            if(!$this->match(self::TOKEN_SEPARATOR, null, true)) { $parameters[$name] = null; continue; }
191 20
            if(false === ($value = $this->value())) { return false; }
192 19
            $this->match(self::TOKEN_WS);
193
194 19
            $parameters[$name] = $value;
195
        }
196
197 33
        return $parameters;
198
    }
199
200 21
    private function value()
201
    {
202 21
        $value = '';
203
        $appendValue = function(array $token) use(&$value) { $value .= $token[1]; };
204
205 21
        if($this->match(self::TOKEN_DELIMITER)) {
206 18
            while($this->position < $this->tokensCount && !$this->lookahead(self::TOKEN_DELIMITER)) {
207 18
                $this->match(null, $appendValue);
208
            }
209
210 18
            return $this->match(self::TOKEN_DELIMITER) ? $value : false;
211
        }
212
213 8
        return $this->match(self::TOKEN_STRING, $appendValue) ? $value : false;
214
    }
215
216
    /* --- PARSER ---------------------------------------------------------- */
217
218 39
    private function beginBacktrack()
219
    {
220 39
        $this->backtracks[] = array();
221 39
    }
222
223
    private function getBacktrack()
224
    {
225
        // switch from array_map() to array_column() when dropping support for PHP <5.5
226
        return implode('', array_map(function(array $token) { return $token[1]; }, array_pop($this->backtracks)));
227
    }
228
229 23
    private function backtrack($modifyPosition = true)
230
    {
231 23
        $tokens = array_pop($this->backtracks);
232 23
        $count = count($tokens);
233 23
        if($modifyPosition) {
234 13
            $this->position -= $count;
235
        }
236
237 23
        foreach($this->backtracks as &$backtrack) {
238
            // array_pop() in loop is much faster than array_slice() because
239
            // it operates directly on the passed array
240 23
            for($i = 0; $i < $count; $i++) {
241 15
                array_pop($backtrack);
242
            }
243
        }
244
245
        return implode('', array_map(function(array $token) { return $token[1]; }, $tokens));
246
    }
247
248 39
    private function lookahead($type, $callback = null)
249
    {
250 39
        if($this->position >= $this->tokensCount) {
251 1
            return false;
252
        }
253
254 39
        $type = (array)$type;
255 39
        $token = $this->tokens[$this->position];
256 39
        if(!empty($type) && !in_array($token[0], $type)) {
257 38
            return false;
258
        }
259
260
        /** @var $callback callable */
261 39
        $callback && $callback($token);
262
263 39
        return true;
264
    }
265
266 39
    private function match($type, $callbacks = null, $ws = false)
267
    {
268 39
        if($this->position >= $this->tokensCount) {
269 14
            return false;
270
        }
271
272 39
        $type = (array)$type;
273 39
        $token = $this->tokens[$this->position];
274 39
        if(!empty($type) && !in_array($token[0], $type)) {
275 39
            return false;
276
        }
277 39
        foreach($this->backtracks as &$backtrack) {
278 39
            $backtrack[] = $token;
279
        }
280
281 39
        $this->position++;
282 39
        foreach((array)$callbacks as $callback) {
283 39
            $callback($token);
284
        }
285
286 39
        $ws && $this->match(self::TOKEN_WS);
287
288 39
        return true;
289
    }
290
291
    /* --- LEXER ----------------------------------------------------------- */
292
293 40
    private function tokenize($text)
294
    {
295 40
        preg_match_all($this->lexerRegex, $text, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
296 40
        $tokens = array();
297 40
        $position = 0;
298
299 40
        foreach($matches as $match) {
300
            switch(true) {
301 39 View Code Duplication
                case -1 !== $match['open'][1]: { $token = $match['open'][0]; $type = self::TOKEN_OPEN; break; }
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of -1 (integer) and $match['open'][1] (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style introduced by
case statements should not use curly braces.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
302 39 View Code Duplication
                case -1 !== $match['close'][1]: { $token = $match['close'][0]; $type = self::TOKEN_CLOSE; break; }
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of -1 (integer) and $match['close'][1] (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style introduced by
case statements should not use curly braces.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
303 38 View Code Duplication
                case -1 !== $match['marker'][1]: { $token = $match['marker'][0]; $type = self::TOKEN_MARKER; break; }
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of -1 (integer) and $match['marker'][1] (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style introduced by
case statements should not use curly braces.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
304 38 View Code Duplication
                case -1 !== $match['separator'][1]: { $token = $match['separator'][0]; $type = self::TOKEN_SEPARATOR; break; }
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of -1 (integer) and $match['separator'][1] (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style introduced by
case statements should not use curly braces.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
305 38 View Code Duplication
                case -1 !== $match['delimiter'][1]: { $token = $match['delimiter'][0]; $type = self::TOKEN_DELIMITER; break; }
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of -1 (integer) and $match['delimiter'][1] (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style introduced by
case statements should not use curly braces.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
306 38 View Code Duplication
                case -1 !== $match['ws'][1]: { $token = $match['ws'][0]; $type = self::TOKEN_WS; break; }
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of -1 (integer) and $match['ws'][1] (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style introduced by
case statements should not use curly braces.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
307 38
                default: { $token = $match['string'][0]; $type = self::TOKEN_STRING; }
0 ignored issues
show
Coding Style introduced by
As per PSR2, default statements should not use curly braces.

As per the PSR-2 coding standard, default statements should not be wrapped in curly braces.

switch ($expr) {
    default: { //wrong
        doSomething();
        break;
    }
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
308
            }
309 39
            $tokens[] = array($type, $token, $position);
310 39
            $position += mb_strlen($token, 'utf-8');
311
        }
312
313 40
        return $tokens;
314
    }
315
316
    private function getTokenizerRegex(SyntaxInterface $syntax)
317
    {
318 4
        $quote = function($text, $group) {
319 4
            return '(?<'.$group.'>'.preg_replace('/(.)/us', '\\\\$0', $text).')';
320 4
        };
321
322
        $rules = array(
323 4
            $quote($syntax->getOpeningTag(), 'open'),
324 4
            $quote($syntax->getClosingTag(), 'close'),
325 4
            $quote($syntax->getClosingTagMarker(), 'marker'),
326 4
            $quote($syntax->getParameterValueSeparator(), 'separator'),
327 4
            $quote($syntax->getParameterValueDelimiter(), 'delimiter'),
328 4
            '(?<ws>\s+)',
329 4
            '(?<string>[\w-]+|\\\\.|.)',
330
        );
331
332 4
        return '~('.implode('|', $rules).')~us';
333
    }
334
}
335