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 ( 6b8bf5...4cda52 )
by Tomasz
01:56
created

RegularParser::content()   D

Complexity

Conditions 9
Paths 18

Size

Total Lines 43
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 9.0239

Importance

Changes 9
Bugs 2 Features 2
Metric Value
c 9
b 2
f 2
dl 0
loc 43
ccs 28
cts 30
cp 0.9333
rs 4.909
cc 9
eloc 30
nc 18
nop 1
crap 9.0239
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 39
    public function parse($text)
39
    {
40 39
        $this->tokens = $this->tokenize($text);
41 39
        $this->backtracks = array();
42 39
        $this->position = 0;
43 39
        $this->tokensCount = count($this->tokens);
44
45 39
        $shortcodes = array();
46 39
        while($this->position < $this->tokensCount) {
47 38
            while($this->position < $this->tokensCount && !$this->lookahead(self::TOKEN_OPEN)) {
48 15
                $this->position++;
49 15
            }
50 38
            $names = array();
51 38
            $this->beginBacktrack();
52 38
            foreach($this->shortcode($names) ?: array() as $shortcode) {
0 ignored issues
show
Bug introduced by
The expression $this->shortcode($names) ?: array() of type string|boolean|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
53 31
                $shortcodes[] = $shortcode;
54 38
            }
55 38
        }
56
57 39
        return $shortcodes;
58
    }
59
60 31
    private function getObject($name, $parameters, $bbCode, $offset, $content, $text)
61
    {
62 31
        return new ParsedShortcode(new Shortcode($name, $parameters, $content, $bbCode), $text, $offset);
63
    }
64
65
    /* --- RULES ----------------------------------------------------------- */
66
67 38
    private function shortcode(array &$names)
68
    {
69 38
        $name = null;
70 38
        $offset = null;
71
72
        $setName = function(array $token) use(&$name) { $name = $token[1]; };
73
        $setOffset = function(array $token) use(&$offset) { $offset = $token[2]; };
74
75 38
        if(!$this->match(self::TOKEN_OPEN, $setOffset, true)) { return false; }
76 38
        if(!$this->match(self::TOKEN_STRING, $setName, false)) { return false; }
77 35
        if($this->lookahead(self::TOKEN_STRING, null)) { return false; }
78 35
        if(!preg_match_all('/^[a-zA-Z0-9-]+$/', $name, $matches)) { return false; }
79 34
        $this->match(self::TOKEN_WS);
80 34
        if(false === ($bbCode = $this->bbCode())) { return false; }
81 34
        if(false === ($parameters = $this->parameters())) { return false; }
82
83
        // self-closing
84 32
        if($this->match(self::TOKEN_MARKER, null, true)) {
85 12
            if(!$this->match(self::TOKEN_CLOSE)) { return false; }
86
87 11
            return array($this->getObject($name, $parameters, $bbCode, $offset, null, $this->getBacktrack()));
88
        }
89
90
        // just-closed or with-content
91 22
        if(!$this->match(self::TOKEN_CLOSE)) { return false; }
92 22
        $this->beginBacktrack();
93 22
        $names[] = $name;
94 22
        list($content, $shortcodes, $closingName) = $this->content($names);
95 22
        if(null !== $closingName && $closingName !== $name) {
96 1
            array_pop($names);
97 1
            array_pop($this->backtracks);
98 1
            array_pop($this->backtracks);
99
100 1
            return $closingName;
101
        }
102 22
        if(false === $content) {
103 13
            $this->backtrack(false);
104 13
            $text = $this->backtrack(false);
105
106 13
            return array_merge(array($this->getObject($name, $parameters, $bbCode, $offset, null, $text)), $shortcodes);
107
        }
108 10
        $content = $this->getBacktrack();
109 10
        if(!$this->close($names)) { return false; }
110
111 10
        return array($this->getObject($name, $parameters, $bbCode, $offset, $content, $this->getBacktrack()));
112
    }
113
114 22
    private function content(array &$names)
