Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

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: 01.05.2026, 11:28 - 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  }