191 lines
4.8 KiB
PHP
Executable file
191 lines
4.8 KiB
PHP
Executable file
<?php
|
|
|
|
/**
|
|
* League.Uri (https://uri.thephpleague.com)
|
|
*
|
|
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace League\Uri\Components;
|
|
|
|
use League\Uri\Contracts\PathInterface;
|
|
use League\Uri\Contracts\UriInterface;
|
|
use League\Uri\Encoder;
|
|
use Psr\Http\Message\UriInterface as Psr7UriInterface;
|
|
use Stringable;
|
|
|
|
use function array_pop;
|
|
use function array_reduce;
|
|
use function end;
|
|
use function explode;
|
|
use function implode;
|
|
use function substr;
|
|
|
|
final class Path extends Component implements PathInterface
|
|
{
|
|
private const DOT_SEGMENTS = ['.' => 1, '..' => 1];
|
|
private const SEPARATOR = '/';
|
|
|
|
private readonly string $path;
|
|
|
|
/**
|
|
* New instance.
|
|
*/
|
|
private function __construct(Stringable|string $path)
|
|
{
|
|
$this->path = $this->validate($path);
|
|
}
|
|
|
|
/**
|
|
* Validate the component content.
|
|
*/
|
|
private function validate(Stringable|string $path): string
|
|
{
|
|
/** @var string $path */
|
|
$path = $this->validateComponent($path);
|
|
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* Returns a new instance from a string or a stringable object.
|
|
*/
|
|
public static function new(Stringable|string $value = ''): self
|
|
{
|
|
return new self($value);
|
|
}
|
|
|
|
/**
|
|
* Create a new instance from a URI object.
|
|
*/
|
|
public static function fromUri(Stringable|string $uri): self
|
|
{
|
|
$uri = self::filterUri($uri);
|
|
$path = $uri->getPath();
|
|
$authority = $uri->getAuthority();
|
|
|
|
return match (true) {
|
|
null === $authority, '' === $authority, '' === $path, '/' === $path[0] => new self($path),
|
|
default => new self('/'.$path),
|
|
};
|
|
}
|
|
|
|
public function value(): ?string
|
|
{
|
|
return Encoder::encodePath($this->path);
|
|
}
|
|
|
|
public function decoded(): string
|
|
{
|
|
return $this->path;
|
|
}
|
|
|
|
public function isAbsolute(): bool
|
|
{
|
|
return self::SEPARATOR === ($this->path[0] ?? '');
|
|
}
|
|
|
|
public function hasTrailingSlash(): bool
|
|
{
|
|
return '' !== $this->path && self::SEPARATOR === substr($this->path, -1);
|
|
}
|
|
|
|
public function withoutDotSegments(): PathInterface
|
|
{
|
|
$current = $this->toString();
|
|
if (!str_contains($current, '.')) {
|
|
return $this;
|
|
}
|
|
|
|
$input = explode(self::SEPARATOR, $current);
|
|
$new = implode(self::SEPARATOR, array_reduce($input, $this->filterDotSegments(...), []));
|
|
if (isset(self::DOT_SEGMENTS[end($input)])) {
|
|
$new .= self::SEPARATOR ;
|
|
}
|
|
|
|
return new self($new);
|
|
}
|
|
|
|
/**
|
|
* Filter Dot segment according to RFC3986.
|
|
*
|
|
* @see http://tools.ietf.org/html/rfc3986#section-5.2.4
|
|
*
|
|
* @return string[]
|
|
*/
|
|
private function filterDotSegments(array $carry, string $segment): array
|
|
{
|
|
if ('..' === $segment) {
|
|
array_pop($carry);
|
|
|
|
return $carry;
|
|
}
|
|
|
|
if (!isset(self::DOT_SEGMENTS[$segment])) {
|
|
$carry[] = $segment;
|
|
}
|
|
|
|
return $carry;
|
|
}
|
|
|
|
/**
|
|
* Returns an instance with a trailing slash.
|
|
*
|
|
* This method MUST retain the state of the current instance, and return
|
|
* an instance that contains the path component with a trailing slash
|
|
*/
|
|
public function withTrailingSlash(): PathInterface
|
|
{
|
|
return $this->hasTrailingSlash() ? $this : new self($this->toString().self::SEPARATOR);
|
|
}
|
|
|
|
public function withoutTrailingSlash(): PathInterface
|
|
{
|
|
return !$this->hasTrailingSlash() ? $this : new self(substr($this->toString(), 0, -1));
|
|
}
|
|
|
|
public function withLeadingSlash(): PathInterface
|
|
{
|
|
return $this->isAbsolute() ? $this : new self(self::SEPARATOR.$this->toString());
|
|
}
|
|
|
|
public function withoutLeadingSlash(): PathInterface
|
|
{
|
|
return !$this->isAbsolute() ? $this : new self(substr($this->toString(), 1));
|
|
}
|
|
|
|
/**
|
|
* DEPRECATION WARNING! This method will be removed in the next major point release.
|
|
*
|
|
* @deprecated Since version 7.0.0
|
|
* @see HierarchicalPath::new()
|
|
*
|
|
* @codeCoverageIgnore
|
|
*
|
|
* Returns a new instance from a string or a stringable object.
|
|
*/
|
|
public static function createFromString(Stringable|string|int $path): self
|
|
{
|
|
return self::new((string) $path);
|
|
}
|
|
|
|
/**
|
|
* DEPRECATION WARNING! This method will be removed in the next major point release.
|
|
*
|
|
* @deprecated Since version 7.0.0
|
|
* @see HierarchicalPath::fromUri()
|
|
*
|
|
* @codeCoverageIgnore
|
|
*
|
|
* Create a new instance from a URI object.
|
|
*/
|
|
public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
|
|
{
|
|
return self::fromUri($uri);
|
|
}
|
|
}
|