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