Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. Filesystem.php

function Filesystem::makePathRelative

Given an existing path, convert it to a path relative to a given starting path.

File

vendor/symfony/filesystem/Filesystem.php, line 436

Class

Filesystem
Provides basic utility to manipulate the file system.

Namespace

Symfony\Component\Filesystem

Code

public function makePathRelative(string $endPath, string $startPath) : string {
    if (!$this->isAbsolutePath($startPath)) {
        throw new InvalidArgumentException(\sprintf('The start path "%s" is not absolute.', $startPath));
    }
    if (!$this->isAbsolutePath($endPath)) {
        throw new InvalidArgumentException(\sprintf('The end path "%s" is not absolute.', $endPath));
    }
    // Normalize separators on Windows
    if ('\\' === \DIRECTORY_SEPARATOR) {
        $endPath = str_replace('\\', '/', $endPath);
        $startPath = str_replace('\\', '/', $startPath);
    }
    $splitDriveLetter = fn($path) => \strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0]) ? [
        substr($path, 2),
        strtoupper($path[0]),
    ] : [
        $path,
        null,
    ];
    $splitPath = function ($path) {
        $result = [];
        foreach (explode('/', trim($path, '/')) as $segment) {
            if ('..' === $segment) {
                array_pop($result);
            }
            elseif ('.' !== $segment && '' !== $segment) {
                $result[] = $segment;
            }
        }
        return $result;
    };
    [
        $endPath,
        $endDriveLetter,
    ] = $splitDriveLetter($endPath);
    [
        $startPath,
        $startDriveLetter,
    ] = $splitDriveLetter($startPath);
    $startPathArr = $splitPath($startPath);
    $endPathArr = $splitPath($endPath);
    if ($endDriveLetter && $startDriveLetter && $endDriveLetter != $startDriveLetter) {
        // End path is on another drive, so no relative path exists
        return $endDriveLetter . ':/' . ($endPathArr ? implode('/', $endPathArr) . '/' : '');
    }
    // Find for which directory the common path stops
    $index = 0;
    while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) {
        ++$index;
    }
    // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels)
    if (1 === \count($startPathArr) && '' === $startPathArr[0]) {
        $depth = 0;
    }
    else {
        $depth = \count($startPathArr) - $index;
    }
    // Repeated "../" for each level need to reach the common path
    $traverser = str_repeat('../', $depth);
    $endPathRemainder = implode('/', \array_slice($endPathArr, $index));
    // Construct $endPath from traversing to the common path, then to the remaining $endPath
    $relativePath = $traverser . ('' !== $endPathRemainder ? $endPathRemainder . '/' : '');
    return '' === $relativePath ? './' : $relativePath;
}

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal