public function getRelativePath(string $fromDir, string $to) : string
    {
        if (strpos($to, 'data://') === 0) {
            return $to;
        }

        list($fromStreamWrapper, $fromDirPath) = $this->splitStreamWrapperAndPath($fromDir);
        list($toStreamWrapper, $toPath) = $this->splitStreamWrapperAndPath($to);

        if ($fromStreamWrapper !== $toStreamWrapper) {
            return $to;
        }

        // some compatibility fixes for Windows paths
        $fromDirPath = str_replace('\\', '/', $fromDirPath);
        $toPath      = str_replace('\\', '/', $toPath);

        if (substr($fromDirPath, -1) !== '/') {
            $fromDirPath .= '/';
        }

        // Optimize common case where $to is a sub path of $from
        if (strpos($toPath, $fromDirPath) === 0) {
            return PathHelper::normalize(substr($toPath, strlen($fromDirPath)) ?: './');
        }

        $fromDirPath = explode('/', $fromDirPath);
        $toPath      = explode('/', $toPath);
        $relPath     = $toPath;

        foreach ($fromDirPath as $depth => $dir) {
            // find first non-matching dir
            if (isset($toPath[$depth]) && $dir === $toPath[$depth]) {
                // ignore this directory
                array_shift($relPath);
            } else {
                // get number of remaining dirs to $from
                $remaining = count($fromDirPath) - $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] = './' . (isset($relPath[0]) ? $relPath[0] : '');
                }
            }
        }

        $relPath = implode('/', $relPath);

        return PathHelper::normalize(
            $relPath === '..' || $relPath === '.'
                ? $relPath . '/'
                : $relPath
        );
    }
public function getRelativePath(string $fromDir, string $to) : string
    {
        if (strpos($to, 'data://') === 0) {
            return $to;
        }

        list($fromStreamWrapper, $fromDirPath) = $this->splitStreamWrapperAndPath($fromDir);
        list($toStreamWrapper, $toPath) = $this->splitStreamWrapperAndPath($to);

        if ($fromStreamWrapper !== $toStreamWrapper) {
            return $to;
        }

        // some compatibility fixes for Windows paths
        $fromDirPath = str_replace('\\', '/', $fromDirPath);
        $toPath      = str_replace('\\', '/', $toPath);

        if (substr($fromDirPath, -1) !== '/') {
            $fromDirPath .= '/';
        }

        // Optimize common case where $to is a sub path of $from
        if (strpos($toPath, $fromDirPath) === 0) {
            return PathHelper::normalize(substr($toPath, strlen($fromDirPath)) ?: './');
        }

        $fromDirPath = explode('/', $fromDirPath);
        $toPath      = explode('/', $toPath);
        $relPath     = $toPath;

        foreach ($fromDirPath as $depth => $dir) {
            // find first non-matching dir
            if (isset($toPath[$depth]) && $dir === $toPath[$depth]) {
                // ignore this directory
                array_shift($relPath);
            } else {
                // get number of remaining dirs to $from
                $remaining = count($fromDirPath) - $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] = './' . (isset($relPath[0]) ? $relPath[0] : '');
                }
            }
        }

        $relPath = implode('/', $relPath);

        return PathHelper::normalize(
            $relPath === '..' || $relPath === '.'
                ? $relPath . '/'
                : $relPath
        );
    }
public function getRelativePath ( string $ fromDir , string $ to ) : string { if ( strpos ( $ to , 'data://' ) === 0 ) { return $ to ; } list ( $ fromStreamWrapper , $ fromDirPath ) = $ this -> splitStreamWrapperAndPath ( $ fromDir ) ; list ( $ toStreamWrapper , $ toPath ) = $ this -> splitStreamWrapperAndPath ( $ to ) ; if ( $ fromStreamWrapper !== $ toStreamWrapper ) { return $ to ; } // some compatibility fixes for Windows paths $ fromDirPath = str_replace ( '\\' , '/' , $ fromDirPath ) ; $ toPath = str_replace ( '\\' , '/' , $ toPath ) ; if ( substr ( $ fromDirPath , - 1 ) !== '/' ) { $ fromDirPath .= '/' ; } // Optimize common case where $to is a sub path of $from if ( strpos ( $ toPath , $ fromDirPath ) === 0 ) { return PathHelper :: normalize ( substr ( $ toPath , strlen ( $ fromDirPath ) ) ? : './' ) ; } $ fromDirPath = explode ( '/' , $ fromDirPath ) ; $ toPath = explode ( '/' , $ toPath ) ; $ relPath = $ toPath ; foreach ( $ fromDirPath as $ depth => $ dir ) { // find first non-matching dir if ( isset ( $ toPath [ $ depth ] ) && $ dir === $ toPath [ $ depth ] ) { // ignore this directory array_shift ( $ relPath ) ; } else { // get number of remaining dirs to $from $ remaining = count ( $ fromDirPath ) - $ 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 ] = './' . ( isset ( $ relPath [ 0 ] ) ? $ relPath [ 0 ] : '' ) ; } } } $ relPath = implode ( '/' , $ relPath ) ; return PathHelper :: normalize ( $ relPath === '..' || $ relPath === '.' ? $ relPath . '/' : $ relPath ) ; }
Gets the relative path from the first path to the second path.

@param string $fromDir
@param string $to

@return string
@throws InvalidArgumentException
Gets the relative path from the first path to the second path .