115
    {
116 22
        $content = null;
117 22
        $shortcodes = array();
118 22
        $closingName = null;
119
        $appendContent = function(array $token) use(&$content) { $content .= $token[1]; };
120
121 22
        while($this->position < $this->tokensCount) {
122 14
            while($this->match(array(self::TOKEN_STRING, self::TOKEN_WS), $appendContent)) {
123 13
                continue;
124
            }
125
126 14
            $this->beginBacktrack();
127 14
            $matchedShortcodes = $this->shortcode($names);
128 14
            if(is_string($matchedShortcodes)) {
129 1
                $closingName = $matchedShortcodes;
130 1
                break;
131
            }
132 14
            if(false !== $matchedShortcodes) {
133 6
                $shortcodes = array_merge($shortcodes, $matchedShortcodes);
134 6
                continue;
135
            }
136 12
            $this->backtrack();
137
138 12
            $this->beginBacktrack();
139 12
            if(false !== ($closingName = $this->close($names))) {
140 10
                if(null === $content) { $content = ''; }
141 10
                $this->backtrack();
142 10
                $shortcodes = array();
143 10
                break;
144
            }
145 3
            $closingName = null;
146 3
            $this->backtrack();
147 3
            if($this->position < $this->tokensCount) {
148
                $shortcodes = array();
149
                break;
150
            }
151
152 3
            $this->match(null, $appendContent);
153 3
        }
154
155 22
        return array($this->position < $this->tokensCount ? $content : false, $shortcodes, $closingName);
156
    }
157
158 12
    private function close(array &$names)
159
    {
160 12
        $closingName = null;
161
        $setName = function(array $token) use(&$closingName) { $closingName = $token[1]; };
162
163 12
        if(!$this->match(self::TOKEN_OPEN, null, true)) { return false; }
164 10
        if(!$this->match(self::TOKEN_MARKER, null, true)) { return false; }
165 10
        if(!$this->match(self::TOKEN_STRING, $setName, true)) { return false; }
166 10
        if(!$this->match(self::TOKEN_CLOSE)) { return false; }
167
168 10
        return in_array($closingName, $names) ? $closingName : false;
169
    }
170
171 34
    private function bbCode()
172
    {
173 34
        return $this->match(self::TOKEN_SEPARATOR, null, true) ? $this->value() : null;
174
    }
175
176 34
    private function parameters()
177
    {
178 34
        $parameters = array();
179
        $setName = function(array $token) use(&$name) { $name = $token[1]; };
180
181 34
        while(true) {
182 34
            $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...
183
184 34
            $this->match(self::TOKEN_WS);
185 34
            if($this->lookahead(array(self::TOKEN_MARKER, self::TOKEN_CLOSE))) { break; }
186 22
            if(!$this->match(self::TOKEN_STRING, $setName, true)) { return false; }
187 21
            if(!$this->match(self::TOKEN_SEPARATOR, null, true)) { $parameters[$name] = null; continue; }
188 20
            if(false === ($value = $this->value())) { return false; }
189 19
            $this->match(self::TOKEN_WS);
190
191 19
            $parameters[$name] = $value;
192 19
        }
193
194 32
        return $parameters;
195
    }
196
197 21
    private function value()
198
    {
199 21
        $value = '';
200
        $appendValue = function(array $token) use(&$value) { $value .= $token[1]; };
201
202 21
        if($this->match(self::TOKEN_DELIMITER)) {
203 18
            while($this->position < $this->tokensCount && !$this->lookahead(self::TOKEN_DELIMITER)) {
204 18
                $this->match(null, $appendValue);
205 18
            }
206
207 18
            return $this->match(self::TOKEN_DELIMITER) ? $value : false;
208
        }
209
210 8
        return $this->match(self::TOKEN_STRING, $appendValue) ? $value : false;
211
    }
212
213
    /* --- PARSER ---------------------------------------------------------- */
214
215
    /**
216
     * This method is used only for debugging purposes. DO NOT DELETE.
217
     *
218
     * @return array
219
     */
220
    private function listBacktracks()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
