8000 added a relative_path Twig function · symfony/symfony@0ec852d · GitHub
[go: up one dir, main page]

Skip to content

Commit 0ec852d

Browse files
committed
added a relative_path Twig function
1 parent ee27ed8 commit 0ec852d

File tree

5 files changed

+189
-3
lines changed

5 files changed

+189
-3
lines changed

src/Symfony/Bridge/Twig/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ CHANGELOG
44
2.7.0
55
-----
66

7-
* added an HttpFoundation extension (provides the `absolute_url` function)
7+
* added an HttpFoundation extension (provides the `absolute_url` and the `relative_path` functions)
88

99
2.5.0
1010
-----

src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
< 8000 div class="diff-text-inner color-fg-muted">@@ -12,7 +12,7 @@
1212
namespace Symfony\Bridge\Twig\Extension;
1313

1414
use Symfony\Component\HttpFoundation\RequestStack;
15-
use Symfony\Component\Asset\Packages;
15+
use Symfony\Component\HttpFoundation\Request;
1616

1717
/**
1818
* Twig extension for the Symfony HttpFoundation component.
@@ -35,17 +35,20 @@ public function getFunctions()
3535
{
3636
return array(
3737
new \Twig_SimpleFunction('absolute_url', array($this, 'generateAbsoluteUrl')),
38+
new \Twig_SimpleFunction('relative_path', array($this, 'generateRelativePath')),
3839
);
3940
}
4041

4142
/**
42-
* Returns the absolute URL for the given path.
43+
* Returns the absolute URL for the given absolute or relative path.
4344
*
4445
* This method returns the path unchanged if no request is available.
4546
*
4647
* @param string $path The path
4748
*
4849
* @return string The absolute URL
50+
*
51+
* @see Request::getUriForPath()
4952
*/
5053
public function generateAbsoluteUrl($path)
5154
{
@@ -57,9 +60,43 @@ public function generateAbsoluteUrl($path)
5760
return $path;
5861
}
5962

63+
if (!$path || '/' !== $path[0]) {
64+
$prefix = $request->getPathInfo();
65+
$last = strlen($prefix) - 1;
66+
if ($last !== $pos = strrpos($prefix, '/')) {
67+
$prefix = substr($prefix, 0, $pos).'/';
68+
}
69+
70+
$path = $prefix.$path;
71+
}
72+
6073
return $request->getUriForPath($path);
6174
}
6275

76+
/**
77+
* Returns a relative path based on the current Request.
78+
*
79+
* This method returns the path unchanged if no request is available.
80+
*
81+
* @param string $path The path
82+
*
83+
* @return string The relative path
84+
*
85+
* @see Request::getRelativeUriForPath()
86+
*/
87+
public function generateRelativePath($path)
88+
{
89+
if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
90+
return $path;
91+
}
92+
93+
if (!$request = $this->requestStack->getMasterRequest()) {
94+
return $path;
95+
}
96+
97+
return $request->getRelativeUriForPath($path);
98+
}
99+
63100
/**
64101
* Returns the name of the extension.
65102
*
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Twig\Tests\Extension;
13+
14+
use Symfony\Bridge\Twig\Extension\HttpFoundationExtension;
15+
use Symfony\Component\HttpFoundation\RequestStack;
16+
use Symfony\Component\HttpFoundation\Request;
17+
18+
class HttpFoundationExtensionTest extends \PHPUnit_Framework_TestCase
19+
{
20+
/**
21+
* @dataProvider getGenerateAbsoluteUrlData()
22+
*/
23+
public function testGenerateAbsoluteUrl($expected, $path, $pathinfo)
24+
{
25+
$stack = new RequestStack();
26+
$stack->push(Request::create($pathinfo));
27+
$extension = new HttpFoundationExtension($stack);
28+
29+
$this->assertEquals($expected, $extension->generateAbsoluteUrl($path));
30+
}
31+
32+
public function getGenerateAbsoluteUrlData()
33+
{
34+
return array(
35+
array('http://localhost/foo.png', '/foo.png', '/foo/bar.html 10000 9;),
36+
array('http://localhost/foo/foo.png', 'foo.png', '/foo/bar.html'),
37+
array('http://localhost/foo/foo.png', 'foo.png', '/foo/bar'),
38+
array('http://localhost/foo/bar/foo.png', 'foo.png', '/foo/bar/'),
39+
40+
array('http://example.com/baz', 'http://example.com/baz', '/'),
41+
array('https://example.com/baz', 'https://example.com/baz', '/'),
42+
array('//example.com/baz', '//example.com/baz', '/'),
43+
);
44+
}
45+
46+
/**
47+
* @dataProvider getGenerateRelativePathData()
48+
*/
49+
public function testGenerateRelativePath($expected, $path, $pathinfo)
50+
{
51+
if (!method_exists('Symfony\Component\HttpFoundation\Request', 'getRelativeUriForPath')) {
52+
$this->markTestSkipped('Your version of Symfony HttpFoundation is too old.');
53+
}
54+
55+
$stack = new RequestStack();
56+
$stack->push(Request::create($pathinfo));
57+
$extension = new HttpFoundationExtension($stack);
58+
59+
$this->assertEquals($expected, $extension->generateRelativePath($path));
60+
}
61+
62+
public function getGenerateRelativePathData()
63+
{
64+
return array(
65+
array('../foo.png', '/foo.png', '/foo/bar.html'),
66+
array('../baz/foo.png', '/baz/foo.png', '/foo/bar.html'),
67+
array('baz/foo.png', 'baz/foo.png', '/foo/bar.html'),
68+
69+
array('http://example.com/baz', 'http://example.com/baz', '/'),
70+
array('https://example.com/baz', 'https://example.com/baz', '/'),
71+
array('//example.com/baz', '//example.com/baz', '/'),
72+
);
73+
}
74+
}

