8000 [Routing] added support for hostname in the apache matcher dumper · symfony/symfony@a8ce621 · GitHub
[go: up one dir, main page]

Skip to content

Commit a8ce621

Browse files
committed
[Routing] added support for hostname in the apache matcher dumper
1 parent 562174a commit a8ce621

File tree

4 files changed

+240
-10
lines changed

4 files changed

+240
-10
lines changed

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

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,55 @@ public function dump(array $options = array())
4646

4747
$rules = array("# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]");
4848
$methodVars = array();
49+
$hostnameRegexUnique = 0;
50+
$prevHosnameRegex = '';
4951

5052
foreach ($this->getRoutes()->all() as $name => $route) {
51-
$rules[] = $this->dumpRoute($name, $route, $options);
52-
$methodVars = array_merge($methodVars, $this->getRouteMethods($route));
53-
}
5453

54+
$compiledRoute = $route->compile();
55+
$hostnameRegex = $compiledRoute->getHostnameRegex();
56+
57+
if (null !== $hostnameRegex && $prevHosnameRegex !== $hostnameRegex) {
58+
59+
$prevHosnameRegex = $hostnameRegex;
60+
$hostnameRegexUnique++;
61+
62+
$rule = array();
63+
64+
$regex = $this->regexToApacheRegex($hostnameRegex);
65+
$regex = self::escape($regex, ' ', '\\');
66+
67+
$rule[] = sprintf('RewriteCond %%{HTTP:Host} %s', $regex);
68+
69+
$variables = array();
70+
$variables[] = sprintf('E=__ROUTING_hostname_%s:1', $hostnameRegexUnique);
71+
72+
foreach ($compiledRoute->getHostnameVariables() as $i => $variable) {
73+
$variables[] = sprintf('E=__ROUTING_hostname_%s_%s:%%%d', $hostnameRegexUnique, $variable, $i+1);
74+
}
75+
76+
$variables = implode(',', $variables);
77+
78+
$rule[] = sprintf('RewriteRule .? - [%s]', $variables);
79+
80+
$rules[] = implode("\n", $rule);
81+
}
82+
83+
$rules[] = $this->dumpRoute($name, $route, $options, $hostnameRegexUnique);
84+
85+
if ($req = $route->getRequirement('_method')) {
86+
$methods = explode('|', strtoupper($req));
87+
$methodVars = array_merge($methodVars, $methods);
88+
}
89+
}
5590
if (0 < count($methodVars)) {
5691
$rule = array('# 405 Method Not Allowed');
5792
$methodVars = array_values(array_unique($methodVars));
93+
if (in_array('GET', $methodVars) && !in_array('HEAD', $methodVars)) {
94+
$methodVars[] = 'HEAD';
95+
}
5896
foreach ($methodVars as $i => $methodVar) {
59-
$rule[] = sprintf('RewriteCond %%{_ROUTING_allow_%s} !-z%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
97+
$rule[] = sprintf('RewriteCond %%{ENV:_ROUTING__allow_%s} =1%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
6098
}
6199
$rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']);
62100

@@ -66,7 +104,16 @@ public function dump(array $options = array())
66104
return implode("\n\n", $rules)."\n";
67105
}
68106

69-
private function dumpRoute($name, $route, array $options)
107+
/**
108+
* Dumps a single route
109+
*
110+
* @param string $name Route name
111+
* @param Route $route The route
112+
* @param array $options Options
113+
* @param bool $hostnameRegexUnique Unique identifier for the hostname regex
114+
* @return string The compiled route
115+
*/
116+
private function dumpRoute($name, $route, array $options, $hostnameRegexUnique)
70117
{
71118
$compiledRoute = $route->compile();
72119

@@ -79,7 +126,10 @@ private function dumpRoute($name, $route, array $options)
79126
$hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex;
80127

81128
$variables = array('E=_ROUTING_route:'.$name);
82-
foreach ($compiledRoute->getVariables() as $i => $variable) {
129+
foreach ($compiledRoute->getHostnameVariables() as $variable) {
130+
$variables[] = sprintf('E=_ROUTING_param_%s:%%{ENV:__ROUTING_hostname_%s_%s}', $variable, $hostnameRegexUnique, $variable);
131+
}
132+
foreach ($compiledRoute->getPathVariables() as $i => $variable) {
83133
$variables[] = 'E=_ROUTING_param_'.$variable.':%'.($i + 1);
84134
}
85135
foreach ($route->getDefaults() as $key => $value) {
@@ -98,22 +148,35 @@ private function dumpRoute($name, $route, array $options)
98148
if (0 < count($methods)) {
99149
$allow = array();
100150
foreach ($methods as $method) {
101-
$methodVars[] = $method;
102151
$allow[] = 'E=_ROUTING_allow_'.$method.':1';
103152
}
104153

154+
if ($hostnameRegex = $compiledRoute->getHostnameRegex()) {
155+
$rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_hostname_%s} =1", $hostnameRegexUnique);
156+
}
157+
105158
$rule[] = "RewriteCond %{REQUEST_URI} $regex";
106159
$rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods));
107160
$rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
108161
}
109162

110163
// redirect with trailing slash appended
111164
if ($hasTrailingSlash) {
165+
166+
if ($hostnameRegex = $compiledRoute->getHostnameRegex()) {
167+
$rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_hostname_%s} =1", $hostnameRegexUnique);
168+
}
169+
112170
$rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$';
113171
$rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
114172
}
115173

116174
// the main rule
175+
176+
if ($hostnameRegex = $compiledRoute->getHostnameRegex()) {
177+
$rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_hostname_%s} =1", $hostnameRegexUnique);
178+
}
179+
117180
$rule[] = "RewriteCond %{REQUEST_URI} $regex";
118181
$rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]";
119182

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

