relativas - Obteniendo ruta relativa de ruta absoluta en PHP
rutas relativas en php (10)
¿Camino relativo? Esto parece más un camino de viaje. Parece que quieres saber la ruta que recorres para ir de la ruta A a la ruta B. Si ese es el caso, puedes explode $ a y $ b en ''/''
luego hacer un bucle inverso a través de $ aParts, comparándolos con $ bParts of el mismo índice hasta que se encuentre el directorio "denominador común" (registrando el número de bucles en el camino). Luego cree una cadena vacía y agregue ''../''
a ella $ numLoops-1 veces y luego agregue a esa $ b menos el directorio del denominador común.
Noté algunas preguntas similares sobre este problema cuando escribí el título, pero no parecen estar en PHP. Entonces, ¿cuál es la solución a esto con una función de PHP?
Para ser especificado.
$a="/home/apache/a/a.php";
$b="/home/root/b/b.php";
$relpath = getRelativePath($a,$b); //needed function,should return ''../../root/b/b.php''
¿Alguna buena idea? Gracias.
Algunos motivos por los que Gordon''s no funcionó para mí ... Aquí está mi solución
function getRelativePath($from, $to) {
$patha = explode(''/'', $from);
$pathb = explode(''/'', $to);
$start_point = count(array_intersect($patha,$pathb));
while($start_point--) {
array_shift($patha);
array_shift($pathb);
}
$output = "";
if(($back_count = count($patha))) {
while($back_count--) {
$output .= "../";
}
} else {
$output .= ''./'';
}
return $output . implode(''/'', $pathb);
}
Basado en la función de Gordon, mi solución es la siguiente:
function getRelativePath($from, $to)
{
$from = explode(''/'', $from);
$to = explode(''/'', $to);
foreach($from as $depth => $dir)
{
if(isset($to[$depth]))
{
if($dir === $to[$depth])
{
unset($to[$depth]);
unset($from[$depth]);
}
else
{
break;
}
}
}
//$rawresult = implode(''/'', $to);
for($i=0;$i<count($from)-1;$i++)
{
array_unshift($to,''..'');
}
$result = implode(''/'', $to);
return $result;
}
Como hemos tenido varias respuestas, decidí probarlas todas y compararlas. Utilicé estos caminos para probar:
$from = "/var/www/sites/web/mainroot/webapp/folder/sub/subf/subfo/subfol/subfold/lastfolder/";
NOTA: si es una carpeta, debe colocar una barra diagonal para que las funciones funcionen correctamente. Entonces, __DIR__
no funcionará. Utilice __FILE__
en __FILE__
lugar o __DIR__ . ''/''
__DIR__ . ''/''
$to = "/var/www/sites/web/mainroot/webapp/folder/aaa/bbb/ccc/ddd";
RESULTADOS: (el separador decimal es una coma, el separador de mil es un punto)
- Función de Gordon: resultado CORRECTO , tiempo para 100,000 ejecs 1,222 segundos
- Función de Young: resultado CORRECTO , tiempo para 100,000 ejecs 1,540 segundos
- Función de Ceagle: resultado INCORRECTO (funciona con algunas rutas pero falla con otras, como las que se usaron en las pruebas y las escritas arriba)
- Función de Loranger: resultado INCORRECTO (funciona con algunas rutas pero falla con otras, como las que se usaron en las pruebas y que están escritas arriba)
¡Así que te sugiero que uses la implementación de Gordon! (El marcado como respuesta)
El de Young también es bueno y se desempeña mejor con estructuras de directorios simples (como "a / b / c.php"), mientras que el de Gordon se desempeña mejor con estructuras complejas, con muchos subdirectorios (como los que se usan en este punto de referencia).
NOTA: Escribo aquí debajo de los resultados devueltos con $from
y $to
como entradas, para que pueda verificar que 2 de ellos están bien, mientras que otros 2 están equivocados:
- Gordon:
../../../../../../aaa/bbb/ccc/ddd
-> CORRECTO - Joven:
../../../../../../aaa/bbb/ccc/ddd
-> CORRECTO - Ceagle:
../../../../../../bbb/ccc/ddd
-> WRONG - Loranger:
../../../../../aaa/bbb/ccc/ddd
-> WRONG
Este código está tomado del generador de URL de Symfony https://github.com/symfony/Routing/blob/master/Generator/UrlGenerator.php
/**
* Returns the target path as relative reference from the base path.
*
* Only the URIs path component (no schema, host etc.) is relevant and must be given, starting with a slash.
* Both paths must be absolute and not contain relative parts.
* Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
* Furthermore, they can be used to reduce the link size in documents.
*
* Example target paths, given a base path of "/a/b/c/d":
* - "/a/b/c/d" -> ""
* - "/a/b/c/" -> "./"
* - "/a/b/" -> "../"
* - "/a/b/c/other" -> "other"
* - "/a/x/y" -> "../../x/y"
*
* @param string $basePath The base path
* @param string $targetPath The target path
*
* @return string The relative target path
*/
function getRelativePath($basePath, $targetPath)
{
if ($basePath === $targetPath) {
return '''';
}
$sourceDirs = explode(''/'', isset($basePath[0]) && ''/'' === $basePath[0] ? substr($basePath, 1) : $basePath);
$targetDirs = explode(''/'', isset($targetPath[0]) && ''/'' === $targetPath[0] ? substr($targetPath, 1) : $targetPath);
array_pop($sourceDirs);
$targetFile = array_pop($targetDirs);
foreach ($sourceDirs as $i => $dir) {
if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
unset($sourceDirs[$i], $targetDirs[$i]);
} else {
break;
}
}
$targetDirs[] = $targetFile;
$path = str_repeat(''../'', count($sourceDirs)).implode(''/'', $targetDirs);
// A reference to the same base directory or an empty subdirectory must be prefixed with "./".
// This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
// as the first segment of a relative-path reference, as it would be mistaken for a scheme name
// (see http://tools.ietf.org/html/rfc3986#section-4.2).
return '''' === $path || ''/'' === $path[0]
|| false !== ($colonPos = strpos($path, '':'')) && ($colonPos < ($slashPos = strpos($path, ''/'')) || false === $slashPos)
? "./$path" : $path;
}
Esto es lo que funciona para mí. Por alguna razón desconocida, la respuesta más votada a esta pregunta no funcionó como se esperaba
public function getRelativePath($absolutePathFrom, $absolutePathDestination)
{
$absolutePathFrom = is_dir($absolutePathFrom) ? rtrim($absolutePathFrom, "//")."/" : $absolutePathFrom;
$absolutePathDestination = is_dir($absolutePathDestination) ? rtrim($absolutePathDestination, "//")."/" : $absolutePathDestination;
$absolutePathFrom = explode("/", str_replace("//", "/", $absolutePathFrom));
$absolutePathDestination = explode("/", str_replace("//", "/", $absolutePathDestination));
$relativePath = "";
$path = array();
$_key = 0;
foreach($absolutePathFrom as $key => $value)
{
if (strtolower($value) != strtolower($absolutePathDestination[$key]))
{
$_key = $key + 1;
for ($i = $key; $i < count($absolutePathDestination); $i++)
{
$path[] = $absolutePathDestination[$i];
}
break;
}
}
for ($i = 0; $i <= (count($absolutePathFrom) - $_key - 1); $i++)
{
$relativePath .= "../";
}
return $relativePath.implode("/", $path);
}
if $a = "C:/xampp/htdocs/projects/SMS/App/www/App/index.php"
y
$b = "C:/xampp/htdocs/projects/SMS/App/www/App/bin/bootstrap/css/bootstrap.min.css"
Entonces $c
, que es la ruta relativa de $b
desde $a
, será
$c = getRelativePath($a, $b) = "bin/bootstrap/css/bootstrap.min.css"
Llegué al mismo resultado usando esas manipulaciones de matrices:
function getRelativePath($path, $from = __FILE__ )
{
$path = explode(DIRECTORY_SEPARATOR, $path);
$from = explode(DIRECTORY_SEPARATOR, dirname($from.''.''));
$common = array_intersect_assoc($path, $from);
$base = array(''.'');
if ( $pre_fill = count( array_diff_assoc($from, $common) ) ) {
$base = array_fill(0, $pre_fill, ''..'');
}
$path = array_merge( $base, array_diff_assoc($path, $common) );
return implode(DIRECTORY_SEPARATOR, $path);
}
El segundo argumento es el archivo al que se refiere la ruta. Es opcional para que pueda obtener la ruta relativa independientemente de la página web que esté actualmente. Para usarlo con el ejemplo @Young o @Gordon, ya que desea conocer la ruta relativa a $ b desde $ a, deberá usar
getRelativePath($b, $a);
Prueba este:
function getRelativePath($from, $to)
{
// some compatibility fixes for Windows paths
$from = is_dir($from) ? rtrim($from, ''//'') . ''/'' : $from;
$to = is_dir($to) ? rtrim($to, ''//'') . ''/'' : $to;
$from = str_replace(''//', ''/'', $from);
$to = str_replace(''//', ''/'', $to);
$from = explode(''/'', $from);
$to = explode(''/'', $to);
$relPath = $to;
foreach($from as $depth => $dir) {
// find first non-matching dir
if($dir === $to[$depth]) {
// ignore this directory
array_shift($relPath);
} else {
// get number of remaining dirs to $from
$remaining = count($from) - $depth;
if($remaining > 1) {
// add traversals up to first matching dir
$padLength = (count($relPath) + $remaining - 1) * -1;
$relPath = array_pad($relPath, $padLength, ''..'');
break;
} else {
$relPath[0] = ''./'' . $relPath[0];
}
}
}
return implode(''/'', $relPath);
}
Esto le dará
$a="/home/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ./root/b/b.php
y
$a="/home/apache/a/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../root/b/b.php
y
$a="/home/root/a/a.php";
$b="/home/apache/htdocs/b/en/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../apache/htdocs/b/en/b.php
y
$a="/home/apache/htdocs/b/en/b.php";
$b="/home/root/a/a.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../../../root/a/a.php
Simple de una sola línea para escenarios comunes:
str_replace(getcwd() . DIRECTORY_SEPARATOR, '''', $filepath)
o:
substr($filepath, strlen(getcwd())+1)
Para comprobar si la ruta es absoluta, intente:
$filepath[0] == DIRECTORY_SEPARATOR
const DS = DIRECTORY_SEPARATOR; // for convenience
function getRelativePath($from, $to) {
$dir = explode(DS, is_file($from) ? dirname($from) : rtrim($from, DS));
$file = explode(DS, $to);
while ($dir && $file && ($dir[0] == $file[0])) {
array_shift($dir);
array_shift($file);
}
return str_repeat(''..''.DS, count($dir)) . implode(DS, $file);
}
Mi intento es deliberadamente más simple, aunque probablemente no sea diferente en rendimiento. Dejaré el benchmarking como ejercicio para el curioso lector. Sin embargo, esto es bastante robusto y debería ser independiente de la plataforma.
Tenga cuidado con las soluciones que utilizan array_intersect
funciones array_intersect
, ya que se romperán si los directorios paralelos tienen el mismo nombre. Por ejemplo, getRelativePath(''start/A/end/'', ''start/B/end/'')
devolvería " ../end
" porque array_intersect
encuentra todos los nombres iguales, en este caso 2 cuando solo debería haber 1.