Trailing slash and root path in makeRelative

master
Anton Smirnov 11 months ago
parent 90db790146
commit 1677908239
  1. 38
      src/AbstractAbsolutePath.php
  2. 100
      tests/UnixPathTest.php

@ -31,34 +31,52 @@ abstract class AbstractAbsolutePath extends AbstractPath implements AbsolutePath
// optimize if the same instance
if ($this === $targetPath || $this->components === $targetPath->components) {
return $this->buildRelative(DataTypeHelper::iterableToNewListInstance(['.']));
return $this->buildRelative(DataTypeHelper::iterableToNewListInstance(
$this->components->count() > 0 && $this->components->top() === '' ? ['.', ''] : ['.']
));
}
$length = min($this->components->count(), $targetPath->components->count());
// strip trailing slash
$baseComponents = $this->components;
if ($baseComponents->count() > 0 && $baseComponents->top() === '') {
$baseComponents = clone $baseComponents; // clone when necessary
$baseComponents->pop();
}
// strip trailing slash
$targetComponents = clone $targetPath->components; // always clone
if ($targetComponents->count() > 0 && $targetComponents->top() === '') {
$targetComponents->pop();
}
$length = min($baseComponents->count(), $targetComponents->count());
$equals ??= fn($a, $b) => $a === $b;
for ($i = 0; $i < $length; $i++) {
if (!$equals($this->components[$i], $targetPath->components[$i])) {
if (!$equals($baseComponents[$i], $targetComponents[$i])) {
break;
}
}
$relativeComponents = clone $targetPath->components;
// delete $i components from the beginning (common prefix)
for ($j = 0; $j < $i; $j++) {
$relativeComponents->shift();
$targetComponents->shift();
}
// add (baseLen - $i) .. elements
$numBaseDiff = $this->components->count() - $i;
$numBaseDiff = $baseComponents->count() - $i;
for ($j = 0; $j < $numBaseDiff; $j++) {
$relativeComponents->unshift('..');
$targetComponents->unshift('..');
}
$relativeComponents->unshift('.');
// relative marker
$targetComponents->unshift('.');
// trailing slash
if ($targetPath->components->count() && $targetPath->components->top() === '') {
$targetComponents->push('');
}
return $this->buildRelative($relativeComponents);
return $this->buildRelative($targetComponents);
}
protected function buildRelative(\SplDoublyLinkedList $components): RelativePathInterface

@ -169,7 +169,105 @@ class UnixPathTest extends TestCase
foreach ($paths as $tpi => $tp) {
$result = $matrix[$bpi][$tpi];
self::assertEquals($result, $bp->makeRelative($tp));
self::assertEquals($result, $bp->makeRelative($tp)->toString());
}
}
}
public function testMakeRelativeTrailingSlash(): void
{
$paths = [
new UnixPath('/path/path1'),
new UnixPath('/path/path1/'),
new UnixPath('/path/path2'),
new UnixPath('/path/path2/'),
];
$matrix = [
[
'.',
'./',
'../path2',
'../path2/',
],
[
'.',
'./',
'../path2',
'../path2/',
],
[
'../path1',
'../path1/',
'.',
'./',
],
[
'../path1',
'../path1/',
'.',
'./',
],
];
foreach ($paths as $bpi => $bp) {
foreach ($paths as $tpi => $tp) {
$result = $matrix[$bpi][$tpi];
self::assertEquals(
$result,
$bp->makeRelative($tp)->toString(),
sprintf('Unexpected relative of base %s and target %s', \strval($bp), \strval($tp)),
);
}
}
}
public function testMakeRelativeRoot(): void
{
$paths = [
new UnixPath('/'),
new UnixPath('/'), // same path, different instance
new UnixPath('/path'),
new UnixPath('/path/'),
];
$matrix = [
[
'.',
'.',
'path',
'path/',
],
[
'.',
'.',
'path',
'path/',
],
[
'..',
'..',
'.',
'./',
],
[
'..',
'..',
'.',
'./',
],
];
foreach ($paths as $bpi => $bp) {
foreach ($paths as $tpi => $tp) {
$result = $matrix[$bpi][$tpi];
self::assertEquals(
$result,
$bp->makeRelative($tp)->toString(),
sprintf('Unexpected relative of base %s and target %s', \strval($bp), \strval($tp)),
);
}
}
}

Loading…
Cancel
Save