221
    {
222
        return array_map(function(array $backtrack) {
223
            return implode('', array_column($backtrack, 1));
224
        }, $this->backtracks);
225
    }
226
227 38
    private function beginBacktrack()
228
    {
229 38
        $this->backtracks[] = array();
230 38
    }
231
232
    private function getBacktrack()
233
    {
234
        // switch from array_map() to array_column() when dropping support for PHP <5.5
235
        return implode('', array_map(function(array $token) { return $token[1]; }, array_pop($this->backtracks)));
236
    }
237
238 22
    private function backtrack($modifyPosition = true)
239
    {
240 22
        $tokens = array_pop($this->backtracks);
241 22
        $count = count($tokens);
242 22
        if($modifyPosition) {
243 12
            $this->position -= $count;
244 12
        }
245
246 22
        foreach($this->backtracks as &$backtrack) {
247
            // array_pop() in loop is much faster than array_slice() because
248
            // it operates directly on the passed array
249 22
            for($i = 0; $i < $count; $i++) {
250 14
                array_pop($backtrack);
251 14
            }
252 22
        }
253
254
        return implode('', array_map(function(array $token) { return $token[1]; }, $tokens));
255
    }
256
257 38
    private function lookahead($type, $callback = null)
258
    {
259 38
        if($this->position >= $this->tokensCount) {
260 1
            return false;
261
        }
262
263 38
        $type = (array)$type;
264 38
        $token = $this->tokens[$this->position];
265 38
        if(!empty($type) && !in_array($token[0], $type)) {
266 37
            return false;
267
        }
268
269
        /** @var $callback callable */
270 38
        $callback && $callback($token);
271
272 38
        return true;
273
    }
274
275 38
    private function match($type, $callbacks = null, $ws = false)
276
    {
277 38
        if($this->position >= $this->tokensCount) {
278 13
            return false;
279
        }
280
281 38
        $type = (array)$type;
282 38
        $token = $this->tokens[$this->position];
283 38
        if(!empty($type) && !in_array($token[0], $type)) {
284 38
            return false;
285
        }
286 38
        foreach($this->backtracks as &$backtrack) {
287 38
            $backtrack[] = $token;
288 38
        }
289
290 38
        $this->position++;
291 38
        foreach((array)$callbacks as $callback) {
292 38
            $callback($token);
293 38
        }
294
295 38
        $ws && $this->match(self::TOKEN_WS);
296
297 38
        return true;
298
    }
299
300
    /* --- LEXER ----------------------------------------------------------- */
301
302 39
    private function tokenize($text)
303
    {
304 39
        preg_match_all($this->lexerRegex, $text, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
305 39
        $tokens = array();
306 39
        $position = 0;
307 39
        $type = null;
308 39
        $token = null;
309 39
        foreach($matches as $match) {
310 38
            switch(true) {
311 38 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...
312 38 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...
313 37 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...
314 37 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...
315 37 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...
316 37 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...
317 37 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...
318
            }
319 38
            $tokens[] = array($type, $token, $position);
320 38
            $position += mb_strlen($token, 'utf-8');
321 39
        }
322
323 39
        return $tokens;
324
    }
325
326
    private function getTokenizerRegex(SyntaxInterface $syntax)
327
    {
328 4
        $quote = function($text, $group) {
329 4
            return '(?<'.$group.'>'.preg_replace('/(.)/us', '\\\\$0', $text).')';
330 4
        };
331
332
        $rules = array(
333 4
            $quote($syntax->getOpeningTag(), 'open'),
334 4
            $quote($syntax->getClosingTag(), 'close'),
335 4
            $quote($syntax->getClosingTagMarker(), 'marker'),
336 4
            $quote($syntax->getParameterValueSeparator(), 'separator'),
337 4
            $quote($syntax->getParameterValueDelimiter(), 'delimiter'),
338 4
            '(?<ws>\s+)',
339 4
            '(?<string>[\w-]+|\\\\.|.)',
340 4
        );
341
342 4
        return '~('.implode('|', $rules).')~us';
343
    }
344
}
345