Skip to content

Commit 3a19c7c

Browse files
committed
added a relative_path Twig function
1 parent bbc08c1 commit 3a19c7c

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

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
*

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)