Expand all lines: src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.apache
Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,96 @@ RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz6,E=_ROUTING_default_foo:bar\
6868
RewriteCond %{REQUEST_URI} ^/te\ st/baz$
6969
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz7]
7070

71+
# baz8
72+
RewriteCond %{REQUEST_URI} ^/te\\\ st/baz$
73+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz8]
74+
75+
# baz9
76+
RewriteCond %{REQUEST_URI} ^/test/(te\\\ st)$
77+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz9,E=_ROUTING_param_baz:%1]
78+
79+
RewriteCond %{HTTP:Host} ^a\.example\.com$
80+
RewriteRule .? - [E=__ROUTING_hostname_1:1]
81+
82+
# route1
83+
RewriteCond %{ENV:__ROUTING_hostname_1} =1
84+
RewriteCond %{REQUEST_URI} ^/route1$
85+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route1]
86+
87+
# route2
88+
RewriteCond %{ENV:__ROUTING_hostname_1} =1
89+
RewriteCond %{REQUEST_URI} ^/c2/route2$
90+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route2]
91+
92+
RewriteCond %{HTTP:Host} ^b\.example\.com$
93+
RewriteRule .? - [E=__ROUTING_hostname_2:1]
94+
95+
# route3
96+
RewriteCond %{ENV:__ROUTING_hostname_2} =1
97+
RewriteCond %{REQUEST_URI} ^/c2/route3$
98+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route3]
99+
100+
RewriteCond %{HTTP:Host} ^a\.example\.com$
101+
RewriteRule .? - [E=__ROUTING_hostname_3:1]
102+
103+
# route4
104+
RewriteCond %{ENV:__ROUTING_hostname_3} =1
105+
RewriteCond %{REQUEST_URI} ^/route4$
106+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route4]
107+
108+
RewriteCond %{HTTP:Host} ^c\.example\.com$
109+
RewriteRule .? - [E=__ROUTING_hostname_4:1]
110+
111+
# route5
112+
RewriteCond %{ENV:__ROUTING_hostname_4} =1
113+
RewriteCond %{REQUEST_URI} ^/route5$
114+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route5]
115+
116+
# route6
117+
RewriteCond %{REQUEST_URI} ^/route6$
118+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route6]
119+
120+
RewriteCond %{HTTP:Host} ^([^\.]++)\.example\.com$
121+
RewriteRule .? - [E=__ROUTING_hostname_5:1,E=__ROUTING_hostname_5_var1:%1]
122+
123+
# route11
124+
RewriteCond %{ENV:__ROUTING_hostname_5} =1
125+
RewriteCond %{REQUEST_URI} ^/route11$
126+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route11,E=_ROUTING_param_var1:%{ENV:__ROUTING_hostname_5_var1}]
127+
128+
# route12
129+
RewriteCond %{ENV:__ROUTING_hostname_5} =1
130+
RewriteCond %{REQUEST_URI} ^/route12$
131+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route12,E=_ROUTING_param_var1:%{ENV:__ROUTING_hostname_5_var1},E=_ROUTING_default_var1:val]
132+
133+
# route13
134+
RewriteCond %{ENV:__ROUTING_hostname_5} =1
135+
RewriteCond %{REQUEST_URI} ^/route13/([^/]++)$
136+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route13,E=_ROUTING_param_var1:%{ENV:__ROUTING_hostname_5_var1},E=_ROUTING_param_name:%1]
137+
138+
# route14
139+
RewriteCond %{ENV:__ROUTING_hostname_5} =1
140+
RewriteCond %{REQUEST_URI} ^/route14/([^/]++)$
141+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route14,E=_ROUTING_param_var1:%{ENV:__ROUTING_hostname_5_var1},E=_ROUTING_param_name:%1,E=_ROUTING_default_var1:val]
142+
143+
RewriteCond %{HTTP:Host} ^c\.example\.com$
144+
RewriteRule .? - [E=__ROUTING_hostname_6:1]
145+
146+
# route15
147+
RewriteCond %{ENV:__ROUTING_hostname_6} =1
148+
RewriteCond %{REQUEST_URI} ^/route15/([^/]++)$
149+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route15,E=_ROUTING_param_name:%1]
150+
151+
# route16
152+
RewriteCond %{REQUEST_URI} ^/route16/([^/]++)$
153+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route16,E=_ROUTING_param_name:%1,E=_ROUTING_default_var1:val]
154+
155+
# route17
156+
RewriteCond %{REQUEST_URI} ^/route17$
157+
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route17]
158+
71159
# 405 Method Not Allowed
72-
RewriteCond %{_ROUTING_allow_GET} !-z [OR]
73-
RewriteCond %{_ROUTING_allow_HEAD} !-z [OR]
74-
RewriteCond %{_ROUTING_allow_POST} !-z
160+
RewriteCond %{ENV:_ROUTING__allow_GET} =1 [OR]
161+
RewriteCond %{ENV:_ROUTING__allow_HEAD} =1 [OR]
162+
RewriteCond %{ENV:_ROUTING__allow_POST} =1
75163
RewriteRule .* app.php [QSA,L]

