Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php @ 14:1fec387a4317
Update Drupal core to 8.5.2 via Composer
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:46:53 +0100 |
parents | |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
13:5fb285c0d0e3 | 14:1fec387a4317 |
---|---|
1 <?php | |
2 | |
3 /* | |
4 * This file is part of the Symfony package. | |
5 * | |
6 * (c) Fabien Potencier <fabien@symfony.com> | |
7 * | |
8 * For the full copyright and license information, please view the LICENSE | |
9 * file that was distributed with this source code. | |
10 */ | |
11 | |
12 namespace Symfony\Component\Routing\Matcher\Dumper; | |
13 | |
14 /** | |
15 * Prefix tree of routes preserving routes order. | |
16 * | |
17 * @author Frank de Jonge <info@frankdejonge.nl> | |
18 * | |
19 * @internal | |
20 */ | |
21 class StaticPrefixCollection | |
22 { | |
23 /** | |
24 * @var string | |
25 */ | |
26 private $prefix; | |
27 | |
28 /** | |
29 * @var array[]|StaticPrefixCollection[] | |
30 */ | |
31 private $items = array(); | |
32 | |
33 /** | |
34 * @var int | |
35 */ | |
36 private $matchStart = 0; | |
37 | |
38 public function __construct($prefix = '') | |
39 { | |
40 $this->prefix = $prefix; | |
41 } | |
42 | |
43 public function getPrefix() | |
44 { | |
45 return $this->prefix; | |
46 } | |
47 | |
48 /** | |
49 * @return mixed[]|StaticPrefixCollection[] | |
50 */ | |
51 public function getItems() | |
52 { | |
53 return $this->items; | |
54 } | |
55 | |
56 /** | |
57 * Adds a route to a group. | |
58 * | |
59 * @param string $prefix | |
60 * @param mixed $route | |
61 */ | |
62 public function addRoute($prefix, $route) | |
63 { | |
64 $prefix = '/' === $prefix ? $prefix : rtrim($prefix, '/'); | |
65 $this->guardAgainstAddingNotAcceptedRoutes($prefix); | |
66 | |
67 if ($this->prefix === $prefix) { | |
68 // When a prefix is exactly the same as the base we move up the match start position. | |
69 // This is needed because otherwise routes that come afterwards have higher precedence | |
70 // than a possible regular expression, which goes against the input order sorting. | |
71 $this->items[] = array($prefix, $route); | |
72 $this->matchStart = count($this->items); | |
73 | |
74 return; | |
75 } | |
76 | |
77 foreach ($this->items as $i => $item) { | |
78 if ($i < $this->matchStart) { | |
79 continue; | |
80 } | |
81 | |
82 if ($item instanceof self && $item->accepts($prefix)) { | |
83 $item->addRoute($prefix, $route); | |
84 | |
85 return; | |
86 } | |
87 | |
88 $group = $this->groupWithItem($item, $prefix, $route); | |
89 | |
90 if ($group instanceof self) { | |
91 $this->items[$i] = $group; | |
92 | |
93 return; | |
94 } | |
95 } | |
96 | |
97 // No optimised case was found, in this case we simple add the route for possible | |
98 // grouping when new routes are added. | |
99 $this->items[] = array($prefix, $route); | |
100 } | |
101 | |
102 /** | |
103 * Tries to combine a route with another route or group. | |
104 * | |
105 * @param StaticPrefixCollection|array $item | |
106 * @param string $prefix | |
107 * @param mixed $route | |
108 * | |
109 * @return null|StaticPrefixCollection | |
110 */ | |
111 private function groupWithItem($item, $prefix, $route) | |
112 { | |
113 $itemPrefix = $item instanceof self ? $item->prefix : $item[0]; | |
114 $commonPrefix = $this->detectCommonPrefix($prefix, $itemPrefix); | |
115 | |
116 if (!$commonPrefix) { | |
117 return; | |
118 } | |
119 | |
120 $child = new self($commonPrefix); | |
121 | |
122 if ($item instanceof self) { | |
123 $child->items = array($item); | |
124 } else { | |
125 $child->addRoute($item[0], $item[1]); | |
126 } | |
127 | |
128 $child->addRoute($prefix, $route); | |
129 | |
130 return $child; | |
131 } | |
132 | |
133 /** | |
134 * Checks whether a prefix can be contained within the group. | |
135 * | |
136 * @param string $prefix | |
137 * | |
138 * @return bool Whether a prefix could belong in a given group | |
139 */ | |
140 private function accepts($prefix) | |
141 { | |
142 return '' === $this->prefix || 0 === strpos($prefix, $this->prefix); | |
143 } | |
144 | |
145 /** | |
146 * Detects whether there's a common prefix relative to the group prefix and returns it. | |
147 * | |
148 * @param string $prefix | |
149 * @param string $anotherPrefix | |
150 * | |
151 * @return false|string A common prefix, longer than the base/group prefix, or false when none available | |
152 */ | |
153 private function detectCommonPrefix($prefix, $anotherPrefix) | |
154 { | |
155 $baseLength = strlen($this->prefix); | |
156 $commonLength = $baseLength; | |
157 $end = min(strlen($prefix), strlen($anotherPrefix)); | |
158 | |
159 for ($i = $baseLength; $i <= $end; ++$i) { | |
160 if (substr($prefix, 0, $i) !== substr($anotherPrefix, 0, $i)) { | |
161 break; | |
162 } | |
163 | |
164 $commonLength = $i; | |
165 } | |
166 | |
167 $commonPrefix = rtrim(substr($prefix, 0, $commonLength), '/'); | |
168 | |
169 if (strlen($commonPrefix) > $baseLength) { | |
170 return $commonPrefix; | |
171 } | |
172 | |
173 return false; | |
174 } | |
175 | |
176 /** | |
177 * Optimizes the tree by inlining items from groups with less than 3 items. | |
178 */ | |
179 public function optimizeGroups() | |
180 { | |
181 $index = -1; | |
182 | |
183 while (isset($this->items[++$index])) { | |
184 $item = $this->items[$index]; | |
185 | |
186 if ($item instanceof self) { | |
187 $item->optimizeGroups(); | |
188 | |
189 // When a group contains only two items there's no reason to optimize because at minimum | |
190 // the amount of prefix check is 2. In this case inline the group. | |
191 if ($item->shouldBeInlined()) { | |
192 array_splice($this->items, $index, 1, $item->items); | |
193 | |
194 // Lower index to pass through the same index again after optimizing. | |
195 // The first item of the replacements might be a group needing optimization. | |
196 --$index; | |
197 } | |
198 } | |
199 } | |
200 } | |
201 | |
202 private function shouldBeInlined() | |
203 { | |
204 if (count($this->items) >= 3) { | |
205 return false; | |
206 } | |
207 | |
208 foreach ($this->items as $item) { | |
209 if ($item instanceof self) { | |
210 return true; | |
211 } | |
212 } | |
213 | |
214 foreach ($this->items as $item) { | |
215 if (is_array($item) && $item[0] === $this->prefix) { | |
216 return false; | |
217 } | |
218 } | |
219 | |
220 return true; | |
221 } | |
222 | |
223 /** | |
224 * Guards against adding incompatible prefixes in a group. | |
225 * | |
226 * @param string $prefix | |
227 * | |
228 * @throws \LogicException when a prefix does not belong in a group | |
229 */ | |
230 private function guardAgainstAddingNotAcceptedRoutes($prefix) | |
231 { | |
232 if (!$this->accepts($prefix)) { | |
233 $message = sprintf('Could not add route with prefix %s to collection with prefix %s', $prefix, $this->prefix); | |
234 | |
235 throw new \LogicException($message); | |
236 } | |
237 } | |
238 } |