You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
php-path/src/AbstractPath.php

163 lines
4.1 KiB
PHTML

1 year ago
<?php
declare(strict_types=1);
namespace Arokettu\Path;
use Arokettu\Path\Helpers\DataTypeHelper;
/**
* @internal
*/
1 year ago
abstract class AbstractPath implements PathInterface
{
protected string $prefix;
protected \SplDoublyLinkedList $components;
1 year ago
abstract protected function parsePath(string $path, bool $strict): void;
1 year ago
1 year ago
public function __construct(string $path, bool $strict = false)
1 year ago
{
1 year ago
$this->parsePath($path, $strict);
1 year ago
}
/**
* @param RelativePathInterface|string $path
* @return static
1 year ago
*/
public function resolveRelative($path, bool $strict = false): self
{
if (\is_string($path)) {
$path = $this->buildRelative($path);
}
return $this->doResolveRelative($path, $strict);
}
protected function buildRelative(string $path): RelativePath
{
return new RelativePath($path);
}
/**
* @return static
*/
protected function doResolveRelative(RelativePathInterface $path, bool $strict): self
1 year ago
{
if ($path instanceof RelativePath) {
// optimize
$relativeComponents = $path->components;
} else {
// allow external implementations
$relativeComponents = $path->getComponents();
if (!array_is_list($relativeComponents)) {
throw new \InvalidArgumentException(
'Poor RelativePathInterface implementation: getComponents() must return a list'
);
}
}
1 year ago
if ($path->isRoot()) {
$newPath = clone $this;
$newPath->components = DataTypeHelper::iterableToNewListInstance($relativeComponents);
1 year ago
return $newPath;
}
$components = clone $this->components;
$numComponents = \count($relativeComponents);
1 year ago
for ($i = 0; $i < $numComponents; $i++) {
if ($relativeComponents[$i] === '.') {
continue;
}
if (
$relativeComponents[$i] === '..' &&
!$components->isEmpty() &&
$components->top() !== '..' &&
$components->top() !== '.'
) {
1 year ago
$components->pop();
continue;
}
$components->push($relativeComponents[$i]);
}
$newPath = clone $this;
$newPath->components = $this->normalizeHead($components, $strict);
return $newPath;
}
protected function normalize(array $components): \SplDoublyLinkedList
{
$componentsList = new \SplDoublyLinkedList();
$prevComponent = null;
foreach ($components as $component) {
if ($component === '.' || $component === '') {
continue;
}
1 year ago
if (
$component === '..' &&
$prevComponent !== '..' && $prevComponent !== null && // leading ..'s
$componentsList->count() > 0 // beginning of the list
) {
1 year ago
$componentsList->pop();
1 year ago
continue;
1 year ago
}
1 year ago
$componentsList->push($component);
1 year ago
$prevComponent = $component;
}
return $componentsList;
}
protected function normalizeHead(\SplDoublyLinkedList $components, bool $strict): \SplDoublyLinkedList
{
while (!$components->isEmpty()) {
if ($components[0] === '.') {
$components->shift();
continue;
}
if ($components[0] === '..') {
if ($strict) {
throw new \InvalidArgumentException('Relative path went beyond root');
}
$components->shift();
continue;
}
break;
}
return $components;
}
public function toString(): string
{
return $this->prefix . \iter\join('/', $this->components);
}
1 year ago
public function __toString(): string
{
return $this->toString();
}
1 year ago
public function getPrefix(): string
{
return $this->prefix;
}
1 year ago
public function getComponents(): array
{
return iterator_to_array($this->components, false);
}
}