PHP mutlak yolundan göreli yolunu alma

7 Cevap php

Ben başlığı yazdığınız bu sorun hakkında bazı benzer sorular fark ettim, ancak PHP olmayacak gibi görünüyor. Yani bir PHP fonksiyonu ile buna çözüm nedir?

Spesifik olarak.

$a="/home/apache/a/a.php";
$b="/home/root/b/b.php";
$relpath = getRelativePath($a,$b); //needed function,should return '../../root/b/b.php'

Herhangi bir iyi bir fikir? Teşekkürler.

7 Cevap

Bunu dene:

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);
}

Bu verecek

$a="/home/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL;  // ./root/b/b.php

ve

$a="/home/apache/a/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../root/b/b.php

ve

$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

ve

$a="/home/apache/htdocs/b/en/b.php";
$b="/home/root/a/a.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../../../root/a/a.php

Göreli yol? Bu daha çok bir seyahat yolu gibi görünüyor. Sen bir o durum buysa yol B'ye yol A almak için gitti yolunu bilmek istediğiniz gibi yapabilirsiniz explode $ a ve üzerinden '/' daha sonra ters döngü $ b $ Aparts, "ortak payda" dizin (yol boyunca döngü sayısını kayıt) bulunana kadar aynı indeksi $ bParts onları karşılaştırarak. Sonra boş bir dize oluşturmak ve '../' o $ b eksi ortak payda dizine eklemek $ numLoops-1 kere buna ekleyin.

Since we've had several answers, I decided to test them all and benchmark them. I used this paths to test:

$from = "/var/www/sites/web/mainroot/webapp/folder/sub/subf/subfo/subfol/subfold/lastfolder/"; Not: bir klasör ise, işlevleri düzgün çalışması için bir bölü koymak zorunda! Yani, __DIR__ çalışmayacaktır. __FILE__ yerine, ya da __DIR__ . '/' kullan

$to = "/var/www/sites/web/mainroot/webapp/folder/aaa/bbb/ccc/ddd";

RESULTS: (decimal separator is comma, thousand separator is dot)

  • Gordon tarafından Fonksiyon: sonuç CORRECT, 100.000 yöneticilerin 1,222 saniye zamanı
  • Young tarafından Fonksiyon: sonuç CORRECT, 100.000 yöneticilerin 1,540 saniye zamanı
  • Ceagle tarafından Fonksiyon: sonuç WRONG (bazı yolları ile çalışır ancak bazı diğerleri başarısız, testlerde kullanılan ve yukarıda yazılı olanlar gibi)
  • Loranger tarafından Fonksiyon: sonuç WRONG (bazı yolları ile çalışır ancak bazı diğerleri başarısız, testlerde kullanılan ve yukarıda yazılı olanlar gibi)

So, I suggest that you use Gordon's implementation! (the one marked as answer)

Young kimse çok iyidir ve Gordon'un bir (bu kriter kullanılan olanlar gibi) alt dizinleri dolu, karmaşık yapıları ile daha iyi performans ise, ("a / b / c.php" gibi) basit dizin yapıları ile daha iyi bir performans sergiliyor.


NOT: Ben $from ve $to giriş olarak, diğer 2 yanlış ise, 2 tanesi Tamam olduğunu doğrulamak böylece döndü sonuçlar aşağıda burada yazmak:

  • Gordon: ../../../../../../aaa/bbb/ccc/ddd -> DOĞRU
  • Genç: ../../../../../../aaa/bbb/ccc/ddd -> DOĞRU
  • Ceagle: ../../../../../../bbb/ccc/ddd -> YANLIŞ
  • Loranger: ../../../../../aaa/bbb/ccc/ddd -> YANLIŞ

Şöyle Gordon'un fonksiyonu dayanarak, benim çözüm:

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;
}

Gordon benim için işe yaramadı nedense .... İşte benim çözüm

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);
}

Ben bu dizi manipülasyonlar kullanarak aynı sonucu geldi:

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);
}

The second argument is the file which the path is relative to. It's optional so you can get the relative path regardless the webpage your currently are. In order to use it with @Young or @Gordon example, because you want to know the relative path to $b from $a, you'll have to use

getRelativePath($b, $a);
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);
}

Performans muhtemelen farklı olsa da, benim girişimi, kasıtlı basittir. Ben meraklı okuyucu için bir alıştırma olarak kıyaslama bırakacağım. Ancak, bu oldukça sağlam ve agnostik bir platform olmalıdır.

Paralel dizinleri aynı adı varsa, bu gibi array_intersect işlevleri kullanarak Beware çözümler kıracak. Örneğin getRelativePath('start/A/end/', 'start/B/end/') sadece 1 olmalıdır zaman array_intersect bu durumda 2 tüm eşit adlarını bulur "../end" Çünkü dönecekti.