J'ai remarqué des questions similaires sur ce problème lorsque j'ai tapé le titre, mais elles ne semblent pas être en PHP. Alors, quelle est la solution avec une fonction PHP?
À spécifier.
$a="/home/Apache/a/a.php";
$b="/home/root/b/b.php";
$relpath = getRelativePath($a,$b); //needed function,should return '../../root/b/b.php'
Des bonnes idées? Merci.
Essaye celui-là:
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);
}
Cela donnera
$a="/home/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ./root/b/b.php
et
$a="/home/Apache/a/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../root/b/b.php
et
$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
et
$a="/home/Apache/htdocs/b/en/b.php";
$b="/home/root/a/a.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../../../root/a/a.php
Comme nous avons eu plusieurs réponses, j'ai décidé de toutes les tester et de les évaluer .. J'ai utilisé ces chemins pour tester:
$from = "/var/www/sites/web/mainroot/webapp/folder/sub/subf/subfo/subfol/subfold/lastfolder/";
NOTE: s'il s'agit d'un dossier, vous devez insérer une barre oblique finale pour que les fonctions fonctionnent correctement! Donc, __DIR__
ne fonctionnera pas. Utilisez __FILE__
à la place ou __DIR__ . '/'
$to = "/var/www/sites/web/mainroot/webapp/folder/aaa/bbb/ccc/ddd";
RÉSULTATS: (le séparateur décimal est une virgule, le séparateur de milliers est un point)
Alors, je vous suggère d'utiliser la mise en œuvre de Gordon! (celui marqué comme réponse)
Celui de Young est bon également et fonctionne mieux avec des structures de répertoires simples (comme "a/b/c.php"), tandis que celui de Gordon fonctionne mieux avec des structures complexes, avec de nombreux sous-répertoires (comme ceux utilisés dans ce test).
NOTE: J'écris ci-dessous les résultats renvoyés avec $from
et $to
en tant qu'entrées afin que vous puissiez vérifier que 2 d'entre eux sont corrects, les 2 autres sont incorrects:
../../../../../../aaa/bbb/ccc/ddd
-> CORRECT../../../../../../aaa/bbb/ccc/ddd
-> CORRECT../../../../../../bbb/ccc/ddd
-> FAUX../../../../../aaa/bbb/ccc/ddd
-> FAUXChemin relatif? Cela ressemble plus à un chemin de voyage. Vous semblez vouloir connaître le chemin que vous empruntez pour aller du chemin A au chemin B. Si c'est le cas, vous pouvez exploser $ a et $ b sur '/'
puis faire une boucle inversée entre $ aParts, en les comparant à bPièces du même index jusqu'à ce que le répertoire "dénominateur commun" soit trouvé (en enregistrant le nombre de boucles en cours de route). Créez ensuite une chaîne vide et ajoutez-lui '../'
$ numLoops-1 fois, puis ajoutez-y $ b moins le répertoire du dénominateur commun.
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);
}
Ma tentative est délibérément plus simple, bien que probablement pas différente en performance. Je laisserai le benchmarking comme exercice pour le lecteur curieux. Cependant, cela est assez robuste et devrait être agnostique de plate-forme.
Attention les solutions utilisant les fonctions array_intersect
car celles-ci se briseront si les répertoires parallèles portent le même nom. Par exemple, getRelativePath('start/A/end/', 'start/B/end/')
renverrait "../end
" car array_intersect
trouve tous les noms égaux, dans ce cas 2 où il ne devrait y en avoir qu'un.
Basé sur la fonction de Gordon, ma solution est la suivante:
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;
}
Ce code provient du générateur d’URL 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;
}
Certaines raisons, les Gordon n'ont pas fonctionné pour moi .... Voici ma solution
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);
}
Je suis arrivé au même résultat en utilisant ces manipulations de tableau:
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);
}
Le deuxième argument est le fichier auquel le chemin est relatif. C'est facultatif pour que vous puissiez obtenir le chemin relatif quelle que soit la page Web que vous êtes actuellement . Pour pouvoir l'utiliser avec @Young ou @Gordon par exemple, car vous voulez connaître le chemin relatif vers $ b à partir de $ a, vous avoir à utiliser
getRelativePath($b, $a);
Une ligne simple pour les scénarios courants:
str_replace(getcwd() . DIRECTORY_SEPARATOR, '', $filepath)
ou:
substr($filepath, strlen(getcwd())+1)
Pour vérifier si le chemin est absolu, essayez:
$filepath[0] == DIRECTORY_SEPARATOR