[Routing] UrlHelper to get absolute URL for a path
This commit is contained in:
parent
65b46a532c
commit
388d8f548c
|
@ -145,6 +145,13 @@ Security
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
TwigBridge
|
||||||
|
==========
|
||||||
|
|
||||||
|
* deprecated the `$requestStack` and `$requestContext` arguments of the
|
||||||
|
`HttpFoundationExtension`, pass a `Symfony\Component\HttpFoundation\UrlHelper`
|
||||||
|
instance as the only argument instead
|
||||||
|
|
||||||
Workflow
|
Workflow
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
|
@ -364,6 +364,13 @@ TwigBundle
|
||||||
* The default value (`false`) of the `twig.strict_variables` configuration option has been changed to `%kernel.debug%`.
|
* The default value (`false`) of the `twig.strict_variables` configuration option has been changed to `%kernel.debug%`.
|
||||||
* The `transchoice` tag and filter have been removed, use the `trans` ones instead with a `%count%` parameter.
|
* The `transchoice` tag and filter have been removed, use the `trans` ones instead with a `%count%` parameter.
|
||||||
* Removed support for legacy templates directories `src/Resources/views/` and `src/Resources/<BundleName>/views/`, use `templates/` and `templates/bundles/<BundleName>/` instead.
|
* Removed support for legacy templates directories `src/Resources/views/` and `src/Resources/<BundleName>/views/`, use `templates/` and `templates/bundles/<BundleName>/` instead.
|
||||||
|
|
||||||
|
TwigBridge
|
||||||
|
----------
|
||||||
|
|
||||||
|
* removed the `$requestStack` and `$requestContext` arguments of the
|
||||||
|
`HttpFoundationExtension`, pass a `Symfony\Component\HttpFoundation\UrlHelper`
|
||||||
|
instance as the only argument instead
|
||||||
|
|
||||||
Validator
|
Validator
|
||||||
--------
|
--------
|
||||||
|
|
|
@ -6,6 +6,9 @@ CHANGELOG
|
||||||
|
|
||||||
* added the `form_parent()` function that allows to reliably retrieve the parent form in Twig templates
|
* added the `form_parent()` function that allows to reliably retrieve the parent form in Twig templates
|
||||||
* added the `workflow_transition_blockers()` function
|
* added the `workflow_transition_blockers()` function
|
||||||
|
* deprecated the `$requestStack` and `$requestContext` arguments of the
|
||||||
|
`HttpFoundationExtension`, pass a `Symfony\Component\HttpFoundation\UrlHelper`
|
||||||
|
instance as the only argument instead
|
||||||
|
|
||||||
4.2.0
|
4.2.0
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace Symfony\Bridge\Twig\Extension;
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
use Symfony\Component\HttpFoundation\UrlHelper;
|
||||||
use Symfony\Component\Routing\RequestContext;
|
use Symfony\Component\Routing\RequestContext;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
@ -24,13 +25,34 @@ use Twig\TwigFunction;
|
||||||
*/
|
*/
|
||||||
class HttpFoundationExtension extends AbstractExtension
|
class HttpFoundationExtension extends AbstractExtension
|
||||||
{
|
{
|
||||||
private $requestStack;
|
private $urlHelper;
|
||||||
private $requestContext;
|
|
||||||
|
|
||||||
public function __construct(RequestStack $requestStack, RequestContext $requestContext = null)
|
/**
|
||||||
|
* @param UrlHelper $urlHelper
|
||||||
|
*/
|
||||||
|
public function __construct($urlHelper)
|
||||||
{
|
{
|
||||||
$this->requestStack = $requestStack;
|
if ($urlHelper instanceof UrlHelper) {
|
||||||
$this->requestContext = $requestContext;
|
$this->urlHelper = $urlHelper;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$urlHelper instanceof RequestStack) {
|
||||||
|
throw new \TypeError(sprintf('The first argument must be an instance of "%s" or an instance of "%s".', UrlHelper::class, RequestStack::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@trigger_error(sprintf('Passing a "%s" instance as the first argument to the "%s" constructor is deprecated since Symfony 4.3, pass a "%s" instance instead.', RequestStack::class, __CLASS__, UrlHelper::class), E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
$requestContext = null;
|
||||||
|
if (2 === \func_num_args()) {
|
||||||
|
$requestContext = \func_get_arg(1);
|
||||||
|
if (!$requestContext instanceof RequestContext) {
|
||||||
|
throw new \TypeError(sprintf('The second argument must be an instance of "%s".', RequestContext::class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->urlHelper = new UrlHelper($urlHelper, $requestContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,55 +79,7 @@ class HttpFoundationExtension extends AbstractExtension
|
||||||
*/
|
*/
|
||||||
public function generateAbsoluteUrl($path)
|
public function generateAbsoluteUrl($path)
|
||||||
{
|
{
|
||||||
if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
|
return $this->urlHelper->getAbsoluteUrl($path);
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$request = $this->requestStack->getMasterRequest()) {
|
|
||||||
if (null !== $this->requestContext && '' !== $host = $this->requestContext->getHost()) {
|
|
||||||
$scheme = $this->requestContext->getScheme();
|
|
||||||
$port = '';
|
|
||||||
|
|
||||||
if ('http' === $scheme && 80 != $this->requestContext->getHttpPort()) {
|
|
||||||
$port = ':'.$this->requestContext->getHttpPort();
|
|
||||||
} elseif ('https' === $scheme && 443 != $this->requestContext->getHttpsPort()) {
|
|
||||||
$port = ':'.$this->requestContext->getHttpsPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('#' === $path[0]) {
|
|
||||||
$queryString = $this->requestContext->getQueryString();
|
|
||||||
$path = $this->requestContext->getPathInfo().($queryString ? '?'.$queryString : '').$path;
|
|
||||||
} elseif ('?' === $path[0]) {
|
|
||||||
$path = $this->requestContext->getPathInfo().$path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('/' !== $path[0]) {
|
|
||||||
$path = rtrim($this->requestContext->getBaseUrl(), '/').'/'.$path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $scheme.'://'.$host.$port.$path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('#' === $path[0]) {
|
|
||||||
$path = $request->getRequestUri().$path;
|
|
||||||
} elseif ('?' === $path[0]) {
|
|
||||||
$path = $request->getPathInfo().$path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$path || '/' !== $path[0]) {
|
|
||||||
$prefix = $request->getPathInfo();
|
|
||||||
$last = \strlen($prefix) - 1;
|
|
||||||
if ($last !== $pos = strrpos($prefix, '/')) {
|
|
||||||
$prefix = substr($prefix, 0, $pos).'/';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $request->getUriForPath($prefix.$path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $request->getSchemeAndHttpHost().$path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,15 +95,7 @@ class HttpFoundationExtension extends AbstractExtension
|
||||||
*/
|
*/
|
||||||
public function generateRelativePath($path)
|
public function generateRelativePath($path)
|
||||||
{
|
{
|
||||||
if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
|
return $this->urlHelper->getRelativePath($path);
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$request = $this->requestStack->getMasterRequest()) {
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $request->getRelativeUriForPath($path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,6 +17,9 @@ use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
use Symfony\Component\Routing\RequestContext;
|
use Symfony\Component\Routing\RequestContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group legacy
|
||||||
|
*/
|
||||||
class HttpFoundationExtensionTest extends TestCase
|
class HttpFoundationExtensionTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
"symfony/dependency-injection": "~3.4|~4.0",
|
"symfony/dependency-injection": "~3.4|~4.0",
|
||||||
"symfony/finder": "~3.4|~4.0",
|
"symfony/finder": "~3.4|~4.0",
|
||||||
"symfony/form": "^4.3",
|
"symfony/form": "^4.3",
|
||||||
"symfony/http-foundation": "~3.4|~4.0",
|
"symfony/http-foundation": "~4.3",
|
||||||
"symfony/http-kernel": "~3.4|~4.0",
|
"symfony/http-kernel": "~3.4|~4.0",
|
||||||
"symfony/mime": "~4.3",
|
"symfony/mime": "~4.3",
|
||||||
"symfony/polyfill-intl-icu": "~1.0",
|
"symfony/polyfill-intl-icu": "~1.0",
|
||||||
|
@ -46,6 +46,7 @@
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"symfony/console": "<3.4",
|
"symfony/console": "<3.4",
|
||||||
"symfony/form": "<4.3",
|
"symfony/form": "<4.3",
|
||||||
|
"symfony/http-foundation": "<4.3",
|
||||||
"symfony/translation": "<4.2",
|
"symfony/translation": "<4.2",
|
||||||
"symfony/workflow": "<4.3"
|
"symfony/workflow": "<4.3"
|
||||||
},
|
},
|
||||||
|
|
|
@ -63,6 +63,12 @@
|
||||||
<service id="request_stack" class="Symfony\Component\HttpFoundation\RequestStack" public="true" />
|
<service id="request_stack" class="Symfony\Component\HttpFoundation\RequestStack" public="true" />
|
||||||
<service id="Symfony\Component\HttpFoundation\RequestStack" alias="request_stack" />
|
<service id="Symfony\Component\HttpFoundation\RequestStack" alias="request_stack" />
|
||||||
|
|
||||||
|
<service id="url_helper" class="Symfony\Component\HttpFoundation\UrlHelper">
|
||||||
|
<argument type="service" id="request_stack" />
|
||||||
|
<argument type="service" id="router.request_context" on-invalid="ignore" />
|
||||||
|
</service>
|
||||||
|
<service id="Symfony\Component\HttpFoundation\UrlHelper" alias="url_helper" />
|
||||||
|
|
||||||
<service id="cache_warmer" class="Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate" public="true">
|
<service id="cache_warmer" class="Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate" public="true">
|
||||||
<argument type="tagged" tag="kernel.cache_warmer" />
|
<argument type="tagged" tag="kernel.cache_warmer" />
|
||||||
<argument>%kernel.debug%</argument>
|
<argument>%kernel.debug%</argument>
|
||||||
|
|
|
@ -108,8 +108,7 @@
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="twig.extension.httpfoundation" class="Symfony\Bridge\Twig\Extension\HttpFoundationExtension">
|
<service id="twig.extension.httpfoundation" class="Symfony\Bridge\Twig\Extension\HttpFoundationExtension">
|
||||||
<argument type="service" id="request_stack" />
|
<argument type="service" id="url_helper" />
|
||||||
<argument type="service" id="router.request_context" on-invalid="ignore" />
|
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="twig.extension.debug" class="Twig\Extension\DebugExtension" />
|
<service id="twig.extension.debug" class="Twig\Extension\DebugExtension" />
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.1.3",
|
"php": "^7.1.3",
|
||||||
"symfony/config": "~4.2",
|
"symfony/config": "~4.2",
|
||||||
"symfony/twig-bridge": "^4.2",
|
"symfony/twig-bridge": "^4.3",
|
||||||
"symfony/http-foundation": "~4.1",
|
"symfony/http-foundation": "~4.3",
|
||||||
"symfony/http-kernel": "~4.1",
|
"symfony/http-kernel": "~4.1",
|
||||||
"symfony/polyfill-ctype": "~1.8",
|
"symfony/polyfill-ctype": "~1.8",
|
||||||
"twig/twig": "~1.34|~2.4"
|
"twig/twig": "~1.34|~2.4"
|
||||||
|
|
|
@ -10,6 +10,7 @@ CHANGELOG
|
||||||
* deprecated `MimeType` and `MimeTypeExtensionGuesser` in favor of `Symfony\Component\Mime\MimeTypes`.
|
* deprecated `MimeType` and `MimeTypeExtensionGuesser` in favor of `Symfony\Component\Mime\MimeTypes`.
|
||||||
* deprecated `FileBinaryMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileBinaryMimeTypeGuesser`.
|
* deprecated `FileBinaryMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileBinaryMimeTypeGuesser`.
|
||||||
* deprecated `FileinfoMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileinfoMimeTypeGuesser`.
|
* deprecated `FileinfoMimeTypeGuesser` in favor of `Symfony\Component\Mime\FileinfoMimeTypeGuesser`.
|
||||||
|
* added `UrlHelper` that allows to get an absolute URL and a relative path for a given path
|
||||||
|
|
||||||
4.2.0
|
4.2.0
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\HttpFoundation\Tests;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
use Symfony\Component\HttpFoundation\UrlHelper;
|
||||||
|
use Symfony\Component\Routing\RequestContext;
|
||||||
|
|
||||||
|
class UrlHelperTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider getGenerateAbsoluteUrlData()
|
||||||
|
*/
|
||||||
|
public function testGenerateAbsoluteUrl($expected, $path, $pathinfo)
|
||||||
|
{
|
||||||
|
$stack = new RequestStack();
|
||||||
|
$stack->push(Request::create($pathinfo));
|
||||||
|
$helper = new UrlHelper($stack);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $helper->getAbsoluteUrl($path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGenerateAbsoluteUrlData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['http://localhost/foo.png', '/foo.png', '/foo/bar.html'],
|
||||||
|
['http://localhost/foo/foo.png', 'foo.png', '/foo/bar.html'],
|
||||||
|
['http://localhost/foo/foo.png', 'foo.png', '/foo/bar'],
|
||||||
|
['http://localhost/foo/bar/foo.png', 'foo.png', '/foo/bar/'],
|
||||||
|
|
||||||
|
['http://example.com/baz', 'http://example.com/baz', '/'],
|
||||||
|
['https://example.com/baz', 'https://example.com/baz', '/'],
|
||||||
|
['//example.com/baz', '//example.com/baz', '/'],
|
||||||
|
|
||||||
|
['http://localhost/foo/bar?baz', '?baz', '/foo/bar'],
|
||||||
|
['http://localhost/foo/bar?baz=1', '?baz=1', '/foo/bar?foo=1'],
|
||||||
|
['http://localhost/foo/baz?baz=1', 'baz?baz=1', '/foo/bar?foo=1'],
|
||||||
|
|
||||||
|
['http://localhost/foo/bar#baz', '#baz', '/foo/bar'],
|
||||||
|
['http://localhost/foo/bar?0#baz', '#baz', '/foo/bar?0'],
|
||||||
|
['http://localhost/foo/bar?baz=1#baz', '?baz=1#baz', '/foo/bar?foo=1'],
|
||||||
|
['http://localhost/foo/baz?baz=1#baz', 'baz?baz=1#baz', '/foo/bar?foo=1'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getGenerateAbsoluteUrlRequestContextData
|
||||||
|
*/
|
||||||
|
public function testGenerateAbsoluteUrlWithRequestContext($path, $baseUrl, $host, $scheme, $httpPort, $httpsPort, $expected)
|
||||||
|
{
|
||||||
|
if (!class_exists('Symfony\Component\Routing\RequestContext')) {
|
||||||
|
$this->markTestSkipped('The Routing component is needed to run tests that depend on its request context.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestContext = new RequestContext($baseUrl, 'GET', $host, $scheme, $httpPort, $httpsPort, $path);
|
||||||
|
$helper = new UrlHelper(new RequestStack(), $requestContext);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $helper->getAbsoluteUrl($path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getGenerateAbsoluteUrlRequestContextData
|
||||||
|
*/
|
||||||
|
public function testGenerateAbsoluteUrlWithoutRequestAndRequestContext($path)
|
||||||
|
{
|
||||||
|
if (!class_exists('Symfony\Component\Routing\RequestContext')) {
|
||||||
|
$this->markTestSkipped('The Routing component is needed to run tests that depend on its request context.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$helper = new UrlHelper(new RequestStack());
|
||||||
|
|
||||||
|
$this->assertEquals($path, $helper->getAbsoluteUrl($path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGenerateAbsoluteUrlRequestContextData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['/foo.png', '/foo', 'localhost', 'http', 80, 443, 'http://localhost/foo.png'],
|
||||||
|
['foo.png', '/foo', 'localhost', 'http', 80, 443, 'http://localhost/foo/foo.png'],
|
||||||
|
['foo.png', '/foo/bar/', 'localhost', 'http', 80, 443, 'http://localhost/foo/bar/foo.png'],
|
||||||
|
['/foo.png', '/foo', 'localhost', 'https', 80, 443, 'https://localhost/foo.png'],
|
||||||
|
['foo.png', '/foo', 'localhost', 'https', 80, 443, 'https://localhost/foo/foo.png'],
|
||||||
|
['foo.png', '/foo/bar/', 'localhost', 'https', 80, 443, 'https://localhost/foo/bar/foo.png'],
|
||||||
|
['/foo.png', '/foo', 'localhost', 'http', 443, 80, 'http://localhost:443/foo.png'],
|
||||||
|
['/foo.png', '/foo', 'localhost', 'https', 443, 80, 'https://localhost:80/foo.png'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGenerateAbsoluteUrlWithScriptFileName()
|
||||||
|
{
|
||||||
|
$request = Request::create('http://localhost/app/web/app_dev.php');
|
||||||
|
$request->server->set('SCRIPT_FILENAME', '/var/www/app/web/app_dev.php');
|
||||||
|
|
||||||
|
$stack = new RequestStack();
|
||||||
|
$stack->push($request);
|
||||||
|
$helper = new UrlHelper($stack);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
'http://localhost/app/web/bundles/framework/css/structure.css',
|
||||||
|
$helper->getAbsoluteUrl('/app/web/bundles/framework/css/structure.css')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getGenerateRelativePathData()
|
||||||
|
*/
|
||||||
|
public function testGenerateRelativePath($expected, $path, $pathinfo)
|
||||||
|
{
|
||||||
|
if (!method_exists('Symfony\Component\HttpFoundation\Request', 'getRelativeUriForPath')) {
|
||||||
|
$this->markTestSkipped('Your version of Symfony HttpFoundation is too old.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$stack = new RequestStack();
|
||||||
|
$stack->push(Request::create($pathinfo));
|
||||||
|
$urlHelper = new UrlHelper($stack);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $urlHelper->getRelativePath($path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGenerateRelativePathData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['../foo.png', '/foo.png', '/foo/bar.html'],
|
||||||
|
['../baz/foo.png', '/baz/foo.png', '/foo/bar.html'],
|
||||||
|
['baz/foo.png', 'baz/foo.png', '/foo/bar.html'],
|
||||||
|
|
||||||
|
['http://example.com/baz', 'http://example.com/baz', '/'],
|
||||||
|
['https://example.com/baz', 'https://example.com/baz', '/'],
|
||||||
|
['//example.com/baz', '//example.com/baz', '/'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\HttpFoundation;
|
||||||
|
|
||||||
|
use Symfony\Component\Routing\RequestContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper service for manipulating URLs within and outside the request scope.
|
||||||
|
*
|
||||||
|
* @author Valentin Udaltsov <udaltsov.valentin@gmail.com>
|
||||||
|
*/
|
||||||
|
final class UrlHelper
|
||||||
|
{
|
||||||
|
private $requestStack;
|
||||||
|
private $requestContext;
|
||||||
|
|
||||||
|
public function __construct(RequestStack $requestStack, ?RequestContext $requestContext = null)
|
||||||
|
{
|
||||||
|
$this->requestStack = $requestStack;
|
||||||
|
$this->requestContext = $requestContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAbsoluteUrl(string $path): string
|
||||||
|
{
|
||||||
|
if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $request = $this->requestStack->getMasterRequest()) {
|
||||||
|
return $this->getAbsoluteUrlFromContext($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('#' === $path[0]) {
|
||||||
|
$path = $request->getRequestUri().$path;
|
||||||
|
} elseif ('?' === $path[0]) {
|
||||||
|
$path = $request->getPathInfo().$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$path || '/' !== $path[0]) {
|
||||||
|
$prefix = $request->getPathInfo();
|
||||||
|
$last = \strlen($prefix) - 1;
|
||||||
|
if ($last !== $pos = strrpos($prefix, '/')) {
|
||||||
|
$prefix = substr($prefix, 0, $pos).'/';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request->getUriForPath($prefix.$path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request->getSchemeAndHttpHost().$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRelativePath(string $path): string
|
||||||
|
{
|
||||||
|
if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $request = $this->requestStack->getMasterRequest()) {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $request->getRelativeUriForPath($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getAbsoluteUrlFromContext(string $path): string
|
||||||
|
{
|
||||||
|
if (null === $this->requestContext || '' === $host = $this->requestContext->getHost()) {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scheme = $this->requestContext->getScheme();
|
||||||
|
$port = '';
|
||||||
|
|
||||||
|
if ('http' === $scheme && 80 !== $this->requestContext->getHttpPort()) {
|
||||||
|
$port = ':'.$this->requestContext->getHttpPort();
|
||||||
|
} elseif ('https' === $scheme && 443 !== $this->requestContext->getHttpsPort()) {
|
||||||
|
$port = ':'.$this->requestContext->getHttpsPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('#' === $path[0]) {
|
||||||
|
$queryString = $this->requestContext->getQueryString();
|
||||||
|
$path = $this->requestContext->getPathInfo().($queryString ? '?'.$queryString : '').$path;
|
||||||
|
} elseif ('?' === $path[0]) {
|
||||||
|
$path = $this->requestContext->getPathInfo().$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('/' !== $path[0]) {
|
||||||
|
$path = rtrim($this->requestContext->getBaseUrl(), '/').'/'.$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $scheme.'://'.$host.$port.$path;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue