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 |
Serializer.php
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 }