PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / core / RegexHelperPreg.php

RegexHelperPreg.php in 404 Solution trunk, at includes/core/RegexHelperPreg.php

112 lines 3.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 if (!defined('ABSPATH')) {
5 exit;
6 }
7
8 /**
9 * preg-backed implementation of ABJ_404_Solution_RegexHelper.
10 *
11 * Use when the PHP mbstring extension is unavailable. Also used directly
12 * by callers that need PCRE regex semantics (\d, \w, lookarounds,
13 * delimiters) regardless of host mbstring availability - the regex
14 * methods auto-pick a delimiter and forward to preg_*.
15 */
16 class ABJ_404_Solution_RegexHelperPreg extends ABJ_404_Solution_RegexHelper {
17
18 /** @var self|null */
19 private static $instance = null;
20 /**
21 * Test seam: install or clear the cached singleton instance without
22 * private-field reflection. Pass null to reset between tests; pass a
23 * configured instance (or double) to install it (M105 singleton-reset seam).
24 *
25 * @param self|null $instance
26 * @return void
27 */
28 public static function setInstance($instance) {
29 self::$instance = $instance;
30 }
31
32
33 /**
34 * Candidate delimiter characters tried in order when wrapping a PCRE
35 * pattern. The first one that does not appear in the pattern wins.
36 *
37 * @var array<int, string>
38 */
39 private $delimiterChars = array('`', '^', '|', '~', '!', ';', ':', ',', '@', "'", '/');
40
41 public static function getInstance(): self {
42 if (self::$instance === null) {
43 self::$instance = new self();
44 }
45 return self::$instance;
46 }
47
48 /**
49 * @param array<int, string>|null $regs
50 * @return bool|int
51 */
52 public function regexMatch(string $pattern, string $string, ?array &$regs = null) {
53 $delimiterA = "{";
54 $delimiterB = "}";
55 if (strpos($pattern, "}") !== false) {
56 $delimiterA = $delimiterB = $this->findADelimiter($pattern);
57 }
58 $regs = $regs ?? [];
59 return preg_match($delimiterA . $pattern . $delimiterB, $string, $regs);
60 }
61
62 /**
63 * @param array<int, string>|null $regs
64 * @return bool|int
65 */
66 public function regexMatchi(string $pattern, string $string, ?array &$regs = null) {
67 $delimiterA = "{";
68 $delimiterB = "}";
69 if (strpos($pattern, "}") !== false) {
70 $delimiterA = $delimiterB = $this->findADelimiter($pattern);
71 }
72 $regs = $regs ?? [];
73 return preg_match($delimiterA . $pattern . $delimiterB . 'i', $string, $regs);
74 }
75
76 /** @return string|null */
77 public function regexReplace($pattern, $replacement, $string) {
78 $delimiterA = "{";
79 $delimiterB = "}";
80 if (strpos($pattern, "}") !== false) {
81 $delimiterA = $delimiterB = $this->findADelimiter($pattern);
82 }
83 $replacementDelimiter = $this->findADelimiter($replacement);
84 $replacement = preg_replace($replacementDelimiter . '\\\\' . $replacementDelimiter, '\$', $replacement) ?? $replacement;
85 return preg_replace($delimiterA . $pattern . $delimiterB, $replacement, $string);
86 }
87
88 /**
89 * Pick a delimiter character that does not occur in $pattern so the
90 * pattern can be wrapped as PCRE input without conflicting.
91 *
92 * @param string $pattern
93 * @return string
94 */
95 public function findADelimiter(string $pattern): string {
96 if ($pattern === '') {
97 return $this->delimiterChars[0];
98 }
99
100 foreach ($this->delimiterChars as $char) {
101 if ($char === '') { continue; }
102 $parts = explode($char, $pattern);
103 if (count($parts) === 1) {
104 return $char;
105 }
106 }
107
108 throw new Exception("I can't find a valid delimiter character to use for the regular expression: "
109 . esc_html($pattern));
110 }
111 }
112