[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |