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 ( 8f3a65...0386f1 )
by Tomasz
12s
created

RegularParser::getObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

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.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

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

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

Loading history...
296 58 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 be defined using a colon.

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.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

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

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

Loading history...
297 58 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 be defined using a colon.

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.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

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

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

Loading history...
298 58 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 be defined using a colon.

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.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

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

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

Loading history...
299 58 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 be defined using a colon.

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.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

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

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

Loading history...
300 58 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 be defined using a colon.

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.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

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

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

Loading history...
301 56 View Code Duplication
                case -1 !== $match['close'][1]: { $token = $match['close'][0]; $type = self::TOKEN_CLOSE; break; }
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...
Coding Style introduced by
case statements should be defined using a colon.

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.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

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

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

Loading history...
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...
302
                default: { throw new \RuntimeException(sprintf('Invalid token.')); }
0 ignored issues
show
Coding Style introduced by
DEFAULT statements must be defined using a colon

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...
303
            }
304 58
            $tokens[] = array($type, $token, $position);
305 58
            $position += mb_strlen($token, 'utf-8');
306
        }
307
308 59
        return $tokens;
309
    }
310
311 15
    private function prepareLexer(SyntaxInterface $syntax)
312
    {
313
        $group = function($text, $group) {
314 15
            return '(?<'.$group.'>'.preg_replace('/(.)/us', '\\\\$0', $text).')';
315 15
        };
316
        $quote = function($text) {
317 15
            return preg_replace('/(.)/us', '\\\\$0', $text);
318 15
        };
319
320
        $rules = array(
321 15
            '(?<string>\\\\.|(?:(?!'.implode('|', array(
322 15
                $quote($syntax->getOpeningTag()),
323 15
                $quote($syntax->getClosingTag()),
324 15
                $quote($syntax->getClosingTagMarker()),
325 15
                $quote($syntax->getParameterValueSeparator()),
326 15
                $quote($syntax->getParameterValueDelimiter()),
327 15
                '\s+',
328 15
            )).').)+)',
329 15
            '(?<ws>\s+)',
330 15
            $group($syntax->getClosingTagMarker(), 'marker'),
331 15
            $group($syntax->getParameterValueDelimiter(), 'delimiter'),
332 15
            $group($syntax->getParameterValueSeparator(), 'separator'),
333 15
            $group($syntax->getOpeningTag(), 'open'),
334 15
            $group($syntax->getClosingTag(), 'close'),
335
        );
336
337 15
        return '~('.implode('|', $rules).')~us';
338
    }
339
}
340