Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
XPathHelper.php
001 <?php
002
003 /**
004 * @package s9e\TextFormatter
005 * @copyright Copyright (c) 2010-2022 The s9e authors
006 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
007 */
008 namespace s9e\TextFormatter\Configurator\Helpers;
009
010 use RuntimeException;
011 use s9e\TextFormatter\Configurator\RecursiveParser;
012 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\BooleanFunctions;
013 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\BooleanOperators;
014 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\Comparisons;
015 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\Core;
016 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\Math;
017 use s9e\TextFormatter\Configurator\RendererGenerators\PHP\XPathConvertor\Convertors\SingleByteStringFunctions;
018 use s9e\TextFormatter\Utils\XPath;
019
020 abstract class XPathHelper
021 {
022 /**
023 * Decode strings inside of an XPath expression
024 *
025 * @param string $expr
026 * @return string
027 */
028 public static function decodeStrings($expr)
029 {
030 return preg_replace_callback(
031 '(\'[^\']*+\'|"[^"]*+")',
032 function ($m)
033 {
034 return $m[0][0] . hex2bin(substr($m[0], 1, -1)) . $m[0][0];
035 },
036 $expr
037 );
038 }
039
040 /**
041 * Encode strings inside of an XPath expression
042 *
043 * @param string $expr
044 * @return string
045 */
046 public static function encodeStrings($expr)
047 {
048 return preg_replace_callback(
049 '(\'[^\']*+\'|"[^"]*+")',
050 function ($m)
051 {
052 return $m[0][0] . bin2hex(substr($m[0], 1, -1)) . $m[0][0];
053 },
054 $expr
055 );
056 }
057
058 /**
059 * Return the list of variables used in a given XPath expression
060 *
061 * @param string $expr XPath expression
062 * @return array Alphabetically sorted list of unique variable names
063 */
064 public static function getVariables($expr)
065 {
066 // First, remove strings' contents to prevent false-positives
067 $expr = preg_replace('/(["\']).*?\\1/s', '$1$1', $expr);
068
069 // Capture all the variable names
070 preg_match_all('/\\$(\\w+)/', $expr, $matches);
071
072 // Dedupe and sort names
073 $varNames = array_unique($matches[1]);
074 sort($varNames);
075
076 return $varNames;
077 }
078
079 /**
080 * Determine whether given XPath expression definitely evaluates to a number
081 *
082 * @param string $expr XPath expression
083 * @return bool Whether given XPath expression definitely evaluates to a number
084 */
085 public static function isExpressionNumeric($expr)
086 {
087 // Detect simple arithmetic operations
088 if (preg_match('(^([$@][-\\w]++|-?[.\\d]++)(?: *(?:[-*+]|div) *(?1))+$)', $expr))
089 {
090 return true;
091 }
092
093 // Try parsing the expression as a math expression
094 try
095 {
096 return (bool) self::getXPathParser()->parse($expr, 'Math');
097 }
098 catch (RuntimeException $e)
099 {
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 }