|
1
|
|
|
<?php |
|
2
|
|
|
namespace Thunder\Shortcode\Utility; |
|
3
|
|
|
|
|
4
|
|
|
use Thunder\Shortcode\Syntax\SyntaxInterface; |
|
5
|
|
|
|
|
6
|
|
|
/** |
|
7
|
|
|
* @author Tomasz Kowalczyk <[email protected]> |
|
8
|
|
|
*/ |
|
9
|
|
|
final class RegexBuilderUtility |
|
10
|
|
|
{ |
|
11
|
168 |
|
public static function buildNameRegex() |
|
12
|
|
|
{ |
|
13
|
168 |
|
return '[a-zA-Z0-9-_]+'; |
|
14
|
|
|
} |
|
15
|
|
|
|
|
16
|
60 |
|
public static function buildShortcodeRegex(SyntaxInterface $syntax) |
|
17
|
|
|
{ |
|
18
|
60 |
|
return '~('.self::createShortcodeRegexContent($syntax).')~us'; |
|
19
|
|
|
} |
|
20
|
|
|
|
|
21
|
60 |
|
public static function buildSingleShortcodeRegex(SyntaxInterface $syntax) |
|
22
|
|
|
{ |
|
23
|
60 |
|
return '~(\A'.self::createShortcodeRegexContent($syntax).'\Z)~us'; |
|
24
|
|
|
} |
|
25
|
|
|
|
|
26
|
60 |
|
public static function buildParametersRegex(SyntaxInterface $syntax) |
|
27
|
|
|
{ |
|
28
|
60 |
|
$equals = self::quote($syntax->getParameterValueSeparator()); |
|
29
|
60 |
|
$string = self::quote($syntax->getParameterValueDelimiter()); |
|
30
|
|
|
|
|
31
|
60 |
|
$space = '\s*'; |
|
32
|
|
|
// lookahead test for either space or end of string |
|
33
|
60 |
|
$empty = '(?=\s|$)'; |
|
34
|
|
|
// equals sign and alphanumeric value |
|
35
|
60 |
|
$simple = $space.$equals.$space.'[^\s]+'; |
|
36
|
|
|
// equals sign and value without unescaped string delimiters enclosed in them |
|
37
|
60 |
|
$complex = $space.$equals.$space.$string.'([^'.$string.'\\\\]*(?:\\\\.[^'.$string.'\\\\]*)*?)'.$string; |
|
38
|
|
|
|
|
39
|
60 |
|
return '~(?:\s*(\w+(?:'.$complex.'|'.$simple.'|'.$empty.')))~us'; |
|
40
|
|
|
} |
|
41
|
|
|
|
|
42
|
60 |
|
private static function createShortcodeRegexContent(SyntaxInterface $syntax) |
|
43
|
|
|
{ |
|
44
|
60 |
|
$open = self::quote($syntax->getOpeningTag()); |
|
45
|
60 |
|
$slash = self::quote($syntax->getClosingTagMarker()); |
|
46
|
60 |
|
$close = self::quote($syntax->getClosingTag()); |
|
47
|
60 |
|
$equals = self::quote($syntax->getParameterValueSeparator()); |
|
48
|
60 |
|
$string = self::quote($syntax->getParameterValueDelimiter()); |
|
49
|
|
|
|
|
50
|
60 |
|
$space = '\s*'; |
|
51
|
|
|
|
|
52
|
|
|
// parameter and value separator can have any number of spaces around itself |
|
53
|
60 |
|
$equalsSpaced = $space.$equals.$space; |
|
54
|
|
|
// lookahead test for space, closing tag, self-closing tag or end of string |
|
55
|
60 |
|
$empty = '(?=\s|'.$close.'|'.$slash.$space.$close.'|$)'; |
|
56
|
|
|
// equals sign and alphanumeric value |
|
57
|
60 |
|
$simple = '(?!=(?:\s*|'.$close.'|'.$slash.$close.'))'; |
|
58
|
|
|
// equals sign and value without unescaped string delimiters enclosed in them |
|
59
|
60 |
|
$complex = $string.'(?:[^'.$string.'\\\\]*(?:\\\\.[^'.$string.'\\\\]*)*)'.$string; |
|
60
|
|
|
// complete parameters matching regex |
|
61
|
60 |
|
$parameters = '(?<parameters>(?:\s*(?:\w+(?:'.$equalsSpaced.$complex.'|'.$equalsSpaced.$simple.'|'.$empty.')))*)'; |
|
62
|
|
|
// BBCode is the part after name that makes it behave like a non-empty parameter value |
|
63
|
60 |
|
$bbCode = '(?:'.$equals.$space.'(?<bbCode>'.$complex.'|'.$simple.'))?'; |
|
64
|
|
|
|
|
65
|
|
|
// alphanumeric characters and dash |
|
66
|
60 |
|
$name = '(?<name>'.static::buildNameRegex().')'; |
|
67
|
|
|
// non-greedy match for any characters |
|
68
|
60 |
|
$content = '(?<content>.*?)'; |
|
69
|
|
|
|
|
70
|
|
|
// equal beginning for each variant: open tag, name and parameters |
|
71
|
60 |
|
$common = $open.$space.$name.$space.$bbCode.$space.$parameters.$space; |
|
72
|
|
|
// closing tag variants: just closing tag, self closing tag or content |
|
73
|
|
|
// and closing block with backreference name validation |
|
74
|
60 |
|
$justClosed = $close; |
|
75
|
60 |
|
$selfClosed = '(?<marker>'.$slash.')'.$space.$close; |
|
76
|
60 |
|
$withContent = $close.$content.$open.$space.'(?<markerContent>'.$slash.')'.$space.'(\k<name>)'.$space.$close; |
|
77
|
|
|
|
|
78
|
60 |
|
return '(?:'.$common.'(?:'.$withContent.'|'.$justClosed.'|'.$selfClosed.'))'; |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
60 |
|
private static function quote($text) |
|
82
|
|
|
{ |
|
83
|
60 |
|
return preg_replace('/(.)/us', '\\\\$0', $text); |
|
84
|
|
|
} |
|
85
|
|
|
} |
|
86
|
|
|
|