diff --git a/src/Symfony/Bundle/TwigBundle/Extension/AssetsExtension.php b/src/Symfony/Bundle/TwigBundle/Extension/AssetsExtension.php
index 20834d7fa207..9dd6a2b96314 100644
--- a/src/Symfony/Bundle/TwigBundle/Extension/AssetsExtension.php
+++ b/src/Symfony/Bundle/TwigBundle/Extension/AssetsExtension.php
@@ -12,6 +12,7 @@
namespace Symfony\Bundle\TwigBundle\Extension;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\Routing\RequestContext;
/**
* Twig extension for Symfony assets helper
@@ -21,10 +22,12 @@
class AssetsExtension extends \Twig_Extension
{
private $container;
+ private $context;
- public function __construct(ContainerInterface $container)
+ public function __construct(ContainerInterface $container, RequestContext $requestContext)
{
$this->container = $container;
+ $this->context = $requestContext;
}
/**
@@ -45,14 +48,21 @@ public function getFunctions()
*
* Absolute paths (i.e. http://...) are returned unmodified.
*
- * @param string $path A public path
- * @param string $packageName The name of the asset package to use
+ * @param string $path A public path
+ * @param string $packageName The name of the asset package to use
+ * @param Boolean $absolute Whether to return an absolute URL or a relative one
*
* @return string A public path which takes into account the base path and URL path
*/
- public function getAssetUrl($path, $packageName = null)
+ public function getAssetUrl($path, $packageName = null, $absolute = false)
{
- return $this->container->get('templating.helper.assets')->getUrl($path, $packageName);
+ $url = $this->container->get('templating.helper.assets')->getUrl($path, $packageName);
+
+ if (!$absolute) {
+ return $url;
+ }
+
+ return $this->ensureUrlIsAbsolute($url);
}
/**
@@ -76,4 +86,33 @@ public function getName()
{
return 'assets';
}
+
+ /**
+ * Ensures an URL is absolute, if possible.
+ *
+ * @param string $url The URL that has to be absolute
+ *
+ * @return string The absolute URL
+ */
+ private function ensureUrlIsAbsolute($url)
+ {
+ if (false !== strpos($url, '://') || 0 === strpos($url, '//')) {
+ return $url;
+ }
+
+ if ('' === $host = $this->context->getHost()) {
+ return $url;
+ }
+
+ $scheme = $this->context->getScheme();
+ $port = '';
+
+ if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
+ $port = ':'.$this->context->getHttpPort();
+ } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) {
+ $port = ':'.$this->context->getHttpsPort();
+ }
+
+ return $scheme.'://'.$host.$port.$url;
+ }
}
diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml
index 5efbe9d6d4f4..945bd876a07a 100644
--- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml
+++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml
@@ -66,6 +66,7 @@
+
diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Extension/AssetsExtensionTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Extension/AssetsExtensionTest.php
new file mode 100644
index 000000000000..5652af57dd23
--- /dev/null
+++ b/src/Symfony/Bundle/TwigBundle/Tests/Extension/AssetsExtensionTest.php
@@ -0,0 +1,106 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\TwigBundle\Tests\Extension;
+
+use Symfony\Bundle\TwigBundle\Extension\AssetsExtension;
+use Symfony\Bundle\TwigBundle\Tests\TestCase;
+use Symfony\Component\Routing\RequestContext;
+
+class AssetsExtensionTest extends TestCase
+{
+ /**
+ * @dataProvider provideGetGetAssetUrlArguments
+ */
+ public function testGetAssetUrl($path, $packageName, $absolute, $relativeUrl, $expectedUrl, $scheme, $host, $httpPort, $httpsPort)
+ {
+ $helper = $this->createHelperMock($path, $packageName, $relativeUrl);
+ $container = $this->createContainerMock($helper);
+
+ $context = $this->createRequestContextMock($scheme, $host, $httpPort, $httpsPort);
+
+ $extension = new AssetsExtension($container, $context);
+ $this->assertEquals($expectedUrl, $extension->getAssetUrl($path, $packageName, $absolute));
+ }
+
+ public function testGetAssetWithtoutHost()
+ {
+ $path = '/path/to/asset';
+ $packageName = null;
+ $relativeUrl = '/bundle-name/path/to/asset';
+
+ $helper = $this->createHelperMock($path, $packageName, $relativeUrl);
+ $container = $this->createContainerMock($helper);
+
+ $context = $this->createRequestContextMock('http', '', 80, 443);
+
+ $extension = new AssetsExtension($container, $context);
+ $this->assertEquals($relativeUrl, $extension->getAssetUrl($path, $packageName, true));
+ }
+
+ public function provideGetGetAssetUrlArguments()
+ {
+ return array(
+ array('/path/to/asset', 'package-name', false, '/bundle-name/path/to/asset', '/bundle-name/path/to/asset', 'http', 'symfony.com', 80, null),
+ array('/path/to/asset', 'package-name', false, 'http://subdomain.symfony.com/bundle-name/path/to/asset', 'http://subdomain.symfony.com/bundle-name/path/to/asset', 'http', 'symfony.com', 80, null),
+ array('/path/to/asset', null, false, '/bundle-name/path/to/asset', '/bundle-name/path/to/asset', 'http', 'symfony.com', 80, null),
+ array('/path/to/asset', 'package-name', true, '/bundle-name/path/to/asset', 'http://symfony.com/bundle-name/path/to/asset', 'http', 'symfony.com', 80, null),
+ array('/path/to/asset', 'package-name', true, 'http://subdomain.symfony.com/bundle-name/path/to/asset', 'http://subdomain.symfony.com/bundle-name/path/to/asset', 'http', 'symfony.com', 80, null),
+ array('/path/to/asset', null, true, '/bundle-name/path/to/asset', 'https://symfony.com:92/bundle-name/path/to/asset', 'https', 'symfony.com', null, 92),
+ array('/path/to/asset', null, true, '/bundle-name/path/to/asset', 'http://symfony.com:660/bundle-name/path/to/asset', 'http', 'symfony.com', 660, null),
+ );
+ }
+
+ private function createRequestContextMock($scheme, $host, $httpPort, $httpsPort)
+ {
+ $context = $this->getMockBuilder('Symfony\Component\Routing\RequestContext')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $context->expects($this->any())
+ ->method('getScheme')
+ ->will($this->returnValue($scheme));
+ $context->expects($this->any())
+ ->method('getHost')
+ ->will($this->returnValue($host));
+ $context->expects($this->any())
+ ->method('getHttpPort')
+ ->will($this->returnValue($httpPort));
+ $context->expects($this->any())
+ ->method('getHttpsPort')
+ ->will($this->returnValue($httpsPort));
+
+ return $context;
+ }
+
+ private function createContainerMock($helper)
+ {
+ $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
+ $container->expects($this->any())
+ ->method('get')
+ ->with('templating.helper.assets')
+ ->will($this->returnValue($helper));
+
+ return $container;
+ }
+
+ private function createHelperMock($path, $packageName, $returnValue)
+ {
+ $helper = $this->getMockBuilder('Symfony\Component\Templating\Helper\CoreAssetsHelper')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $helper->expects($this->any())
+ ->method('getUrl')
+ ->with($path, $packageName)
+ ->will($this->returnValue($returnValue));
+
+ return $helper;
+ }
+}
diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json
index 88d866989ad2..49e32a193e09 100644
--- a/src/Symfony/Bundle/TwigBundle/composer.json
+++ b/src/Symfony/Bundle/TwigBundle/composer.json
@@ -23,7 +23,9 @@
"require-dev": {
"symfony/stopwatch": "~2.2",
"symfony/dependency-injection": "~2.0",
- "symfony/config": "~2.2"
+ "symfony/config": "~2.2",
+ "symfony/routing": "~2.1",
+ "symfony/templating": "~2.1"
},
"autoload": {
"psr-0": { "Symfony\\Bundle\\TwigBundle\\": "" }