Skip to content

Commit 31fa869

Browse files
committed
Simplify implementation for optionals
Drop the manual parsing code, use regex.
1 parent 2acf229 commit 31fa869

File tree

2 files changed

+23
-51
lines changed

2 files changed

+23
-51
lines changed

src/RouteParser/Std.php

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -12,77 +12,45 @@
1212
*/
1313
class Std implements RouteParser {
1414
const VARIABLE_REGEX = <<<'REGEX'
15-
~\{
15+
\{
1616
\s* ([a-zA-Z][a-zA-Z0-9_]*) \s*
1717
(?:
1818
: \s* ([^{}]*(?:\{(?-1)\}[^{}]*)*)
1919
)?
20-
\}~x
20+
\}
2121
REGEX;
2222
const DEFAULT_DISPATCH_REGEX = '[^/]+';
2323

2424
public function parse($route) {
2525
$routeWithoutClosingOptionals = rtrim($route, ']');
2626
$numOptionals = strlen($route) - strlen($routeWithoutClosingOptionals);
27-
$routeParts = $this->parsePlaceholders($routeWithoutClosingOptionals);
28-
if ($numOptionals === 0) {
29-
return [$routeParts];
27+
28+
// Split on [ while skipping placeholders
29+
$segments = preg_split('~' . self::VARIABLE_REGEX . '(*SKIP)(*F) | \[~x', $routeWithoutClosingOptionals);
30+
if ($numOptionals !== count($segments) - 1) {
31+
throw new BadRouteException("Number of opening '[' and closing ']' does not match");
3032
}
31-
return $this->handleOptionals($routeParts, $numOptionals);
32-
}
3333

34-
private function handleOptionals($routeParts, $numOptionals) {
34+
$currentRoute = '';
3535
$routeDatas = [];
36-
$currentRouteData = [];
37-
foreach ($routeParts as $part) {
38-
// skip placeholders
39-
if (!is_string($part)) {
40-
$currentRouteData[] = $part;
41-
continue;
36+
foreach ($segments as $segment) {
37+
if ($segment === '') {
38+
throw new BadRouteException("Empty optional part");
4239
}
4340

44-
$segments = explode('[', $part);
45-
$currentNumOptionals = count($segments) - 1;
46-
$numOptionals -= $currentNumOptionals;
47-
if ($numOptionals < 0) {
48-
throw new BadRouteException("Found more opening '[' than closing ']'");
49-
}
50-
51-
$currentPart = '';
52-
foreach ($segments as $i => $addPart) {
53-
if ($addPart === '') {
54-
if ($currentPart !== '') {
55-
throw new BadRouteException("Empty optional part");
56-
}
57-
$routeDatas[] = $currentRouteData;
58-
continue;
59-
}
60-
61-
$currentPart .= $addPart;
62-
if ($i !== $currentNumOptionals) {
63-
$routeData = $currentRouteData;
64-
$routeData[] = $currentPart;
65-
$routeDatas[] = $routeData;
66-
} else {
67-
$currentRouteData[] = $currentPart;
68-
}
69-
}
70-
}
71-
72-
$routeDatas[] = $currentRouteData;
73-
if ($numOptionals > 0) {
74-
throw new BadRouteException("Found more closing ']' than opening '['");
41+
$currentRoute .= $segment;
42+
$routeDatas[] = $this->parsePlaceholders($currentRoute);
7543
}
76-
7744
return $routeDatas;
7845
}
7946

8047
/**
81-
* Parses a route string only considering {placeholders}, but ignoring [optionals].
48+
* Parses a route string that does not contain optional segments.
8249
*/
8350
private function parsePlaceholders($route) {
8451
if (!preg_match_all(
85-
self::VARIABLE_REGEX, $route, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER
52+
'~' . self::VARIABLE_REGEX . '~x', $route, $matches,
53+
PREG_OFFSET_CAPTURE | PREG_SET_ORDER
8654
)) {
8755
return [$route];
8856
}

test/RouteParser/StdTest.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,23 @@ public function provideTestParse() {
8383
['/test/', ['name', '[^/]+']],
8484
['/test/', ['name', '[^/]+'], '/', ['id', '[0-9]+']],
8585
]
86-
]
86+
],
8787
];
8888
}
8989

9090
public function provideTestParseError() {
9191
return [
92+
[
93+
'/test[opt',
94+
"Number of opening '[' and closing ']' does not match"
95+
],
9296
[
9397
'/test[opt[opt2]',
94-
"Found more opening '[' than closing ']'"
98+
"Number of opening '[' and closing ']' does not match"
9599
],
96100
[
97101
'/testopt]',
98-
"Found more closing ']' than opening '['"
102+
"Number of opening '[' and closing ']' does not match"
99103
],
100104
[
101105
'/test[]',

0 commit comments

Comments
 (0)