[ Index ]

PHP Cross Reference of phpBB-3.3.14-deutsch

title

Body

[close]

/vendor/s9e/regexp-builder/src/ -> Serializer.php (source)

   1  <?php declare(strict_types=1);
   2  
   3  /**
   4  * @package   s9e\RegexpBuilder
   5  * @copyright Copyright (c) 2016-2022 The s9e authors
   6  * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
   7  */
   8  namespace s9e\RegexpBuilder;
   9  
  10  use function array_diff_key, array_map, array_unshift, array_values, count, implode, is_array, is_int;
  11  use s9e\RegexpBuilder\MetaCharacters;
  12  use s9e\RegexpBuilder\Output\OutputInterface;
  13  
  14  class Serializer
  15  {
  16      /**
  17      * @var Escaper
  18      */
  19      protected $escaper;
  20  
  21      /**
  22      * @var MetaCharacters
  23      */
  24      protected $meta;
  25  
  26      /**
  27      * @var OutputInterface
  28      */
  29      protected $output;
  30  
  31      /**
  32      * @param OutputInterface $output
  33      * @parm  MetaCharacters  $meta
  34      * @param Escaper         $escaper
  35      */
  36  	public function __construct(OutputInterface $output, MetaCharacters $meta, Escaper $escaper)
  37      {
  38          $this->escaper = $escaper;
  39          $this->meta    = $meta;
  40          $this->output  = $output;
  41      }
  42  
  43      /**
  44      * Serialize given strings into a regular expression
  45      *
  46      * @param  array[] $strings
  47      * @return string
  48      */
  49  	public function serializeStrings(array $strings): string
  50      {
  51          $info         = $this->analyzeStrings($strings);
  52          $alternations = array_map([$this, 'serializeString'], $info['strings']);
  53          if (!empty($info['chars']))
  54          {
  55              // Prepend the character class to the list of alternations
  56              array_unshift($alternations, $this->serializeCharacterClass($info['chars']));
  57          }
  58  
  59          $expr = implode('|', $alternations);
  60          if ($this->needsParentheses($info))
  61          {
  62              $expr = '(?:' . $expr . ')';
  63          }
  64  
  65          return $expr . $info['quantifier'];
  66      }
  67  
  68      /**
  69      * Analyze given strings to determine how to serialize them
  70      *
  71      * The returned array may contains any of the following elements:
  72      *
  73      *  - (string) quantifier Either '' or '?'
  74      *  - (array)  chars      List of values from single-char strings
  75      *  - (array)  strings    List of multi-char strings
  76      *
  77      * @param  array[] $strings
  78      * @return array
  79      */
  80  	protected function analyzeStrings(array $strings): array
  81      {
  82          $info = ['alternationsCount' => 0, 'quantifier' => ''];
  83          if ($strings[0] === [])
  84          {
  85              $info['quantifier'] = '?';
  86              unset($strings[0]);
  87          }
  88  
  89          $chars = $this->getChars($strings);
  90          if (count($chars) > 1)
  91          {
  92              ++$info['alternationsCount'];
  93              $info['chars'] = array_values($chars);
  94              $strings       = array_diff_key($strings, $chars);
  95          }
  96  
  97          $info['strings']            = array_values($strings);
  98          $info['alternationsCount'] += count($strings);
  99  
 100          return $info;
 101      }
 102  
 103      /**
 104      * Return the portion of strings that are composed of a single character
 105      *
 106      * @param  array<int, array> $strings
 107      * @return array<int, int>            String key => value
 108      */
 109  	protected function getChars(array $strings): array
 110      {
 111          $chars = [];
 112          foreach ($strings as $k => $string)
 113          {
 114              if ($this->isChar($string))
 115              {
 116                  $chars[$k] = $string[0];
 117              }
 118          }
 119  
 120          return $chars;
 121      }
 122  
 123      /**
 124      * Get the list of ranges that cover all given values
 125      *
 126      * @param  integer[] $values Ordered list of values
 127      * @return array[]           List of ranges in the form [start, end]
 128      */
 129  	protected function getRanges(array $values): array
 130      {
 131          $i      = 0;
 132          $cnt    = count($values);
 133          $start  = $values[0];
 134          $end    = $start;
 135          $ranges = [];
 136          while (++$i < $cnt)
 137          {
 138              if ($values[$i] === $end + 1)
 139              {
 140                  ++$end;
 141              }
 142              else
 143              {
 144                  $ranges[] = [$start, $end];
 145                  $start = $end = $values[$i];
 146              }
 147          }
 148          $ranges[] = [$start, $end];
 149  
 150          return $ranges;
 151      }
 152  
 153      /**
 154      * Test whether given string represents a single character
 155      *
 156      * @param  array $string
 157      * @return bool
 158      */
 159  	protected function isChar(array $string): bool
 160      {
 161          return count($string) === 1 && is_int($string[0]) && MetaCharacters::isChar($string[0]);
 162      }
 163  
 164      /**
 165      * Test whether an expression is quantifiable based on the strings info
 166      *
 167      * @param  array $info
 168      * @return bool
 169      */
 170  	protected function isQuantifiable(array $info): bool
 171      {
 172          $strings = $info['strings'];
 173  
 174          return empty($strings) || $this->isSingleQuantifiableString($strings);
 175      }
 176  
 177      /**
 178      * Test whether a list of strings contains only one single quantifiable string
 179      *
 180      * @param  array[] $strings
 181      * @return bool
 182      */
 183  	protected function isSingleQuantifiableString(array $strings): bool
 184      {
 185          return count($strings) === 1 && count($strings[0]) === 1 && MetaCharacters::isQuantifiable($strings[0][0]);
 186      }
 187  
 188      /**
 189      * Test whether an expression needs parentheses based on the strings info
 190      *
 191      * @param  array $info
 192      * @return bool
 193      */
 194  	protected function needsParentheses(array $info): bool
 195      {
 196          return ($info['alternationsCount'] > 1 || ($info['quantifier'] && !$this->isQuantifiable($info)));
 197      }
 198  
 199      /**
 200      * Serialize a given list of values into a character class
 201      *
 202      * @param  integer[] $values
 203      * @return string
 204      */
 205  	protected function serializeCharacterClass(array $values): string
 206      {
 207          $expr = '[';
 208          foreach ($this->getRanges($values) as list($start, $end))
 209          {
 210              $expr .= $this->serializeCharacterClassUnit($start);
 211              if ($end > $start)
 212              {
 213                  if ($end > $start + 1)
 214                  {
 215                      $expr .= '-';
 216                  }
 217                  $expr .= $this->serializeCharacterClassUnit($end);
 218              }
 219          }
 220          $expr .= ']';
 221  
 222          return $expr;
 223      }
 224  
 225      /**
 226      * Serialize a given value to be used in a character class
 227      *
 228      * @param  integer $value
 229      * @return string
 230      */
 231  	protected function serializeCharacterClassUnit(int $value): string
 232      {
 233          return $this->serializeValue($value, 'escapeCharacterClass');
 234      }
 235  
 236      /**
 237      * Serialize an element from a string
 238      *
 239      * @param  array|integer $element
 240      * @return string
 241      */
 242  	protected function serializeElement($element): string
 243      {
 244          return (is_array($element)) ? $this->serializeStrings($element) : $this->serializeLiteral($element);
 245      }
 246  
 247      /**
 248      * Serialize a given value to be used as a literal
 249      *
 250      * @param  integer $value
 251      * @return string
 252      */
 253  	protected function serializeLiteral(int $value): string
 254      {
 255          return $this->serializeValue($value, 'escapeLiteral');
 256      }
 257  
 258      /**
 259      * Serialize a given string into a regular expression
 260      *
 261      * @param  array  $string
 262      * @return string
 263      */
 264  	protected function serializeString(array $string): string
 265      {
 266          return implode('', array_map([$this, 'serializeElement'], $string));
 267      }
 268  
 269      /**
 270      * Serialize a given value
 271      *
 272      * @param  integer $value
 273      * @param  string  $escapeMethod
 274      * @return string
 275      */
 276  	protected function serializeValue(int $value, string $escapeMethod): string
 277      {
 278          return ($value < 0) ? $this->meta->getExpression($value) : $this->escaper->$escapeMethod($this->output->output($value));
 279      }
 280  }


Generated: Mon Nov 25 19:05:08 2024 Cross-referenced by PHPXref 0.7.1