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

Serializer.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 6.20 KiB


001  <?php declare(strict_types=1);
002   
003  /**
004  * @package   s9e\RegexpBuilder
005  * @copyright Copyright (c) 2016-2022 The s9e authors
006  * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
007  */
008  namespace s9e\RegexpBuilder;
009   
010  use function array_diff_key, array_map, array_unshift, array_values, count, implode, is_array, is_int;
011  use s9e\RegexpBuilder\MetaCharacters;
012  use s9e\RegexpBuilder\Output\OutputInterface;
013   
014  class Serializer
015  {
016      /**
017      * @var Escaper
018      */
019      protected $escaper;
020   
021      /**
022      * @var MetaCharacters
023      */
024      protected $meta;
025   
026      /**
027      * @var OutputInterface
028      */
029      protected $output;
030   
031      /**
032      * @param OutputInterface $output
033      * @parm  MetaCharacters  $meta
034      * @param Escaper         $escaper
035      */
036      public function __construct(OutputInterface $output, MetaCharacters $meta, Escaper $escaper)
037      {
038          $this->escaper = $escaper;
039          $this->meta    = $meta;
040          $this->output  = $output;
041      }
042   
043      /**
044      * Serialize given strings into a regular expression
045      *
046      * @param  array[] $strings
047      * @return string
048      */
049      public function serializeStrings(array $strings): string
050      {
051          $info         = $this->analyzeStrings($strings);
052          $alternations = array_map([$this, 'serializeString'], $info['strings']);
053          if (!empty($info['chars']))
054          {
055              // Prepend the character class to the list of alternations
056              array_unshift($alternations, $this->serializeCharacterClass($info['chars']));
057          }
058   
059          $expr = implode('|', $alternations);
060          if ($this->needsParentheses($info))
061          {
062              $expr = '(?:' . $expr . ')';
063          }
064   
065          return $expr . $info['quantifier'];
066      }
067   
068      /**
069      * Analyze given strings to determine how to serialize them
070      *
071      * The returned array may contains any of the following elements:
072      *
073      *  - (string) quantifier Either '' or '?'
074      *  - (array)  chars      List of values from single-char strings
075      *  - (array)  strings    List of multi-char strings
076      *
077      * @param  array[] $strings
078      * @return array
079      */
080      protected function analyzeStrings(array $strings): array
081      {
082          $info = ['alternationsCount' => 0, 'quantifier' => ''];
083          if ($strings[0] === [])
084          {
085              $info['quantifier'] = '?';
086              unset($strings[0]);
087          }
088   
089          $chars = $this->getChars($strings);
090          if (count($chars) > 1)
091          {
092              ++$info['alternationsCount'];
093              $info['chars'] = array_values($chars);
094              $strings       = array_diff_key($strings, $chars);
095          }
096   
097          $info['strings']            = array_values($strings);
098          $info['alternationsCount'] += count($strings);
099   
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  }