src/Symfony/Component/Routing/Tests/Matcher/ApacheUrlMatcherTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,20 @@ public function getMatchData()
118118
'_route' => 'hello',
119119
),
120120
),
121+
array(
122+
'REDIRECT_REDIRECT_ envs',
123+
'/hello/world',
124+
array(
125+
'REDIRECT_REDIRECT__ROUTING_route' => 'hello',
126+
'REDIRECT_REDIRECT__ROUTING_param__controller' => 'AcmeBundle:Default:index',
127+
'REDIRECT_REDIRECT__ROUTING_param_name' => 'world',
128+
),
129+
array(
130+
'_controller' => 'AcmeBundle:Default:index',
131+
'name' => 'world',
132+
'_route' => 'hello',
133+
),
134+
),
121135
);
122136
}
123137
}

src/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,71 @@ private function getRouteCollection()
124124
$collection->add('baz7', new Route(
125125
'/te st/baz'
126126
));
127+
// space preceded with \ in path
128+
$collection->add('baz8', new Route(
129+
'/te\\ st/baz'
130+
));
131+
// space preceded with \ in requirement
132+
$collection->add('baz9', new Route(
133+
'/test/{baz}',
134+
array(),
135+
array(
136+
'baz' => 'te\\\\ st',
137+
)
138+
));
139+
140+
$collection1 = new RouteCollection();
141+
142+
$route1 = new Route('/route1', array(), array(), array(), 'a.example.com');
143+
$collection1->add('route1', $route1);
144+
145+
$collection2 = new RouteCollection();
146+
147+
$route2 = new Route('/route2', array(), array(), array(), 'a.example.com');
148+
$collection2->add('route2', $route2);
149+
150+
$route3 = new Route('/route3', array(), array(), array(), 'b.example.com');
151+
$collection2->add('route3', $route3);
152+
153+
$collection1->addCollection($collection2, '/c2');
154+
155+
$route4 = new Route('/route4', array(), array(), array(), 'a.example.com');
156+
$collection1->add('route4', $route4);
157+
158+
$route5 = new Route('/route5', array(), array(), array(), 'c.example.com');
159+
$collection1->add('route5', $route5);
160+
161+
$route6 = new Route('/route6', array(), array(), array(), null);
162+
$collection1->add('route6', $route6);
163+
164+
$collection->addCollection($collection1);
165+
166+
// hostname and variables
167+
168+
$collection1 = new RouteCollection();
169+
170+
$route11 = new Route('/route11', array(), array(), array(), '{var1}.example.com');
171+
$collection1->add('route11', $route11);
172+
173+
$route12 = new Route('/route12', array('var1' => 'val'), array(), array(), '{var1}.example.com');
174+
$collection1->add('route12', $route12);
175+
176+
$route13 = new Route('/route13/{name}', array(), array(), array(), '{var1}.example.com');
177+
$collection1->add('route13', $route13);
178+
179+
$rou A4D3 te14 = new Route('/route14/{name}', array('var1' => 'val'), array(), array(), '{var1}.example.com');
180+
$collection1->add('route14', $route14);
181+
182+
$route15 = new Route('/route15/{name}', array(), array(), array(), 'c.example.com');
183+
$collection1->add('route15', $route15);
184+
185+
$route16 = new Route('/route16/{name}', array('var1' => 'val'), array(), array(), null);
186+
$collection1->add('route16', $route16);
187+
188+
$route17 = new Route('/route17', array(), array(), array(), null);
189+
$collection1->add('route17', $route17);
190+
191+
$collection->addCollection($collection1);
127192

128193
return $collection;
129194
}

0 commit comments

Comments
 (0)
0