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

RegularParser   F

Complexity

Total Complexity 83

Size/Duplication

Total Lines 322
Duplicated Lines 5.9 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 97.49%

Importance

Changes 0
Metric Value
wmc 83
lcom 1
cbo 5
dl 19
loc 322
ccs 194
cts 199
cp 0.9749
rs 2
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 2
A getObject() 0 4 1
A close() 0 9 6
B parse() 0 27 6
F shortcode() 3 84 25
B parameters() 0 17 7
B value() 3 25 9
A beginBacktrack() 0 5 1
A getBacktrack() 3 10 2
A backtrack() 3 15 3
A lookahead() 0 4 2
B match() 0 18 7
B tokenize() 7 27 11
A prepareLexer() 0 28 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like RegularParser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RegularParser, and based on these observations, apply Extract Interface, too.

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