public function resolveRelativePath(string $basePath, string $relativePath) : string
    {
        if (strpos($relativePath, 'data://') === 0) {
            return $relativePath;
        }

        list($basePathStreamWrapper, $basePath) = $this->splitStreamWrapperAndPath($basePath);
        list($relativeStreamWrapper, $relativePathWithoutStreamWrapper) = $this->splitStreamWrapperAndPath($relativePath);

        $isAbsolutePath = $relativeStreamWrapper !== null;

        if ($isAbsolutePath) {
            return $relativePath;
        } else {
            $relativePath = $relativePathWithoutStreamWrapper;
        }

        $basePath     = str_replace('\\', '/', $basePath);
        $relativePath = str_replace('\\', '/', $relativePath);

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

        if (strpos($relativePath, '.') === false) {
            return ($basePathStreamWrapper ? $basePathStreamWrapper . '://' : '')
            . PathHelper::normalize(str_replace('//', '/', $basePath . '/' . $relativePath));
        }

        $parts          = explode('/', $basePath);
        $relativeParts  = explode('/', trim($relativePath, '/'));
        $isDrivePath    = substr($basePath, 1, 1) === ':';
        $precedingSlash = $basePathStreamWrapper || $isDrivePath ? '' : '/';
        $trailingSlash  = !$basePathStreamWrapper && substr($relativePath, -1) === '/' ? '/' : '';

        foreach ($parts as $key => $part) {
            if ($part === '') {
                unset($parts[$key]);
            }
        }

        foreach ($relativeParts as $part) {
            if ($part === '.') {
                continue;
            } elseif ($part === '..') {
                array_pop($parts);
            } elseif ($part !== '') {
                $parts[] = $part;
            }
        }

        return ($basePathStreamWrapper ? $basePathStreamWrapper . '://' : '')
        . PathHelper::normalize($precedingSlash . implode('/', $parts) . ($parts ? $trailingSlash : ''));
    }
public function resolveRelativePath(string $basePath, string $relativePath) : string
    {
        if (strpos($relativePath, 'data://') === 0) {
            return $relativePath;
        }

        list($basePathStreamWrapper, $basePath) = $this->splitStreamWrapperAndPath($basePath);
        list($relativeStreamWrapper, $relativePathWithoutStreamWrapper) = $this->splitStreamWrapperAndPath($relativePath);

        $isAbsolutePath = $relativeStreamWrapper !== null;

        if ($isAbsolutePath) {
            return $relativePath;
        } else {
            $relativePath = $relativePathWithoutStreamWrapper;
        }

        $basePath     = str_replace('\\', '/', $basePath);
        $relativePath = str_replace('\\', '/', $relativePath);

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

        if (strpos($relativePath, '.') === false) {
            return ($basePathStreamWrapper ? $basePathStreamWrapper . '://' : '')
            . PathHelper::normalize(str_replace('//', '/', $basePath . '/' . $relativePath));
        }

        $parts          = explode('/', $basePath);
        $relativeParts  = explode('/', trim($relativePath, '/'));
        $isDrivePath    = substr($basePath, 1, 1) === ':';
        $precedingSlash = $basePathStreamWrapper || $isDrivePath ? '' : '/';
        $trailingSlash  = !$basePathStreamWrapper && substr($relativePath, -1) === '/' ? '/' : '';

        foreach ($parts as $key => $part) {
            if ($part === '') {
                unset($parts[$key]);
            }
        }

        foreach ($relativeParts as $part) {
            if ($part === '.') {
                continue;
            } elseif ($part === '..') {
                array_pop($parts);
            } elseif ($part !== '') {
                $parts[] = $part;
            }
        }

        return ($basePathStreamWrapper ? $basePathStreamWrapper . '://' : '')
        . PathHelper::normalize($precedingSlash . implode('/', $parts) . ($parts ? $trailingSlash : ''));
    }
public function resolveRelativePath ( string $ basePath , string $ relativePath ) : string { if ( strpos ( $ relativePath , 'data://' ) === 0 ) { return $ relativePath ; } list ( $ basePathStreamWrapper , $ basePath ) = $ this -> splitStreamWrapperAndPath ( $ basePath ) ; list ( $ relativeStreamWrapper , $ relativePathWithoutStreamWrapper ) = $ this -> splitStreamWrapperAndPath ( $ relativePath ) ; $ isAbsolutePath = $ relativeStreamWrapper !== null ; if ( $ isAbsolutePath ) { return $ relativePath ; } else { $ relativePath = $ relativePathWithoutStreamWrapper ; } $ basePath = str_replace ( '\\' , '/' , $ basePath ) ; $ relativePath = str_replace ( '\\' , '/' , $ relativePath ) ; if ( substr ( $ basePath , - 1 ) !== '/' ) { $ basePath .= '/' ; } if ( strpos ( $ relativePath , '.' ) === false ) { return ( $ basePathStreamWrapper ? $ basePathStreamWrapper . '://' : '' ) . PathHelper :: normalize ( str_replace ( '//' , '/' , $ basePath . '/' . $ relativePath ) ) ; } $ parts = explode ( '/' , $ basePath ) ; $ relativeParts = explode ( '/' , trim ( $ relativePath , '/' ) ) ; $ isDrivePath = substr ( $ basePath , 1 , 1 ) === ':' ; $ precedingSlash = $ basePathStreamWrapper || $ isDrivePath ? '' : '/' ; $ trailingSlash = ! $ basePathStreamWrapper && substr ( $ relativePath , - 1 ) === '/' ? '/' : '' ; foreach ( $ parts as $ key => $ part ) { if ( $ part === '' ) { unset ( $ parts [ $ key ] ) ; } } foreach ( $ relativeParts as $ part ) { if ( $ part === '.' ) { continue ; } elseif ( $ part === '..' ) { array_pop ( $ parts ) ; } elseif ( $ part !== '' ) { $ parts [ ] = $ part ; } } return ( $ basePathStreamWrapper ? $ basePathStreamWrapper . '://' : '' ) . PathHelper :: normalize ( $ precedingSlash . implode ( '/' , $ parts ) . ( $ parts ? $ trailingSlash : '' ) ) ; }
Combines the two paths and resolves '..' and '.' relative traversals.

@param string $basePath
@param string $relativePath

@return string
Combines the two paths and resolves .. and . relative traversals .