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 (#66)
by Tomasz
01:49
created

RegularParser::value()   C

Complexity

Conditions 12
Paths 10

Size

Total Lines 31

Duplication

Lines 3
Ratio 9.68 %

Code Coverage

Tests 20
CRAP Score 12

Importance

Changes 0
Metric Value
dl 3
loc 31
ccs 20
cts 20
cp 1
rs 6.9666
c 0
b 0
f 0
cc 12
nc 10
nop 0
crap 12

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
    const VALUE_REGULAR = 0x01;
33
    const VALUE_AGGRESSIVE = 0x02;
34
35
    public $valueMode = self::VALUE_REGULAR;
36
37 15
    public function __construct(SyntaxInterface $syntax = null)
38
    {
39 15
        $this->lexerRegex = $this->prepareLexer($syntax ?: new CommonSyntax());
40 15
        $this->nameRegex = '~^'.RegexBuilderUtility::buildNameRegex().'$~us';
41 15
    }
42
43
    /**
44
     * @param string $text
45
     *
46
     * @return ParsedShortcode[]
47
     */
48 59
    public function parse($text)
49
    {
50 59
        $nestingLevel = ini_set('xdebug.max_nesting_level', -1);
51 59
        $this->tokens = $this->tokenize($text);
52 59
        $this->backtracks = array();
53 59
        $this->lastBacktrack = 0;
54 59
        $this->position = 0;
55 59
        $this->tokensCount = \count($this->tokens);
56
57 59
        $shortcodes = array();
58 59
        while($this->position < $this->tokensCount) {
59 58
            while($this->position < $this->tokensCount && false === $this->lookahead(self::TOKEN_OPEN)) {
60 25
                $this->position++;
61 25
            }
62 58
            $names = array();
63 58
            $this->beginBacktrack();
64 58
            $matches = $this->shortcode($names);
65 58
            if(\is_array($matches)) {
66 50
                foreach($matches as $shortcode) {
67 50
                    $shortcodes[] = $shortcode;
68 50
                }
69 50
            }
70 58
        }
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 17
            if(!$this->match(self::TOKEN_CLOSE, false)) { return false; }
101
102 16
            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 29 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 25
                $content .= $this->match(null, true);
118 25
            }
119
120 29
            $this->beginBacktrack();
121 29
            $contentMatchedShortcodes = $this->shortcode($names);
122 29
            if(\is_string($contentMatchedShortcodes)) {
123 6
                $closingName = $contentMatchedShortcodes;
124 6
                break;
125
            }
126 29
            if(\is_array($contentMatchedShortcodes)) {
127 16
                foreach($contentMatchedShortcodes as $matchedShortcode) {
128 16
                    $shortcodes[] = $matchedShortcode;
129 16
                }
130 16
                continue;
131
            }
132 22
            $this->backtrack();
133
134 22
            $this->beginBacktrack();
135 22
            if(false !== ($closingName = $this->close($names))) {
136 19
                if(null === $content) { $content = ''; }
137 19
                $this->backtrack();
138 19
                $shortcodes = array();
139 19
                break;
140
            }
141 8
            $closingName = null;
142 8
            $this->backtrack();
143
144 8
            $content .= $this->match(null, false);
145 8
        }
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 26
            $this->backtrack(false);
158 26
            $text = $this->backtrack(false);
159
160 26
            return array_merge(array($this->getObject($name, $parameters, $bbCode, $offset, null, $text)), $shortcodes);
161
        }
162 19
        $content = $this->getBacktrack();
163 19
        if(!$this->close($names)) { return false; }
164
165 19
        return array($this->getObject($name, $parameters, $bbCode, $offset, $content, $this->getBacktrack()));
166
    }
167
168 22
    private function close(array &$names)
169
    {
170 22
        if(!$this->match(self::TOKEN_OPEN, true)) { return false; }
171 20
        if(!$this->match(self::TOKEN_MARKER, true)) { return false; }
172 20
        if(!$closingName = $this->match(self::TOKEN_STRING, true)) { return false; }
173 20
        if(!$this->match(self::TOKEN_CLOSE, false)) { return false; }
174
175 20
        return \in_array($closingName, $names, true) ? $closingName : false;
176
    }
177
178 53
    private function parameters()
179
    {
180 53
        $parameters = array();
181
182 53
        while(true) {
183 53
            $this->match(self::TOKEN_WS, false);
184 53
            if($this->lookahead(self::TOKEN_MARKER) || $this->lookahead(self::TOKEN_CLOSE)) { break; }
185 29
            if(!$name = $this->match(self::TOKEN_STRING, true)) { return false; }
186 28
            if(!$this->match(self::TOKEN_SEPARATOR, true)) { $parameters[$name] = null; continue; }
187 27
            if(false === ($value = $this->value())) { return false; }
188 26
            $this->match(self::TOKEN_WS, false);
189
190 26
            $parameters[$name] = $value;
191 26
        }
192
193 51
        return $parameters;
194
    }
195
196 29
    private function value()
197
    {
198 29
        $value = '';
199
200 29
        if($this->match(self::TOKEN_DELIMITER, false)) {
201 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...
202 19
                $value .= $this->match(null, false);
203 19
            }
204
205 19
            return $this->match(self::TOKEN_DELIMITER, false) ? $value : false;
206
        }