src/Symfony/Component/HttpFoundation/Request.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,61 @@ public function getUriForPath($path)
11371137
return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path;
11381138
}
11391139

1140+
/**
1141+
* Returns the path as relative reference from the current Request path.
1142+
*
1143+
* Only the URIs path component (no schema, host etc.) is relevant and must be given.
1144+
* Both paths must be absolute and not contain relative parts.
1145+
* Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
1146+
* Furthermore, they can be used to reduce the link size in documents.
1147+
*
1148+
* Example target paths, given a base path of "/a/b/c/d":
1149+
* - "/a/b/c/d" -> ""
1150+
* - "/a/b/c/" -> "./"
1151+
* - "/a/b/" -> "../"
1152+
* - "/a/b/c/other" -> "other"
1153+
* - "/a/x/y" -> "../../x/y"
1154+
*
1155+
* @param string $path The target path
1156+
*
1157+
* @return string The relative target path
1158+
*/
1159+
public function getRelativeUriForPath($path)
1160+
{
1161+
// be sure that we are dealing with an absolute path
1162+
if (!isset($path[0]) || '/' !== $path[0]) {
1163+
return $path;
1164+
}
1165+
1166+
if ($path === $basePath = $this->getPathInfo()) {
1167+
return '';
1168+
}
1169+
1170+
$sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
1171+
$targetDirs = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path);
1172+
array_pop($sourceDirs);
1173+
$targetFile = array_pop($targetDirs);
1174+
1175+
foreach ($sourceDirs as $i => $dir) {
1176+
if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
1177+
unset($sourceDirs[$i], $targetDirs[$i]);
1178+
} else {
1179+
break;
1180+
}
1181+
}
1182+
1183+
$targetDirs[] = $targetFile;
1184+
$path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs);
1185+
1186+
// A reference to the same base directory or an empty subdirectory must be prefixed with "./".
1187+
// This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
1188+
// as the first segment of a relative-path reference, as it would be mistaken for a scheme name
1189+
// (see http://tools.ietf.org/html/rfc3986#section-4.2).
1190+
return !isset($path[0]) || '/' === $path[0]
1191+
|| false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
1192+
? "./$path" : $path;
1193+
}
1194+
11401195
/**
11411196
* Generates the normalized query string for the Request.
11421197
*

src/Symfony/Component/HttpFoundation/Tests/RequestTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,26 @@ public function testGetUriForPath()
575575
$this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'));
576576
}
577577

578+
/**
579+
* @dataProvider getRelativeUriForPathData()
580+
*/
581+
public function testGetRelativeUriForPath($expected, $pathinfo, $path)
582+
{
583+
$this->assertEquals($expected, Request::create($pathinfo)->getRelativeUriForPath($path));
584+
}
585+
586+
public function getRelativeUriForPathData()
587+
{
588+
return array(
589+
array('me.png', '/foo', '/me.png'),
590+
array('../me.png', '/foo/bar', '/me.png'),
591+
array('me.png', '/foo/bar', '/foo/me.png'),
592+
array('../baz/me.png', '/foo/bar/b', '/foo/baz/me.png'),
593+
array('../../fooz/baz/me.png', '/foo/bar/b', '/fooz/baz/me.png'),
594+
array('baz/me.png', '/foo/bar/b', 'baz/me.png'),
595+
);
596+
}
597+
578598
/**
579599
* @covers Symfony\Component\HttpFoundation\Request::getUserInfo
580600
*/

0 commit comments

Comments
 (0)
0