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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

XPathHelper.php

Zuletzt modifiziert: 02.04.2025, 15:04 - Dateigröße: 7.42 KiB


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  }