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
Pull Request — master (#72)
by Tomasz
01:38
created

RegularParser::prepareLexer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 12
cts 12
cp 1
rs 9.7
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
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
use Thunder\Shortcode\Utility\RegexBuilderUtility;
9
10
/**
11
 * @author Tomasz Kowalczyk <[email protected]>
12
 */
13
final class RegularParser implements ParserInterface
14
{
15
    private $lexerRegex;
16
    private $nameRegex;
17
    private $tokens;
18
    private $tokensCount;
19
    private $position;
20
    /** @var int[] */
21
    private $backtracks;
22
    private $lastBacktrack;
23
    private $tokenMap;
24
25
    const TOKEN_OPEN = 1;
26
    const TOKEN_CLOSE = 2;
27
    const TOKEN_MARKER = 3;
28
    const TOKEN_SEPARATOR = 4;
29
    const TOKEN_DELIMITER = 5;
30
    const TOKEN_STRING = 6;
31
    const TOKEN_WS = 7;
32
33 14
    public function __construct(SyntaxInterface $syntax = null)
34
    {
35 14
        $this->lexerRegex = $this->prepareLexer($syntax ?: new CommonSyntax());
36 14
        $this->nameRegex = '~^'.RegexBuilderUtility::buildNameRegex().'$~us';
37 14
    }
38
39
    /**
40
     * @param string $text
41
     *
42
     * @return ParsedShortcode[]
43
     */
44 57
    public function parse($text)
45
    {
46 57
        $nestingLevel = ini_set('xdebug.max_nesting_level', -1);
47 57
        $this->tokens = $this->tokenize($text);
48 57
        $this->backtracks = array();
49 57
        $this->lastBacktrack = 0;
50 57
        $this->position = 0;
51 57
        $this->tokensCount = \count($this->tokens);
52
53 57
        $shortcodes = array();
54 57
        while($this->position < $this->tokensCount) {
55 56
            while($this->position < $this->tokensCount && false === $this->lookahead(self::TOKEN_OPEN)) {
56 24
                $this->position++;
57 24
            }
58 56
            $names = array();
59 56
            $this->beginBacktrack();
60 56
            $matches = $this->shortcode($names);
61 56
            if(\is_array($matches)) {
62 48
                foreach($matches as $shortcode) {
63 48
                    $shortcodes[] = $shortcode;
64 48
                }
65 48
            }
66 56
        }
67 57
        ini_set('xdebug.max_nesting_level', $nestingLevel);
68
69 57
        return $shortcodes;
70
    }
71
72 48
    private function getObject($name, $parameters, $bbCode, $offset, $content, $text)
73
    {
74 48
        return new ParsedShortcode(new Shortcode($name, $parameters, $content, $bbCode), $text, $offset);
75
    }
76
77
    /* --- RULES ----------------------------------------------------------- */
78
79 56
    private function shortcode(array &$names)
80
    {
81 56
        if(!$this->match(self::TOKEN_OPEN, false)) { return false; }
82 56
        $offset = $this->tokens[$this->position - 1][2];
83 56
        $this->match(self::TOKEN_WS, false);
84 56
        if('' === $name = $this->match(self::TOKEN_STRING, false)) { return false; }
85 53
        if($this->lookahead(self::TOKEN_STRING)) { return false; }
86 53
        if(1 !== preg_match($this->nameRegex, $name, $matches)) { return false; }
87 52
        $this->match(self::TOKEN_WS, false);
88
        // bbCode
89 52
        $bbCode = $this->match(self::TOKEN_SEPARATOR, true) ? $this->value() : null;
90 52
        if(false === $bbCode) { return false; }
91
        // parameters
92 51
        if(false === ($parameters = $this->parameters())) { return false; }
93
94
        // self-closing
95 49
        if($this->match(self::TOKEN_MARKER, true)) {
96 16
            if(!$this->match(self::TOKEN_CLOSE, false)) { return false; }
97
98 15
            return array($this->getObject($name, $parameters, $bbCode, $offset, null, $this->getBacktrack()));
99
        }
100
101
        // just-closed or with-content
102 37
        if(!$this->match(self::TOKEN_CLOSE, false)) { return false; }
103 37
        $this->beginBacktrack();
104 37
        $names[] = $name;
105
106
        // begin inlined content()
107 37
        $content = '';
108 37
        $shortcodes = array();
109 37
        $closingName = null;
110
111 37
        while($this->position < $this->tokensCount) {
112 28 View Code Duplication
            while($this->position < $this->tokensCount && false === $this->lookahead(self::TOKEN_OPEN)) {
0 ignored issues
show
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...
113 24
                $content .= $this->match(null, true);
114 24
            }
115
116 28
            $this->beginBacktrack();
117 28
            $contentMatchedShortcodes = $this->shortcode($names);
118 28
            if(\is_string($contentMatchedShortcodes)) {
119 6
                $closingName = $contentMatchedShortcodes;
120 6
                break;
121
            }
122 28
            if(\is_array($contentMatchedShortcodes)) {
123 15
                foreach($contentMatchedShortcodes as $matchedShortcode) {
124 15
                    $shortcodes[] = $matchedShortcode;
125 15
                }
126 15
                continue;
127
            }
128 22
            $this->backtrack();
129
130 22
            $this->beginBacktrack();
131 22
            if(false !== ($closingName = $this->close($names))) {
132 19
                if(null === $content) { $content = ''; }
133 19
                $this->backtrack();
134 19
                $shortcodes = array();
135 19
                break;
136
            }
137 8
            $closingName = null;
138 8
            $this->backtrack();
139
140 8
            $content .= $this->match(null, false);
141 8
        }
142 37
        $content = $this->position < $this->tokensCount ? $content : false;
143
        // end inlined content()
144
145 37
        if(null !== $closingName && $closingName !== $name) {
146 6
            array_pop($names);
147 6
            array_pop($this->backtracks);
148 6
            array_pop($this->backtracks);
149
150 6
            return $closingName;
151
        }
152 37
        if(false === $content || $closingName !== $name) {
153 24
            $this->backtrack(false);
154 24
            $text = $this->backtrack(false);
155
156 24
            return array_merge(array($this->getObject($name, $parameters, $bbCode, $offset, null, $text)), $shortcodes);
157
        }
158 19
        $content = $this->getBacktrack();
159 19
        if(!$this->close($names)) { return false; }
160
161 19
        return array($this->getObject($name, $parameters, $bbCode, $offset, $content, $this->getBacktrack()));
162
    }
163
164 22
    private function close(array &$names)
165
    {
166 22
        if(!$this->match(self::TOKEN_OPEN, true)) { return false; }
167 20
        if(!$this->match(self::TOKEN_MARKER, true)) { return false; }
168 20
        if(!$closingName = $this->match(self::TOKEN_STRING, true)) { return false; }
169 20
        if(!$this->match(self::TOKEN_CLOSE, false)) { return false; }
170
171 20
        return \in_array($closingName, $names, true) ? $closingName : false;
172
    }
173
174 51
    private function parameters()
175
    {
176 51
        $parameters = array();
177
178 51
        while(true) {
179 51
            $this->match(self::TOKEN_WS, false);
180 51
            if($this->lookahead(self::TOKEN_MARKER) || $this->lookahead(self::TOKEN_CLOSE)) { break; }
181 27
            if(!$name = $this->match(self::TOKEN_STRING, true)) { return false; }
182 26
            if(!$this->match(self::TOKEN_SEPARATOR, true)) { $parameters[$name] = null; continue; }
183 25
            if(false === ($value = $this->value())) { return false; }
184 24
            $this->match(self::TOKEN_WS, false);
185
186 24
            $parameters[$name] = $value;
187 24
        }
188
189 49
        return $parameters;
190
    }
191
192 27
    private function value()
193
    {
194 27
        $value = '';
195
196 27
        if($this->match(self::TOKEN_DELIMITER, false)) {
197 19 View Code Duplication
            while($this->position < $this->tokensCount && false === $this->lookahead(self::TOKEN_DELIMITER)) {
0 ignored issues
show
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...
198 19
                $value .= $this->match(null, false);
199 19
            }
200
201 19
            return $this->match(self::TOKEN_DELIMITER, false) ? $value : false;
202
        }
203
204 14
        if($tmp = $this->match(self::TOKEN_STRING, false)) {
205 13
            $value .= $tmp;
206 13
            while($tmp = $this->match(self::TOKEN_STRING, false)) {
207 2
                $value .= $tmp;
208 2
            }
209
210 13
            return $value;
211
        }
212
213 1
        return false;
214
    }
215
216
    /* --- PARSER ---------------------------------------------------------- */
217
218 56
    private function beginBacktrack()
219
    {
220 56
        $this->backtracks[] = $this->position;
221 56
        $this->lastBacktrack = $this->position;
222 56
    }
223
224 30
    private function getBacktrack()
225
    {
226 30
        $position = array_pop($this->backtracks);
227 30
        $backtrack = '';
228 30 View Code Duplication
        for($i = $position; $i < $this->position; $i++) {
0 ignored issues
show
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...
229 30
            $backtrack .= $this->tokens[$i][1];
230 30
        }
231
232 30
        return $backtrack;
233
    }
234
235 37
    private function backtrack($modifyPosition = true)
236
    {
237 37
        $position = array_pop($this->backtracks);
238 37
        if($modifyPosition) {
239 22
            $this->position = $position;
240 22
        }
241
242 37
        $backtrack = '';
243 37 View Code Duplication
        for($i = $position; $i < $this->lastBacktrack; $i++) {
0 ignored issues
show
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...
244 24
            $backtrack .= $this->tokens[$i][1];
245 24
        }
246 37
        $this->lastBacktrack = $position;
247
248 37
        return $backtrack;
249
    }
250
251 56
    private function lookahead($type)
252
    {
253 56
        return $this->position < $this->tokensCount && $this->tokens[$this->position][0] === $type;
254
    }
255
256 56
    private function match($type, $ws)
257
    {
258 56
        if($this->position >= $this->tokensCount) {
259 20
            return '';
260
        }
261
262 56
        $token = $this->tokens[$this->position];
263 56
        if(!empty($type) && $token[0] !== $type) {
264 56
            return '';
265
        }
266
267 56
        $this->position++;
268 56
        if($ws && $this->position < $this->tokensCount && $this->tokens[$this->position][0] === self::TOKEN_WS) {
269 17
            $this->position++;
270 17
        }
271
272 56
        return $token[1];
273
    }
274
275
    /* --- LEXER ----------------------------------------------------------- */
276
277 57
    private function tokenize($text)
278
    {
279 57
        preg_match_all($this->lexerRegex, $text, $matches, PREG_OFFSET_CAPTURE);
280 57
        if(preg_last_error() !== PREG_NO_ERROR) {
281
            throw new \RuntimeException(sprintf('PCRE failure `%s`.', preg_last_error()));
282
        }
283
284 57
        $tokens = array();
285 57
        $position = 0;
286 57
        foreach($matches[0] as $match) {
287 56
            $type = isset($this->tokenMap[$match[0]])
288 56
                ? $this->tokenMap[$match[0]]
289 56
                : (ctype_space($match[0]) ? self::TOKEN_WS : self::TOKEN_STRING);
290 56
            $tokens[] = array($type, $match[0], $position);
291 56
            $position += mb_strlen($match[0], 'utf-8');
292 57
        }
293
294 57
        return $tokens;
295
    }
296
297 14
    private function prepareLexer(SyntaxInterface $syntax)
298
    {
299 14
        $this->tokenMap = array(
300 14
            $syntax->getOpeningTag() => self::TOKEN_OPEN,
301 14
            $syntax->getClosingTag() => self::TOKEN_CLOSE,
302 14
            $syntax->getClosingTagMarker() => self::TOKEN_MARKER,
303 14
            $syntax->getParameterValueSeparator() => self::TOKEN_SEPARATOR,
304 14
            $syntax->getParameterValueDelimiter() => self::TOKEN_DELIMITER,
305
        );
306
307 14
        $quote = function($text) {
308 14
            return preg_replace('/(.)/us', '\\\\$0', $text);
309 14
        };
310 14
        $symbols = array_map($quote, array_keys($this->tokenMap));
311
312 14
        return '~('.implode('|', $symbols).'|\s+|\\\\.|[\w-]+|.)~us';
313
    }
314
}
315