[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package s9e\TextFormatter 5 * @copyright Copyright (c) 2010-2022 The s9e authors 6 * @license http://www.opensource.org/licenses/mit-license.php The MIT License 7 */ 8 namespace s9e\TextFormatter\Configurator\Helpers; 9 10 use RuntimeException; 11 use s9e\TextFormatter\Configurator\RecursiveParser; 12 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\BooleanFunctions; 13 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\BooleanOperators; 14 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\Comparisons; 15 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\Core; 16 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\Math; 17 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\SingleByteStringFunctions; 18 use s9e\TextFormatter\Utils\XPath; 19 20 abstract class XPathHelper 21 { 22 /** 23 * Decode strings inside of an XPath expression 24 * 25 * @param string $expr 26 * @return string 27 */ 28 public static function decodeStrings($expr) 29 { 30 return preg_replace_callback( 31 '(\'[^\']*+\'|"[^"]*+")', 32 function ($m) 33 { 34 return $m[0][0] . hex2bin(substr($m[0], 1, -1)) . $m[0][0]; 35 }, 36 $expr 37 ); 38 } 39 40 /** 41 * Encode strings inside of an XPath expression 42 * 43 * @param string $expr 44 * @return string 45 */ 46 public static function encodeStrings($expr) 47 { 48 return preg_replace_callback( 49 '(\'[^\']*+\'|"[^"]*+")', 50 function ($m) 51 { 52 return $m[0][0] . bin2hex(substr($m[0], 1, -1)) . $m[0][0]; 53 }, 54 $expr 55 ); 56 } 57 58 /** 59 * Return the list of variables used in a given XPath expression 60 * 61 * @param string $expr XPath expression 62 * @return array Alphabetically sorted list of unique variable names 63 */ 64 public static function getVariables($expr) 65 { 66 // First, remove strings' contents to prevent false-positives 67 $expr = preg_replace('/(["\']).*?\\1/s', '$1$1', $expr); 68 69 // Capture all the variable names 70 preg_match_all('/\\$(\\w+)/', $expr, $matches); 71 72 // Dedupe and sort names 73 $varNames = array_unique($matches[1]); 74 sort($varNames); 75 76 return $varNames; 77 } 78 79 /** 80 * Determine whether given XPath expression definitely evaluates to a number 81 * 82 * @param string $expr XPath expression 83 * @return bool Whether given XPath expression definitely evaluates to a number 84 */ 85 public static function isExpressionNumeric($expr) 86 { 87 // Detect simple arithmetic operations 88 if (preg_match('(^([$@][-\\w]++|-?[.\\d]++)(?: *(?:[-*+]|div) *(?1))+$)', $expr)) 89 { 90 return true; 91 } 92 93 // Try parsing the expression as a math expression 94 try 95 { 96 return (bool) self::getXPathParser()->parse($expr, 'Math'); 97 } 98 catch (RuntimeException $e) 99 { 100 // Do nothing 101 } 102 103 return false; 104 } 105 106 /** 107 * Remove extraneous space in a given XPath expression 108 * 109 * @param string $expr Original XPath expression 110 * @return string Minified XPath expression 111 */ 112 public static function minify($expr) 113 { 114 $expr = trim($expr); 115 116 // Test whether there's any characters that can be removed 117 if (!preg_match('([\\s\\)])', $expr)) 118 { 119 return $expr; 120 } 121 122 // Temporarily encode the content of literal strings 123 $expr = self::encodeStrings(trim($expr)); 124 125 // Normalize whitespace to a single space 126 $expr = preg_replace('(\\s+)', ' ', $expr); 127 128 $regexps = [ 129 // Remove the space between a non-word character and a word character 130 '([-a-z_0-9]\\K (?=[^-a-z_0-9]))i', 131 '([^-a-z_0-9]\\K (?=[-a-z_0-9]))i', 132 133 // Remove the space between two non-word characters as long as they're not two - 134 '((?!- -)[^-a-z_0-9]\\K (?=[^-a-z_0-9]))i', 135 136 // Remove the space between a - and a word character as long as there's a space before - 137 '( -\\K (?=[a-z_0-9]))i', 138 139 // Remove the space between an operator and the next token if it's a left parenthesis 140 '([ \\)](?:and|div|mod|or)\\K (?=\\())', 141 142 // Remove the space after a number 143 '(\\b\\d+\\K )' 144 ]; 145 $expr = preg_replace($regexps, '', $expr); 146 147 // Remove consecutive parentheses where redundant 148 $expr = self::removeRedundantParentheses($expr); 149 150 // Restore the literals 151 $expr = self::decodeStrings($expr); 152 153 return $expr; 154 } 155 156 /** 157 * Remove consecutive parentheses where redundant 158 */ 159 protected static function removeRedundantParentheses(string $expr): string 160 { 161 // Add parentheses around the original expression and terminate the expression with a space 162 preg_match_all('(([\\(\\)])|[^\\(\\)]++)', '(' . $expr . ') ', $m); 163 $tokens = $m[0]; 164 $parens = array_filter($m[1]); 165 166 // Iterate over parentheses and remove the inner pair when consecutive parentheses are found 167 $depth = 0; 168 $left = []; 169 foreach ($parens as $k => $token) 170 { 171 if ($token === '(') 172 { 173 $left[$depth++] = $k; 174 } 175 elseif (--$depth > 0 && $tokens[$k + 1] === ')' && $left[$depth - 1] === $left[$depth] - 1) 176 { 177 unset($tokens[$k], $tokens[$left[$depth]]); 178 } 179 } 180 181 // Remove the extra parentheses as well as the last token before serializing them 182 return implode('', array_slice($tokens, 1, -2)); 183 } 184 185 /** 186 * Parse an XPath expression that is composed entirely of equality tests between a variable part 187 * and a constant part 188 * 189 * @param string $expr 190 * @return array|false 191 */ 192 public static function parseEqualityExpr($expr) 193 { 194 // Match an equality between a variable and a literal or the concatenation of strings 195 $eq = '(?<equality>' 196 . '(?<key>@[-\\w]+|\\$\\w+|\\.)' 197 . '(?<operator>\\s*=\\s*)' 198 . '(?:' 199 . '(?<literal>(?<string>"[^"]*"|\'[^\']*\')|0|[1-9][0-9]*)' 200 . '|' 201 . '(?<concat>concat\\(\\s*(?&string)\\s*(?:,\\s*(?&string)\\s*)+\\))' 202 . ')' 203 . '|' 204 . '(?:(?<literal>(?&literal))|(?<concat>(?&concat)))(?&operator)(?<key>(?&key))' 205 . ')'; 206 207 // Match a string that is entirely composed of equality checks separated with "or" 208 $regexp = '(^(?J)\\s*' . $eq . '\\s*(?:or\\s*(?&equality)\\s*)*$)'; 209 if (!preg_match($regexp, $expr)) 210 { 211 return false; 212 } 213 214 preg_match_all("((?J)$eq)", $expr, $matches, PREG_SET_ORDER); 215 216 $map = []; 217 foreach ($matches as $m) 218 { 219 $key = $m['key']; 220 $value = (!empty($m['concat'])) 221 ? self::evaluateConcat($m['concat']) 222 : self::evaluateLiteral($m['literal']); 223 224 $map[$key][] = $value; 225 } 226 227 return $map; 228 } 229 230 /** 231 * Evaluate a concat() expression where all arguments are string literals 232 * 233 * @param string $expr concat() expression 234 * @return string Expression's value 235 */ 236 protected static function evaluateConcat($expr) 237 { 238 preg_match_all('(\'[^\']*\'|"[^"]*")', $expr, $strings); 239 240 $value = ''; 241 foreach ($strings[0] as $string) 242 { 243 $value .= substr($string, 1, -1); 244 } 245 246 return $value; 247 } 248 249 /** 250 * Evaluate an XPath literal 251 * 252 * @param string $expr XPath literal 253 * @return string Literal's string value 254 */ 255 protected static function evaluateLiteral($expr) 256 { 257 if ($expr[0] === '"' || $expr[0] === "'") 258 { 259 $expr = substr($expr, 1, -1); 260 } 261 262 return $expr; 263 } 264 265 /** 266 * Generate and return a cached XPath parser with a default set of matchers 267 * 268 * @return RecursiveParser 269 */ 270 protected static function getXPathParser() 271 { 272 static $parser; 273 if (!isset($parser)) 274 { 275 $parser = new RecursiveParser; 276 $matchers = []; 277 $matchers[] = new BooleanFunctions($parser); 278 $matchers[] = new BooleanOperators($parser); 279 $matchers[] = new Comparisons($parser); 280 $matchers[] = new Core($parser); 281 $matchers[] = new Math($parser); 282 $matchers[] = new SingleByteStringFunctions($parser); 283 284 $parser->setMatchers($matchers); 285 } 286 287 return $parser; 288 } 289 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |