8000 [Routing] added hostname matching support to RouteCompiler · alexpott/symfony@77d01d1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 77d01d1

Browse files
committed
[Routing] added hostname matching support to RouteCompiler
1 parent 43e9d75 commit 77d01d1

File tree

2 files changed

+147
-16
lines changed

2 files changed

+147
-16
lines changed

src/Symfony/Component/Routing/RouteCompiler.php

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,75 @@ class RouteCompiler implements RouteCompilerInterface
2929
*/
3030
public function compile(Route $route)
3131
{
32+
$staticPrefix = null;
33+
$hostnameVariables = array();
34+
$pathVariables = array();
35+
$variables = array();
36+
$tokens = array();
37+
$regex = null;
38+
$hostnameRegex = null;
39+
$hostnameTokens = array();
40+
41+
if (null !== $hostnamePattern = $route->getHostnamePattern()) {
42+
43+
$result = $this->compilePattern($route, $hostnamePattern, false);
44+
45+
$hostnameVariables = $result['variables'];
46+
$variables = array_merge($variables, $hostnameVariables);
47+
48+
$hostnameTokens = $result['tokens'];
49+
$hostnameRegex = $result['regex'];
50+
}
51+
3252
$pattern = $route->getPattern();
53+
$result = $this->compilePattern($route, $pattern, true);
54+
55+
$staticPrefix = $result['staticPrefix'];
56+
57+
$pathVariables = $result['variables'];
58+
$variables = array_merge($variables, $pathVariables);
59+
60+
$tokens = $result['tokens'];
61+
$regex = $result['regex'];
62+
63+
return new CompiledRoute(
64+
$route,
65+
$staticPrefix,
66+
$regex,
67+
$tokens,
68+
array_unique($variables),
69+
$pathVariables,
70+
$hostnameVariables,
71+
$hostnameRegex,
72+
$hostnameTokens
73+
);
74+
}
75+
76+
private function compilePattern(Route $route, $pattern, $isPath)
77+
{
3378
$len = strlen($pattern);
3479
$tokens = array();
3580
$variables = array();
3681
$pos = 0;
37-
preg_match_all('#.\{([\w\d_]+)\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
82+
83+
if ($isPath) {
84+
$re = '#(?P<char>.)\{(?P<var>[\w\d_]+)\}#';
85+
} else {
86+
$re = '#(?P<char>^|.)\{(?P<var>[\w\d_]+)\}#';
87+
}
88+
89+
preg_match_all($re, $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
3890
foreach ($matches as $match) {
3991
if ($text = substr($pattern, $pos, $match[0][1] - $pos)) {
4092
$tokens[] = array('text', $text);
4193
}
42-
$seps = array($pattern[$pos]);
94+
if ($isPath) {
95+
$seps = array($pattern[$pos]);
96+
} else {
97+
$seps = array('.');
98+
}
4399
$pos = $match[0][1] + strlen($match[0][0]);
44-
$var = $match[1][0];
100+
$var = $match['var'][0];
45101

46102
if ($req = $route->getRequirement($var)) {
47103
$regexp = $req;
@@ -52,7 +108,7 @@ public function compile(Route $route)
52108
$regexp = sprintf('[^%s]+?', preg_quote(implode('', array_unique($seps)), self::REGEX_DELIMITER));
53109
}
54110

55-
$tokens[] = array('variable', $match[0][0][0], $regexp, $var);
111+
$tokens[] = array('variable', $match['char'][0], $regexp, $var);
56112

57113
if (in_array($var, $variables)) {
58114
throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $route->getPattern(), $var));
@@ -67,12 +123,14 @@ public function compile(Route $route)
67123

68124
// find the first optional token
69125
$firstOptional = INF;
70-
for ($i = count($tokens) - 1; $i >= 0; $i--) {
71-
$token = $tokens[$i];
72-
if ('variable' === $token[0] && $route->hasDefault($token[3])) {
73-
$firstOptional = $i;
74-
} else {
75-
break;
126+
if ($isPath) {
127+
for ($i = count($tokens) - 1; $i >= 0; $i--) {
128+
$token = $tokens[$i];
129+
if ('variable' === $token[0] && $route->hasDefault($token[3])) {
130+
$firstOptional = $i;
131+
} else {
132+
break;
133+
}
76134
}
77135
}
78136

@@ -82,12 +140,11 @@ public function compile(Route $route)
82140
$regexp .= $this->computeRegexp($tokens, $i, $firstOptional);
83141
}
84142

85-
return new CompiledRoute(
86-
$route,
87-
'text' === $tokens[0][0] ? $tokens[0][1] : '',
88-
self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s',
89-
array_reverse($tokens),
90-
$variables
143+
return array(
144+
'staticPrefix' => 'text' === $tokens[0][0] ? $tokens[0][1] : '',
145+
'regex' => self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s',
146+
'tokens' => array_reverse($tokens),
147+
'variables' => $variables,
91148
);
92149
}
93150

src/Symfony/Component/Routing/Tests/RouteCompilerTest.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,78 @@ public function testRouteWithSameVariableTwice()
108108

109109
$compiled = $route->compile();
110110
}
111+
112+
/**
113+
* @dataProvider provideCompileExtendedData
114+
*/
115+
public function testCompileExtended($name, $arguments, $prefix, $regex, $variables, $pathVariables, $tokens, $hostnameRegex, $hostnameVariables, $hostnameTokens)
116+
{
117+
$r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
118+
$route = $r->newInstanceArgs($arguments);
119+
120+
$compiled = $route->compile();
121+
$this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)');
122+
$this->assertEquals($regex, str_replace(array("\n", ' '), '', $compiled->getRegex()), $name.' (regex)');
123+
$this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
124+
$this->assertEquals($pathVariables, $compiled->getPathVariables(), $name.' (path variables)');
125+
$this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
126+
127+
128+
$this->assertEquals($hostnameRegex, str_replace(array("\n", ' '), '', $compiled->getHostnameRegex()), $name.' (hostname regex)');
129+
$this->assertEquals($hostnameVariables, $compiled->getHostnameVariables(), $name.' (hostname variables)');
130+
$this->assertEquals($hostnameTokens, $compiled->getHostnameTokens(), $name.' (hostname tokens)');
131+
}
132+
133+
public function provideCompileExtendedData()
134+
{
135+
return array(
136+
array(
137+
'Route with hostname pattern',
138+
array('/hello', array(), array(), array(), 'www.example.com'),
139+
'/hello', '#^/hello$#s', array(), array(), array(
140+
array('text', '/hello'),
141+
),
142+
'#^www\.example\.com$#s', array(), array(
143+
array('text', 'www.example.com'),
144+
),
145+
),
146+
array(
147+
'Route with hostname pattern and some variables',
148+
array('/hello/{name}', array(), array(), array(), 'www.example.{tld}'),
149+
'/hello', '#^/hello/(?<name>[^/]+?)$#s', array('tld', 'name'), array('name'), array(
150+
array('variable', '/', '[^/]+?', 'name'),
151+
array('text', '/hello'),
152+
),
153+
'#^www\.example\.(?<tld>[^\.]+?)$#s', array('tld'), array(
154+
array('variable', '.', '[^\.]+?', 'tld'),
155+
array('text', 'www.example'),
156+
),
157+
),
158+
array(
159+
'Route with variable at begining of hostname',
160+
array('/hello', array(), array(), array(), '{locale}.example.{tld}'),
161+
'/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
162+
array('text', '/hello'),
163+
),
164+
'#^(?<locale>[^\.]+?)\.example\.(?<tld>[^\.]+?)$#s', array('locale', 'tld'), array(
165+
array('variable', '.', '[^\.]+?', 'tld'),
166+
array('text', '.example'),
167+
array('variable', '', '[^\.]+?', 'locale'),
168+
),
169+
),
170+
array(
171+
'Route with hostname variables that has a default value',
172+
array('/hello', array('locale' => 'a', 'tld' => 'b'), array(), array(), '{locale}.example.{tld}'),
173+
'/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
174+
array('text', '/hello'),
175+
),
176+
'#^(?<locale>[^\.]+?)\.example\.(?<tld>[^\.]+?)$#s', array('locale', 'tld'), array(
177+
array('variable', '.', '[^\.]+?', 'tld'),
178+
array('text', '.example'),
179+
array('variable', '', '[^\.]+?', 'locale'),
180+
),
181+
),
182+
);
183+
}
111184
}
185+

0 commit comments

Comments
 (0)
0