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
|
|
|
|