Skip to content
This repository was archived by the owner on Jan 31, 2020. It is now read-only.

Commit c4687f8

Browse files
committed
More cleanup and tests
1 parent 9b98581 commit c4687f8

16 files changed

+1025
-424
lines changed

src/PartialRouteResult.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public static function fromMethodFailure(
3535
) : PartialRouteResult {
3636
$result = new self();
3737
$result->success = false;
38-
$result->allowedMethods = $allowedMethods;
38+
$result->setAllowedMethods($allowedMethods);
3939
$result->pathOffset = $pathOffset;
4040
$result->matchedPathLength = $matchedPathLength;
4141
return $result;

src/Route/Chain.php

+6-3
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public static function factory($options = []) : RouteInterface
8686
}
8787

8888
if ($options['routes'] instanceof Traversable) {
89-
$options['routes'] = ArrayUtils::iteratorToArray($options['child_routes']);
89+
$options['routes'] = ArrayUtils::iteratorToArray($options['routes']);
9090
}
9191

9292
if (! isset($options['route_plugins'])) {
@@ -121,6 +121,9 @@ public function addRoute($name, $route, $priority = null) : void
121121

122122
public function partialMatch(Request $request, int $pathOffset = 0, array $options = []) : PartialRouteResult
123123
{
124+
if ($pathOffset < 0) {
125+
throw new Exception\InvalidArgumentException('Route path offset cannot be negative');
126+
}
124127
if ($this->chainRoutes !== null) {
125128
$this->addRoutes($this->chainRoutes);
126129
$this->chainRoutes = null;
@@ -193,7 +196,7 @@ public function assemble(array $params = [], array $options = []) : UriInterface
193196

194197
$uri = $options['uri'] ?? new Uri();
195198
if (! $uri instanceof UriInterface) {
196-
throw new Exception\DomainException(\sprintf(
199+
throw new Exception\InvalidArgumentException(\sprintf(
197200
'Route assemble option \'uri\' must be instance of %s, got %s',
198201
UriInterface::class,
199202
(\is_object($uri) ? \get_class($uri) : \gettype($uri))
@@ -215,7 +218,7 @@ public function assemble(array $params = [], array $options = []) : UriInterface
215218
$uri = $route->assemble($params, $chainOptions);
216219
$params = array_diff_key($params, array_flip($route->getLastAssembledParams()));
217220

218-
$this->assembledParams += $route->getLastAssembledParams();
221+
$this->assembledParams = \array_merge($this->assembledParams, $route->getLastAssembledParams());
219222
}
220223

221224
return $uri;

src/Route/Part.php

+27-26
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ public function match(Request $request, int $pathOffset = 0, array $options = []
147147
{
148148
$partialResult = $this->route->partialMatch($request, $pathOffset, $options);
149149

150+
// no match, bail out
150151
if ($partialResult->isFailure() && ! $partialResult->isMethodFailure()) {
151152
return RouteResult::fromRouteFailure();
152153
}
@@ -162,54 +163,54 @@ public function match(Request $request, int $pathOffset = 0, array $options = []
162163
return RouteResult::fromMethodFailure($partialResult->getAllowedMethods());
163164
}
164165

166+
// @TODO get rid of lazy routes
165167
if ($this->childRoutes !== null) {
166168
$this->addRoutes($this->childRoutes);
167169
$this->childRoutes = null;
168170
}
169171

170-
$nextOffset = $pathOffset + $partialResult->getMatchedPathLength();
171-
172-
if (isset($options['translator'])
173-
&& ! isset($options['locale'])
174-
&& $partialResult->isSuccess()
175-
&& null !== ($locale = $partialResult->getMatchedParams()['locale'] ?? null)
176-
) {
177-
$options['locale'] = $locale;
172+
// pass matched params to child routes.
173+
// Could be used for obtaining locale from parent route match.
174+
if ($partialResult->isSuccess()) {
175+
$options['parent_match_params'] = $options['parent_match_params'] ?? [];
176+
$options['parent_match_params'] += $partialResult->getMatchedParams();
178177
}
179178

180-
$gatherMethods = $options['gather_allowed_methods'] ?? false;
181179
if ($partialResult->isMethodFailure()) {
180+
// we got partial method failure, keep matching to find all allowed methods
182181
$options['gather_allowed_methods'] = true;
183182
}
184183

184+
// match child routes
185+
$nextOffset = $pathOffset + $partialResult->getMatchedPathLength();
185186
$result = parent::match($request, $nextOffset, $options);
186187
if ($result->isFailure() && ! $result->isMethodFailure()) {
187188
return $result;
188189
}
189-
if ($partialResult->isMethodFailure()) {
190-
$allowed = $partialResult->getAllowedMethods();
191-
if (! empty($methods = $result->getAllowedMethods())) {
192-
$allowed = \array_intersect($allowed, $methods);
190+
191+
// gather allowed methods
192+
$partialAllowed = $partialResult->getAllowedMethods();
193+
$childAllowed = $result->getAllowedMethods();
194+
if (null !== $partialAllowed && null !== $childAllowed) {
195+
$allowed = \array_intersect($partialAllowed, $childAllowed);
196+
} else {
197+
$allowed = $partialAllowed ?? $childAllowed;
198+
}
199+
200+
// was it a method failure?
201+
if ($partialResult->isMethodFailure() || $result->isMethodFailure()) {
202+
if (empty($allowed)) {
203+
return RouteResult::fromRouteFailure();
193204
}
194205
return RouteResult::fromMethodFailure($allowed);
195206
}
196-
if ($result->isMethodFailure()) {
197-
return $result;
198-
}
207+
208+
// we got success
199209
$return = RouteResult::fromRouteMatch(
200210
\array_merge($partialResult->getMatchedParams(), $result->getMatchedParams()),
201211
$result->getMatchedRouteName()
202212
);
203-
$allowed = $partialResult->getAllowedMethods();
204-
$nested = $result->getAllowedMethods();
205-
if (! empty($allowed) && ! empty($nested)) {
206-
$allowed = \array_intersect($allowed, $nested);
207-
}
208-
$allowed = $allowed ?? $nested;
209-
if (! empty($allowed)) {
210-
$return->withAllowedMethods($allowed);
211-
}
212-
return $return;
213+
return $return->withAllowedMethods($allowed);
213214
}
214215

215216
/**

src/Route/Segment.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,8 @@ public function partialMatch(Request $request, int $pathOffset = 0, array $optio
369369
}
370370

371371
$translator = $options['translator'];
372-
$textDomain = (isset($options['text_domain']) ? $options['text_domain'] : 'default');
373-
$locale = (isset($options['locale']) ? $options['locale'] : null);
372+
$textDomain = $options['text_domain'] ?? 'default';
373+
$locale = $options['locale'] ?? $options['parent_match_params']['locale'] ?? null;
374374

375375
foreach ($this->translationKeys as $key) {
376376
$regex = str_replace('#' . $key . '#', $translator->translate($key, $textDomain, $locale), $regex);

src/RouteResult.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static function fromMethodFailure(array $allowedMethods) : RouteResult
2626
{
2727
$result = new self();
2828
$result->success = false;
29-
$result->allowedMethods = $allowedMethods;
29+
$result->setAllowedMethods($allowedMethods);
3030
return $result;
3131
}
3232

src/RouteResultTrait.php

+14-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public function withMatchedParams(array $params) : self
8585
public function withAllowedMethods(?array $allowedMethods) : self
8686
{
8787
$result = clone $this;
88-
$result->allowedMethods = $allowedMethods;
88+
$result->setAllowedMethods($allowedMethods);
8989
return $result;
9090
}
9191

@@ -142,4 +142,17 @@ public function getAllowedMethods() : ?array
142142
{
143143
return $this->allowedMethods;
144144
}
145+
146+
private function setAllowedMethods(?array $methods) : void
147+
{
148+
if (null !== $methods) {
149+
// @TODO verify this is faster than array_map + array_unique.
150+
// array is too small to see the difference I think
151+
$methods = \array_keys(\array_change_key_case(
152+
\array_flip($methods),
153+
\CASE_UPPER
154+
));
155+
}
156+
$this->allowedMethods = $methods;
157+
}
145158
}

src/SimpleRouteStack.php

+6-7
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ public function addRoutes($routes) : void
152152
* @param string $name
153153
* @param mixed $route
154154
* @param int $priority
155-
* @return SimpleRouteStack
156155
*/
157156
public function addRoute($name, $route, $priority = null) : void
158157
{
@@ -172,7 +171,6 @@ public function addRoute($name, $route, $priority = null) : void
172171
*
173172
* @see RouteStackInterface::removeRoute()
174173
* @param string $name
175-
* @return SimpleRouteStack
176174
*/
177175
public function removeRoute($name) : void
178176
{
@@ -282,8 +280,7 @@ protected function routeFromArray($specs) : RouteInterface
282280
/**
283281
* match(): defined by RouteInterface interface.
284282
*
285-
* @see \Zend\Router\RouteInterface::match()
286-
* @param Request $request
283+
* @param Request $request
287284
* @param int $pathOffset
288285
* @param array $options
289286
* @return RouteResult
@@ -295,17 +292,19 @@ public function match(Request $request, int $pathOffset = 0, array $options = []
295292
/** @var RouteInterface $route */
296293
$result = $route->match($request, $pathOffset, $options);
297294
if ($result->isMethodFailure()) {
298-
$allowedMethods += $result->getAllowedMethods();
295+
$allowedMethods = \array_merge($allowedMethods, $result->getAllowedMethods());
299296
}
300297
if ($result->isSuccess()) {
301298
return $result->withMatchedRouteName($name)
302299
->withMatchedParams(
303300
\array_merge($this->defaultParams, $result->getMatchedParams())
304-
);
301+
)
302+
// simple route stack does not gather allowed methods on success
303+
->withAllowedMethods(null);
305304
}
306305
}
307306
if (! empty($allowedMethods)) {
308-
return RouteResult::fromMethodFailure(\array_keys(\array_flip($allowedMethods)));
307+
return RouteResult::fromMethodFailure($allowedMethods);
309308
}
310309

311310
return RouteResult::fromRouteFailure();

src/TreeRouteStack.php

+32-17
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,34 @@ public static function factory($options = []) : RouteInterface
5252
));
5353
}
5454

55-
/**
56-
* @var TreeRouteStack $instance
57-
*/
58-
$instance = parent::factory($options);
55+
if ($options instanceof Traversable) {
56+
$options = ArrayUtils::iteratorToArray($options);
57+
} elseif (! is_array($options)) {
58+
throw new Exception\InvalidArgumentException(sprintf(
59+
'%s expects an array or Traversable set of options',
60+
__METHOD__
61+
));
62+
}
63+
64+
$routePluginManager = null;
65+
if (isset($options['route_plugins'])) {
66+
$routePluginManager = $options['route_plugins'];
67+
}
68+
69+
$instance = new static($routePluginManager);
5970

6071
if (isset($options['prototypes'])) {
6172
$instance->addPrototypes($options['prototypes']);
6273
}
6374

75+
if (isset($options['routes'])) {
76+
$instance->addRoutes($options['routes']);
77+
}
78+
79+
if (isset($options['default_params'])) {
80+
$instance->setDefaultParams($options['default_params']);
81+
}
82+
6483
return $instance;
6584
}
6685

@@ -227,40 +246,36 @@ public function match(Request $request, int $pathOffset = 0, array $options = []
227246
// method failure have special handling
228247
continue;
229248
}
249+
250+
if (null !== $result->getAllowedMethods()) {
251+
$allowedMethods = \array_merge($allowedMethods, $result->getAllowedMethods());
252+
}
253+
230254
if ($result->isMethodFailure()) {
231-
$allowedMethods += $result->getAllowedMethods();
232255
continue;
233256
}
234257

235-
// now handling success
258+
// store first successful result, it will be returned later
236259
if (null === $successfulRouteResult) {
237260
$successfulRouteResult = $result->withParentRouteName($name)
238261
->withMatchedParams(\array_merge($this->defaultParams, $result->getMatchedParams()));
239262
}
240263

241-
// @TODO declare constant for the attribute
242264
if (($options['gather_allowed_methods'] ?? false) !== true
243265
|| empty($result->getAllowedMethods())
244266
) {
245267
// we do not need to gather allowed methods or all methods are allowed, we can return immediately
246-
return $successfulRouteResult;
268+
return $successfulRouteResult->withAllowedMethods(null);
247269
}
248-
249-
$allowedMethods += $result->getAllowedMethods();
250270
}
251271

252-
$allowedMethods = \array_keys($allowedMethods);
253272
if ($successfulRouteResult) {
254273
// return successful result with gathered methods
255-
return $successfulRouteResult->withAllowedMethods(
256-
\array_keys(\array_flip($allowedMethods))
257-
);
274+
return $successfulRouteResult->withAllowedMethods($allowedMethods);
258275
}
259276

260277
if (! empty($allowedMethods)) {
261-
return RouteResult::fromMethodFailure(
262-
\array_keys(\array_flip($allowedMethods))
263-
);
278+
return RouteResult::fromMethodFailure($allowedMethods);
264279
}
265280

266281
return RouteResult::fromRouteFailure();

test/PartialRouteResultTest.php

+7
Original file line numberDiff line numberDiff line change
@@ -231,4 +231,11 @@ public function testWithMatchedParamsThrowsForUnsuccessfulResult()
231231
$result = PartialRouteResult::fromRouteFailure();
232232
$result->withMatchedParams(['foo' => 'bar']);
233233
}
234+
235+
public function testAllowedMethodsAreDisambiguatedAndCastToUppercase()
236+
{
237+
$methods = ['GeT', 'post', 'PoSt'];
238+
$result = PartialRouteResult::fromMethodFailure($methods, 0, 0);
239+
$this->assertEquals(['GET', 'POST'], $result->getAllowedMethods());
240+
}
234241
}

0 commit comments

Comments
 (0)