Skip to content

Commit 63f37d2

Browse files
committed
Merge pull request #163 from symfony-cmf/use-context-to-improve-request-reconstruction
use the request context to rebuild the request on matching
2 parents ff71a6c + 7d7098a commit 63f37d2

File tree

3 files changed

+105
-1
lines changed

3 files changed

+105
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Changelog
22
=========
33

4+
* **2016-01-09**: When ChainRouter::match is used with a RequestMatcher, the
5+
Request is now properly rebuilt from the RequestContext if that was set on
6+
the ChainRouter.
47
* **2014-09-29**: ChainRouter does not require a RouterInterface, as a
58
RequestMatcher and UrlGenerator is fine too. Fixed chain router interface to
69
not force a RouterInterface.

ChainRouter.php

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,12 @@ private function doMatch($url, Request $request = null)
180180
// matching requests is more powerful than matching URLs only, so try that first
181181
if ($router instanceof RequestMatcherInterface) {
182182
if (empty($requestForMatching)) {
183-
$requestForMatching = Request::create($url);
183+
$requestForMatching = $this->rebuildRequest($url);
184184
}
185185

186186
return $router->matchRequest($requestForMatching);
187187
}
188+
188189
// every router implements the match method
189190
return $router->match($url);
190191
} catch (ResourceNotFoundException $e) {
@@ -249,6 +250,51 @@ public function generate($name, $parameters = array(), $absolute = UrlGeneratorI
249250
throw new RouteNotFoundException(sprintf('None of the chained routers were able to generate route: %s', $info));
250251
}
251252

253+
/**
254+
* Rebuild the request object from a URL with the help of the RequestContext.
255+
*
256+
* If the request context is not set, this simply returns the request object built from $uri.
257+
*
258+
* @param string $uri
259+
*
260+
* @return Request
261+
*/
262+
private function rebuildRequest($uri)
263+
{
264+
if (!$this->context) {
265+
return Request::create($uri);
266+
}
267+
268+
$server = array();
269+
if ($this->context->getHost()) {
270+
$server['SERVER_NAME'] = $this->context->getHost();
271+
$server['HTTP_HOST'] = $this->context->getHost();
272+
}
273+
if ($this->context->getBaseUrl()) {
274+
$uri = $this->context->getBaseUrl().$uri;
275+
$server['SCRIPT_FILENAME'] = $this->context->getBaseUrl();
276+
$server['PHP_SELF'] = $this->context->getBaseUrl();
277+
}
278+
if ('https' === $this->context->getScheme()) {
279+
$server['HTTPS'] = 'on';
280+
$server['SERVER_PORT'] = $this->context->getHttpsPort();
281+
if (443 !== $this->context->getHttpsPort()) {
282+
// this is parsed from the host by symfony request
283+
// https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Request.php#L971
284+
$server['HTTP_HOST'] .= ':'.$this->context->getHttpsPort();
285+
}
286+
} else {
287+
$server['SERVER_PORT'] = $this->context->getHttpPort();
288+
if (80 !== $this->context->getHttpPort()) {
289+
// this is parsed from the host by symfony request
290+
// https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Request.php#L971
291+
$server['HTTP_HOST'] .= ':'.$this->context->getHttpPort();
292+
}
293+
}
294+
295+
return Request::create($uri, $this->context->getMethod(), $this->context->getParameters(), array(), array(), $server);
296+
}
297+
252298
private function getErrorMessage($name, $router = null, $parameters = null)
253299
{
254300
if ($router instanceof VersatileGeneratorInterface) {

Tests/Routing/ChainRouterTest.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,61 @@ public function testMatchWithRequestMatchers()
309309
$this->assertEquals(array('test'), $result);
310310
}
311311

312+
public function provideBaseUrl()
313+
{
314+
return array(
315+
array(''),
316+
array('/web'),
317+
);
318+
}
319+
320+
/**
321+
* Call match on ChainRouter that has RequestMatcher in the chain.
322+
*
323+
* @dataProvider provideBaseUrl
324+
*/
325+
public function testMatchWithRequestMatchersAndContext($baseUrl)
326+
{
327+
$url = '/test';
328+
329+
list($low) = $this->createRouterMocks();
330+
331+
$high = $this->getMock('Symfony\Cmf\Component\Routing\Tests\Routing\RequestMatcher');
332+
333+
$high
334+
->expects($this->once())
335+
->method('matchRequest')
336+
->with($this->callback(function (Request $r) use ($url, $baseUrl) {
337+
return true === $r->isSecure()
338+
&& 'foobar.com' === $r->getHost()
339+
&& 4433 === $r->getPort()
340+
&& $baseUrl === $r->getBaseUrl()
341+
&& $url === $r->getPathInfo()
342+
;
343+
}))
344+
->will($this->throwException(new \Symfony\Component\Routing\Exception\ResourceNotFoundException()))
345+
;
346+
$low
347+
->expects($this->once())
348+
->method('match')
349+
->with($url)
350+
->will($this->returnValue(array('test')))
351+
;
352+
353+
$this->router->add($low, 10);
354+
$this->router->add($high, 20);
355+
356+
$requestContext = new RequestContext();
357+
$requestContext->setScheme('https');
358+
$requestContext->setHost('foobar.com');
359+
$requestContext->setHttpsPort(4433);
360+
$requestContext->setBaseUrl($baseUrl);
361+
$this->router->setContext($requestContext);
362+
363+
$result = $this->router->match($url);
364+
$this->assertEquals(array('test'), $result);
365+
}
366+
312367
/**
313368
* If there is a method not allowed but another router matches, that one is used.
314369
*/

0 commit comments

Comments
 (0)