PHP - Get relative path from one directory to another

Solution:

Following function would do the job:

function getRelativePath($source, $destination) {
    $sourceArray = [];
    preg_match_all('/([^\/]+)/', $source, $sourceArray);
    $destinationArray = [];
    preg_match_all('/([^\/]+)/', $destination, $destinationArray);
    
    $sourceArray = array_reverse($sourceArray[0]);
    $destinationArray = array_reverse($destinationArray[0]);
    
    $relative = [];
    $hasPath = false;
    foreach ($sourceArray as $path) {
        for ($i = 0; $i < count($destinationArray); $i++ ) {
            $to = $destinationArray[$i];
            if ($path == $to) {
                $hasPath = true;
                for ($j = $i - 1; $j >= 0 ; $j--)
                    $relative[] = $destinationArray[$j];
                break 2;    
            }
        }
        $relative[] = "..";
    }
    return $hasPath ? implode("/",$relative) . "/" : "NO PATH";
}

Answer

Solution:

The Amir MB's solution seems flawed in a few ways.

Say we have repetitions in folder names throughout the path.

/var/www/src/vendor/package/public
/var/www/vendor/src/index.php

Your solution returns:

../../src/index.php/

i.e., where it stumbles upon a repetitive (existing in both paths) folder name, the ascension breaks off.

The correct answer must be:

../../../../vendor/src/index.php

And in

/var/www/src/vendor_1/Package_1/public
/var/www/vendor/src/index.php

Flawed: ../../../index.php/
Correct: ../../../../vendor/src/index.php

When both paths are relative, a similar thing occurs:

src/vendor_1/Package_1/public
vendor/src/index.php

Flawed: ../../../index.php/
Correct: ../../../../vendor/src/index.php

I came across other cases where the solution failed. The algorithm has to be changed.

Here is a simple function given the source and destination EXIST:

function getRelativePath($source, $destination)
{
    $paths =
        array_map(fn ($arg) => explode('/', realpath($arg)), func_get_args());
    return
        str_repeat('../', count(array_diff_assoc(...$paths))) .
        implode('/', array_diff_assoc(...array_reverse($paths)));
}

Nothing fancy. No check is implemented on the source being a folder and not a file (note the destination can be a file while the source is supposed to be a directory from where the relative path is calculated.

I will probably come back shortly with a universal solution for any path, existing or not, as relative as absolute.

Source