| 1 |
<?php |
| 2 |
|
| 3 |
|
| 4 |
if (!defined('ABSPATH')) { |
| 5 |
exit; |
| 6 |
} |
| 7 |
|
| 8 |
/** |
| 9 |
* Polymorphic regex service. |
| 10 |
* |
| 11 |
* Owns the three regex primitives the plugin needs across hosts with and |
| 12 |
* without the PHP mbstring extension, plus the urlLooksLikeRegex URL |
| 13 |
* classifier. Two concrete implementations live alongside this base class: |
| 14 |
* |
| 15 |
* - ABJ_404_Solution_RegexHelperMb - uses mb_ereg / mb_eregi / mb_ereg_replace |
| 16 |
* - ABJ_404_Solution_RegexHelperPreg - uses native preg_match / preg_replace |
| 17 |
* |
| 18 |
* Choose Mb when the mbstring extension is loaded; otherwise Preg. The Preg |
| 19 |
* variant is also used directly by callers that intentionally want PCRE |
| 20 |
* regex semantics regardless of host mbstring availability (e.g. SQL |
| 21 |
* placeholder rewrites that use \d / lookarounds, which mb_ereg's POSIX |
| 22 |
* syntax cannot express). |
| 23 |
* |
| 24 |
* Extracted from ABJ_404_Solution_MbStringAdapter per design-audit-2026-06-02 |
| 25 |
* M201 (parent task i802, this task i826). The adapter previously carried |
| 26 |
* both byte/multibyte string primitives and regex primitives in the same |
| 27 |
* interface; splitting regex off lets callers depend on the smaller surface |
| 28 |
* they actually need. |
| 29 |
*/ |
| 30 |
abstract class ABJ_404_Solution_RegexHelper { |
| 31 |
|
| 32 |
/** |
| 33 |
* Regex match. Mb implementations use POSIX (mb_ereg) syntax; Preg |
| 34 |
* implementations use PCRE syntax (without delimiters - the helper |
| 35 |
* supplies them). Callers that intentionally rely on PCRE shorthand |
| 36 |
* classes (\d, \w, lookarounds, etc.) MUST use the Preg helper |
| 37 |
* directly, not the polymorphic one. |
| 38 |
* |
| 39 |
* @param string $pattern |
| 40 |
* @param string $string |
| 41 |
* @param array<int, string>|null $regs |
| 42 |
* @return bool|int |
| 43 |
*/ |
| 44 |
abstract public function regexMatch(string $pattern, string $string, ?array &$regs = null); |
| 45 |
|
| 46 |
/** |
| 47 |
* Case-insensitive regex match. Same syntax caveat as regexMatch(). |
| 48 |
* |
| 49 |
* @param string $pattern |
| 50 |
* @param string $string |
| 51 |
* @param array<int, string>|null $regs |
| 52 |
* @return bool|int |
| 53 |
*/ |
| 54 |
abstract public function regexMatchi(string $pattern, string $string, ?array &$regs = null); |
| 55 |
|
| 56 |
/** |
| 57 |
* Regex replace. Same syntax caveat as regexMatch(). |
| 58 |
* |
| 59 |
* @param string $pattern |
| 60 |
* @param string $replacement |
| 61 |
* @param string $string |
| 62 |
* @return string|null |
| 63 |
*/ |
| 64 |
abstract public function regexReplace($pattern, $replacement, $string); |
| 65 |
|
| 66 |
/** |
| 67 |
* Check whether a URL appears to contain regex syntax. |
| 68 |
* |
| 69 |
* This is a PCRE-pattern URL classifier used to warn users when a |
| 70 |
* redirect URL looks like it contains regex syntax but is not marked |
| 71 |
* as a regex redirect. Implementation is PCRE-only by definition (the |
| 72 |
* patterns it scans for are PCRE constructs like \d, \w, lookarounds, |
| 73 |
* etc.), so the same concrete method serves both Mb and Preg variants |
| 74 |
* - it is not polymorphic. |
| 75 |
* |
| 76 |
* @param string|null $url |
| 77 |
* @return bool true if the URL appears to contain regex patterns |
| 78 |
*/ |
| 79 |
public function urlLooksLikeRegex($url) { |
| 80 |
if ($url === null || $url === '' || !is_string($url)) { |
| 81 |
return false; |
| 82 |
} |
| 83 |
|
| 84 |
$regexIndicators = array( |
| 85 |
'/\(\.\*\)/', // (.*) - common capture-all pattern |
| 86 |
'/\(\.\+\)/', // (.+) - one or more of anything |
| 87 |
'/\(\?\:/', // (?: - non-capturing group |
| 88 |
'/\(\?=/', // (?= - positive lookahead |
| 89 |
'/\(\?!/', // (?! - negative lookahead |
| 90 |
'/\[\^[^\]]+\]/', // [^...] - negated character class |
| 91 |
'/\[[a-z]-[a-z]\]/i', // [a-z] or [A-Z] - character range |
| 92 |
'/\[[0-9]-[0-9]\]/', // [0-9] - digit range |
| 93 |
'/\\\\d/', // \d - digit shorthand |
| 94 |
'/\\\\w/', // \w - word character shorthand |
| 95 |
'/\\\\s/', // \s - whitespace shorthand |
| 96 |
'/\.\*/', // .* - match anything (greedy) |
| 97 |
'/\.\+/', // .+ - match one or more of anything |
| 98 |
'/\.\?/', // .? - match zero or one of anything |
| 99 |
'/\{\d+,?\d*\}/', // {n} or {n,} or {n,m} - quantifiers |
| 100 |
'/\|/', // | - alternation |
| 101 |
); |
| 102 |
|
| 103 |
foreach ($regexIndicators as $pattern) { |
| 104 |
if (preg_match($pattern, $url)) { |
| 105 |
return true; |
| 106 |
} |
| 107 |
} |
| 108 |
|
| 109 |
return false; |
| 110 |
} |
| 111 |
} |
| 112 |
|