Skip to content

Commit 3c59f97

Browse files
giosh94mhznicolas-grekas
authored andcommitted
UrlHelper is now aware of RequestContext changes
RequestContext in routing can change at runtime after the UrlHelper has been created, so using a RequestContextAwareInterface instance (i.e. the router) will workaround URL generation issues.
1 parent c437f39 commit 3c59f97

File tree

2 files changed

+63
-11
lines changed

2 files changed

+63
-11
lines changed

Tests/UrlHelperTest.php

+36
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\HttpFoundation\RequestStack;
1717
use Symfony\Component\HttpFoundation\UrlHelper;
1818
use Symfony\Component\Routing\RequestContext;
19+
use Symfony\Component\Routing\RequestContextAwareInterface;
1920

2021
class UrlHelperTest extends TestCase
2122
{
@@ -64,11 +65,46 @@ public function testGenerateAbsoluteUrlWithRequestContext($path, $baseUrl, $host
6465
}
6566

6667
$requestContext = new RequestContext($baseUrl, 'GET', $host, $scheme, $httpPort, $httpsPort, $path);
68+
6769
$helper = new UrlHelper(new RequestStack(), $requestContext);
6870

6971
$this->assertEquals($expected, $helper->getAbsoluteUrl($path));
7072
}
7173

74+
/**
75+
* @dataProvider getGenerateAbsoluteUrlRequestContextData
76+
*/
77+
public function testGenerateAbsoluteUrlWithRequestContextAwareInterface($path, $baseUrl, $host, $scheme, $httpPort, $httpsPort, $expected)
78+
{
79+
if (!class_exists(RequestContext::class)) {
80+
$this->markTestSkipped('The Routing component is needed to run tests that depend on its request context.');
81+
}
82+
83+
$requestContext = new RequestContext($baseUrl, 'GET', $host, $scheme, $httpPort, $httpsPort, $path);
84+
$contextAware = new class($requestContext) implements RequestContextAwareInterface {
85+
private $requestContext;
86+
87+
public function __construct($requestContext)
88+
{
89+
$this->requestContext = $requestContext;
90+
}
91+
92+
public function setContext(RequestContext $context)
93+
{
94+
$this->requestContext = $context;
95+
}
96+
97+
public function getContext()
98+
{
99+
return $this->requestContext;
100+
}
101+
};
102+
103+
$helper = new UrlHelper(new RequestStack(), $contextAware);
104+
105+
$this->assertEquals($expected, $helper->getAbsoluteUrl($path));
106+
}
107+
72108
/**
73109
* @dataProvider getGenerateAbsoluteUrlRequestContextData
74110
*/

UrlHelper.php

+27-11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpFoundation;
1313

1414
use Symfony\Component\Routing\RequestContext;
15+
use Symfony\Component\Routing\RequestContextAwareInterface;
1516

1617
/**
1718
* A helper service for manipulating URLs within and outside the request scope.
@@ -23,8 +24,15 @@ final class UrlHelper
2324
private $requestStack;
2425
private $requestContext;
2526

26-
public function __construct(RequestStack $requestStack, RequestContext $requestContext = null)
27+
/**
28+
* @param RequestContextAwareInterface|RequestContext|null $requestContext
29+
*/
30+
public function __construct(RequestStack $requestStack, $requestContext = null)
2731
{
32+
if (null !== $requestContext && !$requestContext instanceof RequestContext && !$requestContext instanceof RequestContextAwareInterface) {
33+
throw new \TypeError(__METHOD__.': Argument #2 ($requestContext) must of type Symfony\Component\Routing\RequestContextAwareInterface|Symfony\Component\Routing\RequestContext|null, '.get_debug_type($requestContext).' given.');
34+
}
35+
2836
$this->requestStack = $requestStack;
2937
$this->requestContext = $requestContext;
3038
}
@@ -73,28 +81,36 @@ public function getRelativePath(string $path): string
7381

7482
private function getAbsoluteUrlFromContext(string $path): string
7583
{
76-
if (null === $this->requestContext || '' === $host = $this->requestContext->getHost()) {
84+
if (null === $context = $this->requestContext) {
85+
return $path;
86+
}
87+
88+
if ($context instanceof RequestContextAwareInterface) {
89+
$context = $context->getContext();
90+
}
91+
92+
if ('' === $host = $context->getHost()) {
7793
return $path;
7894
}
7995

80-
$scheme = $this->requestContext->getScheme();
96+
$scheme = $context->getScheme();
8197
$port = '';
8298

83-
if ('http' === $scheme && 80 !== $this->requestContext->getHttpPort()) {
84-
$port = ':'.$this->requestContext->getHttpPort();
85-
} elseif ('https' === $scheme && 443 !== $this->requestContext->getHttpsPort()) {
86-
$port = ':'.$this->requestContext->getHttpsPort();
99+
if ('http' === $scheme && 80 !== $context->getHttpPort()) {
100+
$port = ':'.$context->getHttpPort();
101+
} elseif ('https' === $scheme && 443 !== $context->getHttpsPort()) {
102+
$port = ':'.$context->getHttpsPort();
87103
}
88104

89105
if ('#' === $path[0]) {
90-
$queryString = $this->requestContext->getQueryString();
91-
$path = $this->requestContext->getPathInfo().($queryString ? '?'.$queryString : '').$path;
106+
$queryString = $context->getQueryString();
107+
$path = $context->getPathInfo().($queryString ? '?'.$queryString : '').$path;
92108
} elseif ('?' === $path[0]) {
93-
$path = $this->requestContext->getPathInfo().$path;
109+
$path = $context->getPathInfo().$path;
94110
}
95111

96112
if ('/' !== $path[0]) {
97-
$path = rtrim($this->requestContext->getBaseUrl(), '/').'/'.$path;
113+
$path = rtrim($context->getBaseUrl(), '/').'/'.$path;
98114
}
99115

100116
return $scheme.'://'.$host.$port.$path;

0 commit comments

Comments
 (0)