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 |
RecursiveParser.php
001 <?php declare(strict_types=1);
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;
009
010 use RuntimeException;
011 use s9e\TextFormatter\Configurator\RecursiveParser\MatcherInterface;
012
013 class RecursiveParser
014 {
015 /**
016 * @var array Callback associated with each match name
017 */
018 protected $callbacks = [];
019
020 /**
021 * @var array Match names associated with each group
022 */
023 protected $groupMatches = [];
024
025 /**
026 * @var array Groups associated with each match name
027 */
028 protected $matchGroups = [];
029
030 /**
031 * @var string Regexp used to match input
032 */
033 protected $regexp;
034
035 /**
036 * Parse given string
037 *
038 * @param string $str
039 * @param string $name Allowed match, either match name or group name (default: allow all)
040 * @return mixed
041 */
042 public function parse(string $str, string $name = '')
043 {
044 $regexp = $this->regexp;
045 if ($name !== '')
046 {
047 $restrict = (isset($this->groupMatches[$name])) ? implode('|', $this->groupMatches[$name]) : $name;
048 $regexp = preg_replace('(\\(\\?<(?!(?:' . $restrict . '|\\w+\\d+)>))', '(*F)$0', $regexp);
049 }
050
051 preg_match($regexp, $str, $m);
052 if (!isset($m['MARK']))
053 {
054 throw new RuntimeException('Cannot parse ' . var_export($str, true));
055 }
056
057 $name = $m['MARK'];
058 $args = $this->getArguments($m, $name);
059
060 return [
061 'groups' => $this->matchGroups[$name] ?? [],
062 'match' => $name,
063 'value' => call_user_func_array($this->callbacks[$name], $args)
064 ];
065 }
066
067 /**
068 * Set the list of matchers used by this parser
069 *
070 * @param MatcherInterface[]
071 * @return void
072 */
073 public function setMatchers(array $matchers): void
074 {
075 $matchRegexps = [];
076 $this->groupMatches = [];
077 $this->matchGroups = [];
078 foreach ($this->getMatchersConfig($matchers) as $matchName => $matchConfig)
079 {
080 foreach ($matchConfig['groups'] as $group)
081 {
082 $this->groupMatches[$group][] = $matchName;
083 }
084
085 $regexp = $matchConfig['regexp'];
086 $regexp = $this->insertCaptureNames($matchName , $regexp);
087 $regexp = str_replace(' ', '\\s*+', $regexp);
088 $regexp = '(?<' . $matchName . '>' . $regexp . ')(*:' . $matchName . ')';
089
090 $matchRegexps[] = $regexp;
091 $this->callbacks[$matchName] = $matchConfig['callback'];
092 $this->matchGroups[$matchName] = $matchConfig['groups'];
093 }
094
095 $groupRegexps = [];
096 foreach ($this->groupMatches as $group => $names)
097 {
098 $groupRegexps[] = '(?<' . $group . '>(?&' . implode(')|(?&', $names) . '))';
099 }
100
101 $this->regexp = '((?(DEFINE)' . implode('', $groupRegexps). ')'
102 . '^(?:' . implode('|', $matchRegexps) . ')$)s';
103 }
104
105 /**
106 * Get the list of arguments produced by a regexp's match
107 *
108 * @param string[] $matches Regexp matches
109 * @param string $name Regexp name
110 * @return string[]
111 */
112 protected function getArguments(array $matches, string $name): array
113 {
114 $args = [];
115 $i = 0;
116 while (isset($matches[$name . $i]))
117 {
118 $args[] = $matches[$name . $i];
119 ++$i;
120 }
121
122 return $args;
123 }
124
125 /**
126 * Collect, normalize, sort and return the config for all matchers
127 *
128 * @param MatcherInterface[] $matchers
129 * @return array
130 */
131 protected function getMatchersConfig(array $matchers): array
132 {
133 $matchersConfig = [];
134 foreach ($matchers as $matcher)
135 {
136 foreach ($matcher->getMatchers() as $matchName => $matchConfig)
137 {
138 if (is_string($matchConfig))
139 {
140 $matchConfig = ['regexp' => $matchConfig];
141 }
142 $parts = explode(':', $matchName);
143 $matchName = array_pop($parts);
144 $matchConfig += [
145 'callback' => [$matcher, 'parse' . $matchName],
146 'groups' => [],
147 'order' => 0
148 ];
149 $matchConfig['name'] = $matchName;
150 $matchConfig['groups'] = array_unique(array_merge($matchConfig['groups'], $parts));
151 sort($matchConfig['groups']);
152
153 $matchersConfig[$matchName] = $matchConfig;
154 }
155 }
156 uasort($matchersConfig, 'static::sortMatcherConfig');
157
158 return $matchersConfig;
159 }
160
161 /**
162 * Insert capture names into given regexp
163 *
164 * @param string $name Name of the regexp, used to name captures
165 * @param string $regexp Original regexp
166 * @return string Modified regexp
167 */
168 protected function insertCaptureNames(string $name, string $regexp): string
169 {
170 $i = 0;
171
172 return preg_replace_callback(
173 '((?<!\\\\)\\((?!\\?))',
174 function ($m) use (&$i, $name)
175 {
176 return '(?<' . $name . $i++ . '>';
177 },
178 $regexp
179 );
180 }
181
182 /**
183 * Compare two matchers' config
184 *
185 * @param array $a
186 * @param array $b
187 * @return integer
188 */
189 protected static function sortMatcherConfig(array $a, array $b): int
190 {
191 if ($a['order'] !== $b['order'])
192 {
193 return $a['order'] - $b['order'];
194 }
195
196 return strcmp($a['name'], $b['name']);
197 }
198 }