8000 [Routing] added hostname matching support to PhpMatcherDumper · symfony/symfony@85d11af · GitHub
[go: up one dir, main page]

Skip to content

Commit 85d11af

Browse files
committed
[Routing] added hostname matching support to PhpMatcherDumper
1 parent 402359b commit 85d11af

File tree

6 files changed

+435
-25
lines changed

6 files changed

+435
-25
lines changed

src/Symfony/Component/Routing/Matcher/Dumper/DumperCollection.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,52 @@ protected function setParent(DumperCollection $parent)
9898
{
9999
$this->parent = $parent;
100100
}
101+
102+
/**
103+
* Returns true if the attribute is defined
104+
*
105+
* @param string $name The attribute name
106+
* @return Boolean true if the attribute is defined, false otherwise
107+
*/
108+
public function hasAttribute($name)
109+
{
110+
return array_key_exists($name, $this->attributes);
111+
}
112+
113+
/**
114+
* Returns an attribute by name
115+
*
116+
* @param string $name The attribute name
117+
* @param mixed $default Default value is the attribute doesn't exist
118+
* @return mixed The attribute value
119+
*/
120+
public function getAttribute($name, $default = null)
121+
{
122+
if ($this->hasAttribute($name)) {
123+
return $this->attributes[$name];
124+
} else {
125+
return $default;
126+
}
127+
}
128+
129+
/**
130+
* Sets an attribute by name
131+
*
132+
* @param string $name The attribute name
133+
* @param mixed $value The attribute value
134+
*/
135+
public function setAttribute($name, $value)
136+
{
137+
$this->attributes[$name] = $value;
138+
}
139+
140+
/**
141+
* Sets multiple attributes
142+
*
143+
* @param array $attributes The attributes
144+
*/
145+
public function setAttributes($attributes)
146+
{
147+
$this->attributes = $attributes;
148+
}
101149
}

src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php

Lines changed: 91 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,37 @@ public function match(\$pathinfo)
109109
*/
110110
private function compileRoutes(RouteCollection $routes, $supportsRedirections)
111111
{
112-
$collection = $this->flattenRouteCollection($routes);
113-
$tree = $this->buildPrefixTree($collection);
112+
$fetchedHostname = false;
114113

115-
return $this->compilePrefixRoutes($tree, $supportsRedirections);
114+
$routes = $this->flattenRouteCollection($routes);
115+
$groups = $this->groupRoutesByHostnameRegex($routes);
116+
$code = '';
117+
118+
foreach ($groups as $collection) {
119+
if (null !== $regex = $collection->getAttribute('hostname_regex')) {
120+
121+
if (!$fetchedHostname) {
122+
$code .= " \$hostname = \$this->context->getHost();\n\n";
123+
$fetchedHostname = true;
124+
}
125+
126+
$code .= sprintf(" if (preg_match(%s, \$hostname, \$hostnameMatches)) {\n", var_export($regex, true));
127+
}
128+
129+
$tree = $this->buildPrefixTree($collection);
130+
$groupCode = $this->compilePrefixRoutes($tree, $supportsRedirections);
131+
132+
if (null !== $regex) {
133+
// apply extra indention at each line (except empty ones)
134+
$groupCode = preg_replace('/^.{2,}$/m', ' $0', $groupCode);
135+
$code .= $groupCode;
136+
$code .= " }\n\n";
137+
} else {
138+
$code .= $groupCode;
139+
}
140+
}
141+
142+
return $code;
116143
}
117144