207
208 16
        if($this->lookahead(self::TOKEN_STRING) || $this->lookahead(self::TOKEN_MARKER)) {
209 15
            while(true) {
210 15
                if($this->lookahead(self::TOKEN_WS) || $this->lookahead(self::TOKEN_CLOSE)) {
211 15
                    break;
212
                }
213 15
                if($this->lookaheadN(array(self::TOKEN_MARKER, self::TOKEN_CLOSE))) {
214 3
                    if($this->valueMode === self::VALUE_AGGRESSIVE) {
215 1
                        $value .= $this->match(null, false);
216 1
                    }
217 3
                    break;
218
                }
219 15
                $value .= $this->match(null, false);
220 15
            }
221
222 15
            return $value;
223
        }
224
225 1
        return false;
226
    }
227
228
    /* --- PARSER ---------------------------------------------------------- */
229
230 58
    private function beginBacktrack()
231
    {
232 58
        $this->backtracks[] = $this->position;
233 58
        $this->lastBacktrack = $this->position;
234 58
    }
235
236 31
    private function getBacktrack()
237
    {
238 31
        $position = array_pop($this->backtracks);
239 31
        $backtrack = '';
240 31 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...
241 31
            $backtrack .= $this->tokens[$i][1];
242 31
        }
243
244 31
        return $backtrack;
245
    }
246
247 39
    private function backtrack($modifyPosition = true)
248
    {
249 39
        $position = array_pop($this->backtracks);
250 39
        if($modifyPosition) {
251 22
            $this->position = $position;
252 22
        }
253
254 39
        $backtrack = '';
255 39 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...
256 26
            $backtrack .= $this->tokens[$i][1];
257 26
        }
258 39
        $this->lastBacktrack = $position;
259
260 39
        return $backtrack;
261
    }
262
263 58
    private function lookahead($type)
264
    {
265 58
        return $this->position < $this->tokensCount && $this->tokens[$this->position][0] === $type;
266
    }
267
268 15
    private function lookaheadN(array $types)
269
    {
270 15
        $count = count($types);
271 15
        if($this->position + $count > $this->tokensCount) {
272
            return false;
273
        }
274
275 15
        $position = $this->position;
276 15
        foreach($types as $type) {
277
            // note: automatically skips whitespace tokens
278 15
            if($this->tokens[$position][0] === self::TOKEN_WS) {
279
                $position++;
280
            }
281 15
            if($type !== $this->tokens[$position][0]) {
282 15
                return false;
283
            }
284 3
            $position++;
285 3
        }
286
287 3
        return true;
288
    }
289
290 58
    private function match($type, $ws)
291
    {
292 58
        if($this->position >= $this->tokensCount) {
293 20
            return '';
294
        }
295
296 58
        $token = $this->tokens[$this->position];
297 58
        if(!empty($type) && $token[0] !== $type) {
298 58
            return '';
299
        }
300
301 58
        $this->position++;
302 58
        if($ws && $this->position < $this->tokensCount && $this->tokens[$this->position][0] === self::TOKEN_WS) {
303 17
            $this->position++;
304 17
        }
305
306 58
        return $token[1];
307
    }
308
309
    /* --- LEXER ----------------------------------------------------------- */
310
311 59
    private function tokenize($text)
312
    {
313 59
        $count = preg_match_all($this->lexerRegex, $text, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
314 59
        if(false === $count || preg_last_error() !== PREG_NO_ERROR) {
315
            throw new \RuntimeException(sprintf('PCRE failure `%s`.', preg_last_error()));
316
        }
317
318 59
        $tokens = array();
319 59
        $position = 0;
320
321 59
        foreach($matches as $match) {
322 58
            switch(true) {
323 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...
324 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...
325 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...
326 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...
327 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...
328 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...
329 56 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 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...
330
                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...
331
            }
332 58
            $tokens[] = array($type, $token, $position);
333 58
            $position += mb_strlen($token, 'utf-8');
334 59
        }
335
336 59
        return $tokens;
337
    }
338
339 15
    private function prepareLexer(SyntaxInterface $syntax)
340
    {
341
        $group = function($text, $group) {
342 15
            return '(?<'.$group.'>'.preg_replace('/(.)/us', '\\\\$0', $text).')';
343 15
        };
344 15
        $quote = function($text) {
345 15
            return preg_replace('/(.)/us', '\\\\$0', $text);
346 15
        };
347
348
        $rules = array(
349 15
            '(?<string>\\\\.|(?:(?!'.implode('|', array(
350 15
                $quote($syntax->getOpeningTag()),
351 15
                $quote($syntax->getClosingTag()),
352 15
                $quote($syntax->getClosingTagMarker()),
353 15
                $quote($syntax->getParameterValueSeparator()),
354 15
                $quote($syntax->getParameterValueDelimiter()),
355 15
                '\s+',
356 15
            )).').)+)',
357 15
            '(?<ws>\s+)',
358 15
            $group($syntax->getClosingTagMarker(), 'marker'),
359 15
            $group($syntax->getParameterValueDelimiter(), 'delimiter'),
360 15
            $group($syntax->getParameterValueSeparator(), 'separator'),
361 15
            $group($syntax->getOpeningTag(), 'open'),
362 15
            $group($syntax->getClosingTag(), 'close'),
363 15
        );
364
365 15
        return '~('.implode('|', $rules).')~us';
366
    }
367
}
368