118145
/**
@@ -171,6 +198,7 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren
171198
$conditions = array();
172199
$hasTrailingSlash = false;
173200
$matches = false;
201+
$hostnameMatches = false;
174202
$methods = array();
175203

176204
if ($req = $route->getRequirement('_method')) {
@@ -183,7 +211,7 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren
183211

184212
$supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods));
185213

186-
if (!count($compiledRoute->getVariables()) && false !== preg_match('#^(.)\^(?<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
214+
if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
187215
if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
188216
$conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
189217
$hasTrailingSlash = true;
@@ -205,6 +233,10 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren
205233
$matches = true;
206234
}
207235

236+
if ($compiledRoute->getHostnameVariables()) {
237+
$hostnameMatches = true;
238+
}
239+
208240
$conditions = implode(' && ', $conditions);
209241

210242
$code .= <<<EOF
@@ -263,10 +295,30 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren
263295
}
264296

265297
// optimize parameters array
266-
if (true === $matches && $route->getDefaults()) {
267-
$code .= sprintf(" return array_merge(\$this->mergeDefaults(\$matches, %s), array('_route' => '%s'));\n"
268-
, str_replace("\n", '', var_export($route->getDefaults(), true)), $name);
269-
} elseif (true === $matches) {
298+
if (($matches || $hostnameMatches) && $route->getDefaults()) {
299+
300+
$vars = array();
301+
if ($matches) {
302+
$vars[] = '$matches';
303+
}
304+
if ($hostnameMatches) {
305+
$vars[] = '$hostnameMatches';
306+
}
307+
$matchesExpr = implode(' + ', $vars);
308+
309+
$code .= sprintf(" return array_merge(\$this->mergeDefaults(%s, %s), array('_route' => '%s'));\n"
310+
, $matchesExpr, str_replace("\n", '', var_export($route->getDefaults(), true)), $name);
311+
312+
} elseif ($matches || $hostnameMatches) {
313+
314+
if (!$matches) {
315+
$code .= " \$matches = \$hostnameMatches;\n";
316+
} else {
317+
if ($hostnameMatches) {
318+
$code .= " \$matches = \$matches + \$hostnameMatches;\n";
319+
}
320+
}
321+
270322
$code .= sprintf(" \$matches['_route'] = '%s';\n\n", $name);
271323
$code .= " return \$matches;\n";
272324
} elseif ($route->getDefaults()) {
@@ -308,6 +360,37 @@ private function flattenRouteCollection(RouteCollection $routes, DumperCollectio
308360
return $to;
309361
}
310362

363+
/**
364+
* Groups consecutive routes having the same hostname regex
365+
*
366+
* The results is a collection of collections of routes having the same
367+
* hostnameRegex
368+
*
369+
* @param DumperCollection $routes Flat collection of DumperRoutes
370+
*
371+
* @return DumperCollection A collection with routes grouped by hostname regex in sub-collections
372+
*/
373+
private function groupRoutesByHostnameRegex(DumperCollection $routes)
374+
{
375+
$groups = new DumperCollection();
376+
377+
$currentGroup = new DumperCollection();
378+
$currentGroup->setAttribute('hostname_regex', null);
379+
$groups->add($currentGroup);
380+
381+
foreach ($routes as $route) {
382+
$hostnameRegex = $route->getRoute()->compile()->getHostnameRegex();
383+
if ($currentGroup->getAttribute('hostname_regex') !== $hostnameRegex) {
384+
$currentGroup = new DumperCollection();
385+
$currentGroup->setAttribute('hostname_regex', $hostnameRegex);
386+
$groups->add($currentGroup);
387+
}
388+
$currentGroup->add($route);
389+
}
390+
391+
return $groups;
392+
}
393+
311394
/**
312395
* Organizes the routes into a prefix tree.
313396
*
@@ -331,5 +414,4 @@ private function buildPrefixTree(DumperCollection $collection)
331414

332415
return $tree;
333416
}
334-
335417
}

src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php

Lines changed: 107 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,22 +206,121 @@ public function match($pathinfo)
206206
return $matches;
207207
}
208208

209-
if (0 === strpos($pathinfo, '/a')) {
210-
if (0 === strpos($pathinfo, '/aba')) {
211-
// ababa
212-
if ($pathinfo === '/ababa') {
213-
return array('_route' => 'ababa');
209+
if (0 === strpos($pathinfo, '/aba')) {
210+
// ababa
211+
if ($pathinfo === '/ababa') {
212+
return array('_route' => 'ababa');
213+
}
214+
215+
// foo4
216+
if (preg_match('#^/aba/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
217+
$matches['_route'] = 'foo4';
218+
219+
return $matches;
220+
}
221+
222+
}
223+
224+
$hostname = $this->context->getHost();
225+
226+
if (preg_match('#^a\\.example\\.com$#s', $hostname, $hostnameMatches)) {
227+
// route1
228+
if ($pathinfo === '/route1') {
229+
return array('_route' => 'route1');
230+
}
231+
232+
// route2
233+
if ($pathinfo === '/c2/route2') {
234+
return array('_route' => 'route2');
235+
}
236+
237+
}
238+
239+
if (preg_match('#^b\\.example\\.com$#s', $hostname, $hostnameMatches)) {
240+
// route3
241+
if ($pathinfo === '/c2/route3') {
242+
return array('_route' => 'route3');
243+
}
244+
245+
}
246+
247+
if (preg_match('#^a\\.example\\.com$#s', $hostname, $hostnameMatches)) {
248+
// route4
249+
if ($pathinfo === '/route4') {
250+
return array('_route' => 'route4');
251+
}
252+
253+
}
254+
255+
if (preg_match('#^c\\.example\\.com$#s', $hostname, $hostnameMatches)) {
256+
// route5
257+
if ($pathinfo === '/route5') {
258+
return array('_route' => 'route5');
259+
}
260+
261+
}
262+
263+
// route6
264+
if ($pathinfo === '/route6') {
265+
return array('_route' => 'route6');
266+
}
267+
268+
if (preg_match('#^(?<var1>[^\\.]++)\\.example\\.com$#s', $hostname, $hostnameMatches)) {
269+
if (0 === strpos($pathinfo, '/route1')) {
270+
// route11
271+
if ($pathinfo === '/route11') {
272+
$matches = $hostnameMatches;
273+
$matches['_route'] = 'route11';
274+
275+
return $matches;
214276
}
215277

216-
// foo4
217-
if (preg_match('#^/aba/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
218-
$matches['_route'] = 'foo4';
278+
// route12
279+
if ($pathinfo === '/route12') {
280+
return array_merge($this->mergeDefaults($hostnameMatches, array ( 'var1' => 'val',)), array('_route' => 'route12'));
281+
}
282+
283+
// route13
284+
if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
285+
$matches = $matches + $hostnameMatches;
286+
$matches['_route'] = 'route13';
219287

220288
return $matches;
221289
}
222290

291+
// route14
292+
if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
293+
return array_merge($this->mergeDefaults($matches + $hostnameMatches, array ( 'var1' => 'val',)), array('_route' => 'route14'));
294+
}
295+
223296
}
224297

298+
}
299+
300+
if (preg_match('#^c\\.example\\.com$#s', $hostname, $hostnameMatches)) {
301+
// route15
302+
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
303+
$matches['_route'] = 'route15';
304+
305+
return $matches;
306+
}
307+
308+
}
309+
310+
if (0 === strpos($pathinfo, '/route1')) {
311+
// route16
312+
if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
313+
return array_merge($this->mergeDefaults($matches, array ( 'var1' => 'val',)), array('_route' => 'route16'));
314+
}
315+
316+
// route17
317+
if ($pathinfo === '/route17') {
318+
return array('_route' => 'route17');
319+
}
320+
321+
}
322+
323+
if (0 === strpos($pathinfo, '/a')) {
225324
// a
226325
if ($pathinfo === '/a/a...') {
227326
return array('_route' => 'a');

0 commit comments

Comments
 (0)
0