Initial commit

This commit is contained in:
2025-08-04 16:33:07 +03:30
commit f798e8e35c
9595 changed files with 1208683 additions and 0 deletions

658
vendor/league/uri/BaseUri.php vendored Normal file
View File

@@ -0,0 +1,658 @@
<?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;
use JsonSerializable;
use League\Uri\Contracts\UriAccess;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\Idna\Converter as IdnaConverter;
use League\Uri\IPv4\Converter as IPv4Converter;
use League\Uri\IPv6\Converter as IPv6Converter;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
use function array_pop;
use function array_reduce;
use function count;
use function end;
use function explode;
use function implode;
use function in_array;
use function preg_match;
use function rawurldecode;
use function str_repeat;
use function str_replace;
use function strpos;
use function substr;
/**
* @phpstan-import-type ComponentMap from UriInterface
*/
class BaseUri implements Stringable, JsonSerializable, UriAccess
{
/** @var array<string,int> */
final protected const WHATWG_SPECIAL_SCHEMES = ['ftp' => 1, 'http' => 1, 'https' => 1, 'ws' => 1, 'wss' => 1];
/** @var array<string,int> */
final protected const DOT_SEGMENTS = ['.' => 1, '..' => 1];
protected readonly Psr7UriInterface|UriInterface|null $origin;
protected readonly ?string $nullValue;
/**
* @param UriFactoryInterface|null $uriFactory Deprecated, will be removed in the next major release
*/
final protected function __construct(
protected readonly Psr7UriInterface|UriInterface $uri,
protected readonly ?UriFactoryInterface $uriFactory
) {
$this->nullValue = $this->uri instanceof Psr7UriInterface ? '' : null;
$this->origin = $this->computeOrigin($this->uri, $this->nullValue);
}
public static function from(Stringable|string $uri, ?UriFactoryInterface $uriFactory = null): static
{
return new static(static::formatHost(static::filterUri($uri, $uriFactory)), $uriFactory);
}
public function withUriFactory(UriFactoryInterface $uriFactory): static
{
return new static($this->uri, $uriFactory);
}
public function withoutUriFactory(): static
{
return new static($this->uri, null);
}
public function getUri(): Psr7UriInterface|UriInterface
{
return $this->uri;
}
public function getUriString(): string
{
return $this->uri->__toString();
}
public function jsonSerialize(): string
{
return $this->uri->__toString();
}
public function __toString(): string
{
return $this->uri->__toString();
}
public function origin(): ?self
{
return match (null) {
$this->origin => null,
default => new self($this->origin, $this->uriFactory),
};
}
/**
* Returns the Unix filesystem path.
*
* The method will return null if a scheme is present and is not the `file` scheme
*/
public function unixPath(): ?string
{
return match ($this->uri->getScheme()) {
'file', $this->nullValue => rawurldecode($this->uri->getPath()),
default => null,
};
}
/**
* Returns the Windows filesystem path.
*
* The method will return null if a scheme is present and is not the `file` scheme
*/
public function windowsPath(): ?string
{
static $regexpWindowsPath = ',^(?<root>[a-zA-Z]:),';
if (!in_array($this->uri->getScheme(), ['file', $this->nullValue], true)) {
return null;
}
$originalPath = $this->uri->getPath();
$path = $originalPath;
if ('/' === ($path[0] ?? '')) {
$path = substr($path, 1);
}
if (1 === preg_match($regexpWindowsPath, $path, $matches)) {
$root = $matches['root'];
$path = substr($path, strlen($root));
return $root.str_replace('/', '\\', rawurldecode($path));
}
$host = $this->uri->getHost();
return match ($this->nullValue) {
$host => str_replace('/', '\\', rawurldecode($originalPath)),
default => '\\\\'.$host.'\\'.str_replace('/', '\\', rawurldecode($path)),
};
}
/**
* Returns a string representation of a File URI according to RFC8089.
*
* The method will return null if the URI scheme is not the `file` scheme
*/
public function toRfc8089(): ?string
{
$path = $this->uri->getPath();
return match (true) {
'file' !== $this->uri->getScheme() => null,
in_array($this->uri->getAuthority(), ['', null, 'localhost'], true) => 'file:'.match (true) {
'' === $path,
'/' === $path[0] => $path,
default => '/'.$path,
},
default => (string) $this->uri,
};
}
/**
* Tells whether the `file` scheme base URI represents a local file.
*/
public function isLocalFile(): bool
{
return match (true) {
'file' !== $this->uri->getScheme() => false,
in_array($this->uri->getAuthority(), ['', null, 'localhost'], true) => true,
default => false,
};
}
/**
* Tells whether the URI is opaque or not.
*
* A URI is opaque if and only if it is absolute
* and does not has an authority path.
*/
public function isOpaque(): bool
{
return $this->nullValue === $this->uri->getAuthority()
&& $this->isAbsolute();
}
/**
* Tells whether two URI do not share the same origin.
*/
public function isCrossOrigin(Stringable|string $uri): bool
{
if (null === $this->origin) {
return true;
}
$uri = static::filterUri($uri);
$uriOrigin = $this->computeOrigin($uri, $uri instanceof Psr7UriInterface ? '' : null);
return match(true) {
null === $uriOrigin,
$uriOrigin->__toString() !== $this->origin->__toString() => true,
default => false,
};
}
/**
* Tells whether the URI is absolute.
*/
public function isAbsolute(): bool
{
return $this->nullValue !== $this->uri->getScheme();
}
/**
* Tells whether the URI is a network path.
*/
public function isNetworkPath(): bool
{
return $this->nullValue === $this->uri->getScheme()
&& $this->nullValue !== $this->uri->getAuthority();
}
/**
* Tells whether the URI is an absolute path.
*/
public function isAbsolutePath(): bool
{
return $this->nullValue === $this->uri->getScheme()
&& $this->nullValue === $this->uri->getAuthority()
&& '/' === ($this->uri->getPath()[0] ?? '');
}
/**
* Tells whether the URI is a relative path.
*/
public function isRelativePath(): bool
{
return $this->nullValue === $this->uri->getScheme()
&& $this->nullValue === $this->uri->getAuthority()
&& '/' !== ($this->uri->getPath()[0] ?? '');
}
/**
* Tells whether both URI refers to the same document.
*/
public function isSameDocument(Stringable|string $uri): bool
{
return $this->normalize(static::filterUri($uri)) === $this->normalize($this->uri);
}
/**
* Tells whether the URI contains an Internationalized Domain Name (IDN).
*/
public function hasIdn(): bool
{
return IdnaConverter::isIdn($this->uri->getHost());
}
/**
* Tells whether the URI contains an IPv4 regardless if it is mapped or native.
*/
public function hasIPv4(): bool
{
return IPv4Converter::fromEnvironment()->isIpv4($this->uri->getHost());
}
/**
* Resolves a URI against a base URI using RFC3986 rules.
*
* This method MUST retain the state of the submitted URI instance, and return
* a URI instance of the same type that contains the applied modifications.
*
* This method MUST be transparent when dealing with error and exceptions.
* It MUST not alter or silence them apart from validating its own parameters.
*/
public function resolve(Stringable|string $uri): static
{
$uri = static::formatHost(static::filterUri($uri, $this->uriFactory));
$null = $uri instanceof Psr7UriInterface ? '' : null;
if ($null !== $uri->getScheme()) {
return new static(
$uri->withPath(static::removeDotSegments($uri->getPath())),
$this->uriFactory
);
}
if ($null !== $uri->getAuthority()) {
return new static(
$uri
->withScheme($this->uri->getScheme())
->withPath(static::removeDotSegments($uri->getPath())),
$this->uriFactory
);
}
$user = $null;
$pass = null;
$userInfo = $this->uri->getUserInfo();
if (null !== $userInfo) {
[$user, $pass] = explode(':', $userInfo, 2) + [1 => null];
}
[$path, $query] = $this->resolvePathAndQuery($uri);
return new static(
$uri
->withPath($this->removeDotSegments($path))
->withQuery($query)
->withHost($this->uri->getHost())
->withPort($this->uri->getPort())
->withUserInfo($user, $pass)
->withScheme($this->uri->getScheme()),
$this->uriFactory
);
}
/**
* Relativize a URI according to a base URI.
*
* This method MUST retain the state of the submitted URI instance, and return
* a URI instance of the same type that contains the applied modifications.
*
* This method MUST be transparent when dealing with error and exceptions.
* It MUST not alter of silence them apart from validating its own parameters.
*/
public function relativize(Stringable|string $uri): static
{
$uri = static::formatHost(static::filterUri($uri, $this->uriFactory));
if ($this->canNotBeRelativize($uri)) {
return new static($uri, $this->uriFactory);
}
$null = $uri instanceof Psr7UriInterface ? '' : null;
$uri = $uri->withScheme($null)->withPort(null)->withUserInfo($null)->withHost($null);
$targetPath = $uri->getPath();
$basePath = $this->uri->getPath();
return new static(
match (true) {
$targetPath !== $basePath => $uri->withPath(static::relativizePath($targetPath, $basePath)),
static::componentEquals('query', $uri) => $uri->withPath('')->withQuery($null),
$null === $uri->getQuery() => $uri->withPath(static::formatPathWithEmptyBaseQuery($targetPath)),
default => $uri->withPath(''),
},
$this->uriFactory
);
}
final protected function computeOrigin(Psr7UriInterface|UriInterface $uri, ?string $nullValue): Psr7UriInterface|UriInterface|null
{
$scheme = $uri->getScheme();
if ('blob' !== $scheme) {
return match (true) {
isset(static::WHATWG_SPECIAL_SCHEMES[$scheme]) => $uri
->withFragment($nullValue)
->withQuery($nullValue)
->withPath('')
->withUserInfo($nullValue),
default => null,
};
}
$components = UriString::parse($uri->getPath());
if ($uri instanceof Psr7UriInterface) {
/** @var ComponentMap $components */
$components = array_map(fn ($component) => null === $component ? '' : $component, $components);
}
return match (true) {
null !== $components['scheme'] && isset(static::WHATWG_SPECIAL_SCHEMES[strtolower($components['scheme'])]) => $uri
->withFragment($nullValue)
->withQuery($nullValue)
->withPath('')
->withHost($components['host'])
->withPort($components['port'])
->withScheme($components['scheme'])
->withUserInfo($nullValue),
default => null,
};
}
/**
* Normalizes a URI for comparison; this URI string representation is not suitable for usage as per RFC guidelines.
*/
final protected function normalize(Psr7UriInterface|UriInterface $uri): string
{
$null = $uri instanceof Psr7UriInterface ? '' : null;
$path = $uri->getPath();
if ('/' === ($path[0] ?? '') || '' !== $uri->getScheme().$uri->getAuthority()) {
$path = $this->removeDotSegments($path);
}
$query = $uri->getQuery();
$pairs = null === $query ? [] : explode('&', $query);
sort($pairs);
static $regexpEncodedChars = ',%(2[D|E]|3\d|4[1-9|A-F]|5[\d|AF]|6[1-9|A-F]|7[\d|E]),i';
$value = preg_replace_callback(
$regexpEncodedChars,
static fn (array $matches): string => rawurldecode($matches[0]),
[$path, implode('&', $pairs)]
) ?? ['', $null];
[$path, $query] = $value + ['', $null];
if ($null !== $uri->getAuthority() && '' === $path) {
$path = '/';
}
return $uri
->withHost(Uri::fromComponents(['host' => $uri->getHost()])->getHost())
->withPath($path)
->withQuery([] === $pairs ? $null : $query)
->withFragment($null)
->__toString();
}
/**
* Input URI normalization to allow Stringable and string URI.
*/
final protected static function filterUri(Stringable|string $uri, UriFactoryInterface|null $uriFactory = null): Psr7UriInterface|UriInterface
{
return match (true) {
$uri instanceof UriAccess => $uri->getUri(),
$uri instanceof Psr7UriInterface,
$uri instanceof UriInterface => $uri,
$uriFactory instanceof UriFactoryInterface => $uriFactory->createUri((string) $uri),
default => Uri::new($uri),
};
}
/**
* Remove dot segments from the URI path as per RFC specification.
*/
final protected function removeDotSegments(string $path): string
{
if (!str_contains($path, '.')) {
return $path;
}
$reducer = function (array $carry, string $segment): array {
if ('..' === $segment) {
array_pop($carry);
return $carry;
}
if (!isset(static::DOT_SEGMENTS[$segment])) {
$carry[] = $segment;
}
return $carry;
};
$oldSegments = explode('/', $path);
$newPath = implode('/', array_reduce($oldSegments, $reducer(...), []));
if (isset(static::DOT_SEGMENTS[end($oldSegments)])) {
$newPath .= '/';
}
// @codeCoverageIgnoreStart
// added because some PSR-7 implementations do not respect RFC3986
if (str_starts_with($path, '/') && !str_starts_with($newPath, '/')) {
return '/'.$newPath;
}
// @codeCoverageIgnoreEnd
return $newPath;
}
/**
* Resolves an URI path and query component.
*
* @return array{0:string, 1:string|null}
*/
final protected function resolvePathAndQuery(Psr7UriInterface|UriInterface $uri): array
{
$targetPath = $uri->getPath();
$null = $uri instanceof Psr7UriInterface ? '' : null;
if (str_starts_with($targetPath, '/')) {
return [$targetPath, $uri->getQuery()];
}
if ('' === $targetPath) {
$targetQuery = $uri->getQuery();
if ($null === $targetQuery) {
$targetQuery = $this->uri->getQuery();
}
$targetPath = $this->uri->getPath();
//@codeCoverageIgnoreStart
//because some PSR-7 Uri implementations allow this RFC3986 forbidden construction
if (null !== $this->uri->getAuthority() && !str_starts_with($targetPath, '/')) {
$targetPath = '/'.$targetPath;
}
//@codeCoverageIgnoreEnd
return [$targetPath, $targetQuery];
}
$basePath = $this->uri->getPath();
if (null !== $this->uri->getAuthority() && '' === $basePath) {
$targetPath = '/'.$targetPath;
}
if ('' !== $basePath) {
$segments = explode('/', $basePath);
array_pop($segments);
if ([] !== $segments) {
$targetPath = implode('/', $segments).'/'.$targetPath;
}
}
return [$targetPath, $uri->getQuery()];
}
/**
* Tells whether the component value from both URI object equals.
*
* @pqram 'query'|'authority'|'scheme' $property
*/
final protected function componentEquals(string $property, Psr7UriInterface|UriInterface $uri): bool
{
$getComponent = function (string $property, Psr7UriInterface|UriInterface $uri): ?string {
$component = match ($property) {
'query' => $uri->getQuery(),
'authority' => $uri->getAuthority(),
default => $uri->getScheme(),
};
return match (true) {
$uri instanceof UriInterface, '' !== $component => $component,
default => null,
};
};
return $getComponent($property, $uri) === $getComponent($property, $this->uri);
}
/**
* Filter the URI object.
*/
final protected static function formatHost(Psr7UriInterface|UriInterface $uri): Psr7UriInterface|UriInterface
{
$host = $uri->getHost();
try {
$converted = IPv4Converter::fromEnvironment()->toDecimal($host);
} catch (MissingFeature) {
$converted = null;
}
if (false === filter_var($converted, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
$converted = IPv6Converter::compress($host);
}
return match (true) {
null !== $converted => $uri->withHost($converted),
'' === $host,
$uri instanceof UriInterface => $uri,
default => $uri->withHost((string) Uri::fromComponents(['host' => $host])->getHost()),
};
}
/**
* Tells whether the submitted URI object can be relativized.
*/
final protected function canNotBeRelativize(Psr7UriInterface|UriInterface $uri): bool
{
return !static::componentEquals('scheme', $uri)
|| !static::componentEquals('authority', $uri)
|| static::from($uri)->isRelativePath();
}
/**
* Relatives the URI for an authority-less target URI.
*/
final protected static function relativizePath(string $path, string $basePath): string
{
$baseSegments = static::getSegments($basePath);
$targetSegments = static::getSegments($path);
$targetBasename = array_pop($targetSegments);
array_pop($baseSegments);
foreach ($baseSegments as $offset => $segment) {
if (!isset($targetSegments[$offset]) || $segment !== $targetSegments[$offset]) {
break;
}
unset($baseSegments[$offset], $targetSegments[$offset]);
}
$targetSegments[] = $targetBasename;
return static::formatPath(
str_repeat('../', count($baseSegments)).implode('/', $targetSegments),
$basePath
);
}
/**
* returns the path segments.
*
* @return string[]
*/
final protected static function getSegments(string $path): array
{
return explode('/', match (true) {
'' === $path,
'/' !== $path[0] => $path,
default => substr($path, 1),
});
}
/**
* Formatting the path to keep a valid URI.
*/
final protected static function formatPath(string $path, string $basePath): string
{
$colonPosition = strpos($path, ':');
$slashPosition = strpos($path, '/');
return match (true) {
'' === $path => match (true) {
'' === $basePath,
'/' === $basePath => $basePath,
default => './',
},
false === $colonPosition => $path,
false === $slashPosition,
$colonPosition < $slashPosition => "./$path",
default => $path,
};
}
/**
* Formatting the path to keep a resolvable URI.
*/
final protected static function formatPathWithEmptyBaseQuery(string $path): string
{
$targetSegments = static::getSegments($path);
/** @var string $basename */
$basename = end($targetSegments);
return '' === $basename ? './' : $basename;
}
}

327
vendor/league/uri/Http.php vendored Normal file
View File

@@ -0,0 +1,327 @@
<?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;
use Deprecated;
use JsonSerializable;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\UriTemplate\TemplateCanNotBeExpanded;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
use Stringable;
/**
* @phpstan-import-type InputComponentMap from UriString
*/
final class Http implements Stringable, Psr7UriInterface, JsonSerializable
{
private readonly UriInterface $uri;
private function __construct(UriInterface $uri)
{
if (null === $uri->getScheme() && '' === $uri->getHost()) {
throw new SyntaxError('An URI without scheme cannot contain an empty host string according to PSR-7: '.$uri);
}
$port = $uri->getPort();
if (null !== $port && ($port < 0 || $port > 65535)) {
throw new SyntaxError('The URI port is outside the established TCP and UDP port ranges: '.$uri);
}
$this->uri = $this->normalizePsr7Uri($uri);
}
/**
* PSR-7 UriInterface makes the following normalization.
*
* Safely stringify input when possible for League UriInterface compatibility.
*
* Query, Fragment and User Info when undefined are normalized to the empty string
*/
private function normalizePsr7Uri(UriInterface $uri): UriInterface
{
$components = [];
if ('' === $uri->getFragment()) {
$components['fragment'] = null;
}
if ('' === $uri->getQuery()) {
$components['query'] = null;
}
if ('' === $uri->getUserInfo()) {
$components['user'] = null;
$components['pass'] = null;
}
return match ($components) {
[] => $uri,
default => Uri::fromComponents([...$uri->toComponents(), ...$components]),
};
}
/**
* Create a new instance from a string or a stringable object.
*/
public static function new(Stringable|string $uri = ''): self
{
return self::fromComponents(UriString::parse($uri));
}
/**
* Create a new instance from a hash of parse_url parts.
*
* @param InputComponentMap $components a hash representation of the URI similar
* to PHP parse_url function result
*/
public static function fromComponents(array $components): self
{
$components += [
'scheme' => null, 'user' => null, 'pass' => null, 'host' => null,
'port' => null, 'path' => '', 'query' => null, 'fragment' => null,
];
if ('' === $components['user']) {
$components['user'] = null;
}
if ('' === $components['pass']) {
$components['pass'] = null;
}
if ('' === $components['query']) {
$components['query'] = null;
}
if ('' === $components['fragment']) {
$components['fragment'] = null;
}
return new self(Uri::fromComponents($components));
}
/**
* Create a new instance from the environment.
*/
public static function fromServer(array $server): self
{
return new self(Uri::fromServer($server));
}
/**
* Create a new instance from a URI and a Base URI.
*
* The returned URI must be absolute.
*/
public static function fromBaseUri(Stringable|string $uri, Stringable|string|null $baseUri = null): self
{
return new self(Uri::fromBaseUri($uri, $baseUri));
}
/**
* Creates a new instance from a template.
*
* @throws TemplateCanNotBeExpanded if the variables are invalid or missing
* @throws UriException if the variables are invalid or missing
*/
public static function fromTemplate(Stringable|string $template, iterable $variables = []): self
{
return new self(Uri::fromTemplate($template, $variables));
}
public function getScheme(): string
{
return $this->uri->getScheme() ?? '';
}
public function getAuthority(): string
{
return $this->uri->getAuthority() ?? '';
}
public function getUserInfo(): string
{
return $this->uri->getUserInfo() ?? '';
}
public function getHost(): string
{
return $this->uri->getHost() ?? '';
}
public function getPort(): ?int
{
return $this->uri->getPort();
}
public function getPath(): string
{
return $this->uri->getPath();
}
public function getQuery(): string
{
return $this->uri->getQuery() ?? '';
}
public function getFragment(): string
{
return $this->uri->getFragment() ?? '';
}
public function __toString(): string
{
return $this->uri->toString();
}
public function jsonSerialize(): string
{
return $this->uri->toString();
}
/**
* Safely stringify input when possible for League UriInterface compatibility.
*/
private function filterInput(string $str): ?string
{
return match ('') {
$str => null,
default => $str,
};
}
private function newInstance(UriInterface $uri): self
{
return match ($this->uri->toString()) {
$uri->toString() => $this,
default => new self($uri),
};
}
public function withScheme(string $scheme): self
{
return $this->newInstance($this->uri->withScheme($this->filterInput($scheme)));
}
public function withUserInfo(string $user, ?string $password = null): self
{
return $this->newInstance($this->uri->withUserInfo($this->filterInput($user), $password));
}
public function withHost(string $host): self
{
return $this->newInstance($this->uri->withHost($this->filterInput($host)));
}
public function withPort(?int $port): self
{
return $this->newInstance($this->uri->withPort($port));
}
public function withPath(string $path): self
{
return $this->newInstance($this->uri->withPath($path));
}
public function withQuery(string $query): self
{
return $this->newInstance($this->uri->withQuery($this->filterInput($query)));
}
public function withFragment(string $fragment): self
{
return $this->newInstance($this->uri->withFragment($this->filterInput($fragment)));
}
/**
* DEPRECATION WARNING! This method will be removed in the next major point release.
*
* @deprecated Since version 7.0.0
* @codeCoverageIgnore
* @see Http::new()
*
* Create a new instance from a string.
*/
#[Deprecated(message:'use League\Uri\Http::new() instead', since:'league/uri:7.0.0')]
public static function createFromString(Stringable|string $uri = ''): self
{
return self::new($uri);
}
/**
* DEPRECATION WARNING! This method will be removed in the next major point release.
*
* @deprecated Since version 7.0.0
* @codeCoverageIgnore
* @see Http::fromComponents()
*
* Create a new instance from a hash of parse_url parts.
*
* @param InputComponentMap $components a hash representation of the URI similar
* to PHP parse_url function result
*/
#[Deprecated(message:'use League\Uri\Http::fromComponents() instead', since:'league/uri:7.0.0')]
public static function createFromComponents(array $components): self
{
return self::fromComponents($components);
}
/**
* DEPRECATION WARNING! This method will be removed in the next major point release.
*
* @deprecated Since version 7.0.0
* @codeCoverageIgnore
* @see Http::fromServer()
*
* Create a new instance from the environment.
*/
#[Deprecated(message:'use League\Uri\Http::fromServer() instead', since:'league/uri:7.0.0')]
public static function createFromServer(array $server): self
{
return self::fromServer($server);
}
/**
* DEPRECATION WARNING! This method will be removed in the next major point release.
*
* @deprecated Since version 7.0.0
* @codeCoverageIgnore
* @see Http::new()
*
* Create a new instance from a URI object.
*/
#[Deprecated(message:'use League\Uri\Http::new() instead', since:'league/uri:7.0.0')]
public static function createFromUri(Psr7UriInterface|UriInterface $uri): self
{
return self::new($uri);
}
/**
* DEPRECATION WARNING! This method will be removed in the next major point release.
*
* @deprecated Since version 7.0.0
* @codeCoverageIgnore
* @see Http::fromBaseUri()
*
* Create a new instance from a URI and a Base URI.
*
* The returned URI must be absolute.
*/
#[Deprecated(message:'use League\Uri\Http::fromBaseUri() instead', since:'league/uri:7.0.0')]
public static function createFromBaseUri(Stringable|string $uri, Stringable|string|null $baseUri = null): self
{
return self::fromBaseUri($uri, $baseUri);
}
}

25
vendor/league/uri/HttpFactory.php vendored Normal file
View File

@@ -0,0 +1,25 @@
<?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;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
final class HttpFactory implements UriFactoryInterface
{
public function createUri(string $uri = ''): UriInterface
{
return Http::new($uri);
}
}

20
vendor/league/uri/LICENSE vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2015 ignace nyamagana butera
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1328
vendor/league/uri/Uri.php vendored Normal file

File diff suppressed because it is too large Load Diff

105
vendor/league/uri/UriInfo.php vendored Normal file
View File

@@ -0,0 +1,105 @@
<?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;
use Deprecated;
use League\Uri\Contracts\UriInterface;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
/**
* @deprecated since version 7.0.0
* @codeCoverageIgnore
* @see BaseUri
*/
final class UriInfo
{
/**
* @codeCoverageIgnore
*/
private function __construct()
{
}
/**
* Tells whether the URI represents an absolute URI.
*/
#[Deprecated(message:'use League\Uri\BaseUri::isAbsolute() instead', since:'league/uri:7.0.0')]
public static function isAbsolute(Psr7UriInterface|UriInterface $uri): bool
{
return BaseUri::from($uri)->isAbsolute();
}
/**
* Tell whether the URI represents a network path.
*/
#[Deprecated(message:'use League\Uri\BaseUri::isNetworkPath() instead', since:'league/uri:7.0.0')]
public static function isNetworkPath(Psr7UriInterface|UriInterface $uri): bool
{
return BaseUri::from($uri)->isNetworkPath();
}
/**
* Tells whether the URI represents an absolute path.
*/
#[Deprecated(message:'use League\Uri\BaseUri::isAbsolutePath() instead', since:'league/uri:7.0.0')]
public static function isAbsolutePath(Psr7UriInterface|UriInterface $uri): bool
{
return BaseUri::from($uri)->isAbsolutePath();
}
/**
* Tell whether the URI represents a relative path.
*
*/
#[Deprecated(message:'use League\Uri\BaseUri::isRelativePath() instead', since:'league/uri:7.0.0')]
public static function isRelativePath(Psr7UriInterface|UriInterface $uri): bool
{
return BaseUri::from($uri)->isRelativePath();
}
/**
* Tells whether both URI refers to the same document.
*/
#[Deprecated(message:'use League\Uri\BaseUri::isSameDocument() instead', since:'league/uri:7.0.0')]
public static function isSameDocument(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $baseUri): bool
{
return BaseUri::from($baseUri)->isSameDocument($uri);
}
/**
* Returns the URI origin property as defined by WHATWG URL living standard.
*
* {@see https://url.spec.whatwg.org/#origin}
*
* For URI without a special scheme the method returns null
* For URI with the file scheme the method will return null (as this is left to the implementation decision)
* For URI with a special scheme the method returns the scheme followed by its authority (without the userinfo part)
*/
#[Deprecated(message:'use League\Uri\BaseUri::origin() instead', since:'league/uri:7.0.0')]
public static function getOrigin(Psr7UriInterface|UriInterface $uri): ?string
{
return BaseUri::from($uri)->origin()?->__toString();
}
/**
* Tells whether two URI do not share the same origin.
*
* @see UriInfo::getOrigin()
*/
#[Deprecated(message:'use League\Uri\BaseUri::isCrossOrigin() instead', since:'league/uri:7.0.0')]
public static function isCrossOrigin(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $baseUri): bool
{
return BaseUri::from($baseUri)->isCrossOrigin($uri);
}
}

56
vendor/league/uri/UriResolver.php vendored Normal file
View File

@@ -0,0 +1,56 @@
<?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;
use Deprecated;
use League\Uri\Contracts\UriInterface;
use Psr\Http\Message\UriInterface as Psr7UriInterface;
/**
* @deprecated since version 7.0.0
* @codeCoverageIgnore
* @see BaseUri
*/
final class UriResolver
{
/**
* Resolves a URI against a base URI using RFC3986 rules.
*
* This method MUST retain the state of the submitted URI instance, and return
* a URI instance of the same type that contains the applied modifications.
*
* This method MUST be transparent when dealing with error and exceptions.
* It MUST not alter or silence them apart from validating its own parameters.
*/
#[Deprecated(message:'use League\Uri\BaseUri::resolve() instead', since:'league/uri:7.0.0')]
public static function resolve(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $baseUri): Psr7UriInterface|UriInterface
{
return BaseUri::from($baseUri)->resolve($uri)->getUri();
}
/**
* Relativizes a URI according to a base URI.
*
* This method MUST retain the state of the submitted URI instance, and return
* a URI instance of the same type that contains the applied modifications.
*
* This method MUST be transparent when dealing with error and exceptions.
* It MUST not alter or silence them apart from validating its own parameters.
*/
#[Deprecated(message:'use League\Uri\BaseUri::relativize() instead', since:'league/uri:7.0.0')]
public static function relativize(Psr7UriInterface|UriInterface $uri, Psr7UriInterface|UriInterface $baseUri): Psr7UriInterface|UriInterface
{
return BaseUri::from($baseUri)->relativize($uri)->getUri();
}
}

123
vendor/league/uri/UriTemplate.php vendored Normal file
View File

@@ -0,0 +1,123 @@
<?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;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Exceptions\SyntaxError;
use League\Uri\UriTemplate\Template;
use League\Uri\UriTemplate\TemplateCanNotBeExpanded;
use League\Uri\UriTemplate\VariableBag;
use Stringable;
use function array_fill_keys;
use function array_key_exists;
/**
* Defines the URI Template syntax and the process for expanding a URI Template into a URI reference.
*
* @link https://tools.ietf.org/html/rfc6570
* @package League\Uri
* @author Ignace Nyamagana Butera <nyamsprod@gmail.com>
* @since 6.1.0
*/
final class UriTemplate
{
private readonly Template $template;
private readonly VariableBag $defaultVariables;
/**
* @throws SyntaxError if the template syntax is invalid
* @throws TemplateCanNotBeExpanded if the template or the variables are invalid
*/
public function __construct(Stringable|string $template, iterable $defaultVariables = [])
{
$this->template = $template instanceof Template ? $template : Template::new($template);
$this->defaultVariables = $this->filterVariables($defaultVariables);
}
private function filterVariables(iterable $variables): VariableBag
{
if (!$variables instanceof VariableBag) {
$variables = new VariableBag($variables);
}
return $variables
->filter(fn ($value, string|int $name) => array_key_exists(
$name,
array_fill_keys($this->template->variableNames, 1)
));
}
public function getTemplate(): string
{
return $this->template->value;
}
/**
* @return array<string>
*/
public function getVariableNames(): array
{
return $this->template->variableNames;
}
public function getDefaultVariables(): array
{
return iterator_to_array($this->defaultVariables);
}
/**
* Returns a new instance with the updated default variables.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the modified default variables.
*
* If present, variables whose name is not part of the current template
* possible variable names are removed.
*
* @throws TemplateCanNotBeExpanded if the variables are invalid
*/
public function withDefaultVariables(iterable $defaultVariables): self
{
$defaultVariables = $this->filterVariables($defaultVariables);
if ($defaultVariables == $this->defaultVariables) {
return $this;
}
return new self($this->template, $defaultVariables);
}
/**
* @throws TemplateCanNotBeExpanded if the variables are invalid
* @throws UriException if the resulting expansion cannot be converted to a UriInterface instance
*/
public function expand(iterable $variables = []): UriInterface
{
return Uri::new($this->template->expand(
$this->filterVariables($variables)->replace($this->defaultVariables)
));
}
/**
* @throws TemplateCanNotBeExpanded if the variables are invalid or missing
* @throws UriException if the resulting expansion cannot be converted to a UriInterface instance
*/
public function expandOrFail(iterable $variables = []): UriInterface
{
return Uri::new($this->template->expandOrFail(
$this->filterVariables($variables)->replace($this->defaultVariables)
));
}
}

View File

@@ -0,0 +1,99 @@
<?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\UriTemplate;
use Deprecated;
use League\Uri\Exceptions\SyntaxError;
use Stringable;
use function array_filter;
use function array_map;
use function array_unique;
use function explode;
use function implode;
/**
* @internal The class exposes the internal representation of an Expression and its usage
* @link https://www.rfc-editor.org/rfc/rfc6570#section-2.2
*/
final class Expression
{
/** @var array<VarSpecifier> */
private readonly array $varSpecifiers;
/** @var array<string> */
public readonly array $variableNames;
public readonly string $value;
private function __construct(public readonly Operator $operator, VarSpecifier ...$varSpecifiers)
{
$this->varSpecifiers = $varSpecifiers;
$this->variableNames = array_unique(
array_map(
static fn (VarSpecifier $varSpecifier): string => $varSpecifier->name,
$varSpecifiers
)
);
$this->value = '{'.$operator->value.implode(',', array_map(
static fn (VarSpecifier $varSpecifier): string => $varSpecifier->toString(),
$varSpecifiers
)).'}';
}
/**
* @throws SyntaxError if the expression is invalid
*/
public static function new(Stringable|string $expression): self
{
$parts = Operator::parseExpression($expression);
return new Expression($parts['operator'], ...array_map(
static fn (string $varSpec): VarSpecifier => VarSpecifier::new($varSpec),
explode(',', $parts['variables'])
));
}
/**
* DEPRECATION WARNING! This method will be removed in the next major point release.
*
* @throws SyntaxError if the expression is invalid
* @see Expression::new()
*
* @deprecated Since version 7.0.0
* @codeCoverageIgnore
*/
#[Deprecated(message:'use League\Uri\UriTemplate\Exppression::new() instead', since:'league/uri:7.0.0')]
public static function createFromString(Stringable|string $expression): self
{
return self::new($expression);
}
public function expand(VariableBag $variables): string
{
$expanded = implode(
$this->operator->separator(),
array_filter(
array_map(
fn (VarSpecifier $varSpecifier): string => $this->operator->expand($varSpecifier, $variables),
$this->varSpecifiers
),
static fn ($value): bool => '' !== $value
)
);
return match ('') {
$expanded => '',
default => $this->operator->first().$expanded,
};
}
}

View File

@@ -0,0 +1,225 @@
<?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\UriTemplate;
use League\Uri\Encoder;
use League\Uri\Exceptions\SyntaxError;
use Stringable;
use function implode;
use function is_array;
use function preg_match;
use function rawurlencode;
use function str_contains;
use function substr;
/**
* Processing behavior according to the expression type operator.
*
* @internal The class exposes the internal representation of an Operator and its usage
*
* @link https://www.rfc-editor.org/rfc/rfc6570#section-2.2
* @link https://tools.ietf.org/html/rfc6570#appendix-A
*/
enum Operator: string
{
/**
* Expression regular expression pattern.
*
* @link https://tools.ietf.org/html/rfc6570#section-2.2
*/
private const REGEXP_EXPRESSION = '/^\{(?:(?<operator>[\.\/;\?&\=,\!@\|\+#])?(?<variables>[^\}]*))\}$/';
/**
* Reserved Operator characters.
*
* @link https://tools.ietf.org/html/rfc6570#section-2.2
*/
private const RESERVED_OPERATOR = '=,!@|';
case None = '';
case ReservedChars = '+';
case Label = '.';
case Path = '/';
case PathParam = ';';
case Query = '?';
case QueryPair = '&';
case Fragment = '#';
public function first(): string
{
return match ($this) {
self::None, self::ReservedChars => '',
default => $this->value,
};
}
public function separator(): string
{
return match ($this) {
self::None, self::ReservedChars, self::Fragment => ',',
self::Query, self::QueryPair => '&',
default => $this->value,
};
}
public function isNamed(): bool
{
return match ($this) {
self::Query, self::PathParam, self::QueryPair => true,
default => false,
};
}
/**
* Removes percent encoding on reserved characters (used with + and # modifiers).
*/
public function decode(string $var): string
{
return match ($this) {
Operator::ReservedChars, Operator::Fragment => (string) Encoder::encodeQueryOrFragment($var),
default => rawurlencode($var),
};
}
/**
* @throws SyntaxError if the expression is invalid
* @throws SyntaxError if the operator used in the expression is invalid
* @throws SyntaxError if the contained variable specifiers are invalid
*
* @return array{operator:Operator, variables:string}
*/
public static function parseExpression(Stringable|string $expression): array
{
$expression = (string) $expression;
if (1 !== preg_match(self::REGEXP_EXPRESSION, $expression, $parts)) {
throw new SyntaxError('The expression "'.$expression.'" is invalid.');
}
/** @var array{operator:string, variables:string} $parts */
$parts = $parts + ['operator' => ''];
if ('' !== $parts['operator'] && str_contains(self::RESERVED_OPERATOR, $parts['operator'])) {
throw new SyntaxError('The operator used in the expression "'.$expression.'" is reserved.');
}
return [
'operator' => self::from($parts['operator']),
'variables' => $parts['variables'],
];
}
/**
* Replaces an expression with the given variables.
*
* @throws TemplateCanNotBeExpanded if the variables is an array and a ":" modifier needs to be applied
* @throws TemplateCanNotBeExpanded if the variables contains nested array values
*/
public function expand(VarSpecifier $varSpecifier, VariableBag $variables): string
{
$value = $variables->fetch($varSpecifier->name);
if (null === $value) {
return '';
}
[$expanded, $actualQuery] = $this->inject($value, $varSpecifier);
if (!$actualQuery) {
return $expanded;
}
if ('&' !== $this->separator() && '' === $expanded) {
return $varSpecifier->name;
}
return $varSpecifier->name.'='.$expanded;
}
/**
* @param string|array<string> $value
*
* @return array{0:string, 1:bool}
*/
private function inject(array|string $value, VarSpecifier $varSpec): array
{
if (is_array($value)) {
return $this->replaceList($value, $varSpec);
}
if (':' === $varSpec->modifier) {
$value = substr($value, 0, $varSpec->position);
}
return [$this->decode($value), $this->isNamed()];
}
/**
* Expands an expression using a list of values.
*
* @param array<string> $value
*
* @throws TemplateCanNotBeExpanded if the variables is an array and a ":" modifier needs to be applied
*
* @return array{0:string, 1:bool}
*/
private function replaceList(array $value, VarSpecifier $varSpec): array
{
if (':' === $varSpec->modifier) {
throw TemplateCanNotBeExpanded::dueToUnableToProcessValueListWithPrefix($varSpec->name);
}
if ([] === $value) {
return ['', false];
}
$pairs = [];
$isList = array_is_list($value);
$useQuery = $this->isNamed();
foreach ($value as $key => $var) {
if (!$isList) {
$key = rawurlencode((string) $key);
}
$var = $this->decode($var);
if ('*' === $varSpec->modifier) {
if (!$isList) {
$var = $key.'='.$var;
} elseif ($key > 0 && $useQuery) {
$var = $varSpec->name.'='.$var;
}
}
$pairs[$key] = $var;
}
if ('*' === $varSpec->modifier) {
if (!$isList) {
// Don't prepend the value name when using the `explode` modifier with an associative array.
$useQuery = false;
}
return [implode($this->separator(), $pairs), $useQuery];
}
if (!$isList) {
// When an associative array is encountered and the `explode` modifier is not set, then
// the result must be a comma separated list of keys followed by their respective values.
$retVal = [];
foreach ($pairs as $offset => $data) {
$retVal[$offset] = $offset.','.$data;
}
$pairs = $retVal;
}
return [implode(',', $pairs), $useQuery];
}
}

View File

@@ -0,0 +1,145 @@
<?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\UriTemplate;
use Deprecated;
use League\Uri\Exceptions\SyntaxError;
use Stringable;
use function array_filter;
use function array_map;
use function array_reduce;
use function array_unique;
use function preg_match_all;
use function preg_replace;
use function str_contains;
use function str_replace;
use const PREG_SET_ORDER;
/**
* @internal The class exposes the internal representation of a Template and its usage
*/
final class Template implements Stringable
{
/**
* Expression regular expression pattern.
*/
private const REGEXP_EXPRESSION_DETECTOR = '/(?<expression>\{[^}]*})/x';
/** @var array<Expression> */
private readonly array $expressions;
/** @var array<string> */
public readonly array $variableNames;
private function __construct(public readonly string $value, Expression ...$expressions)
{
$this->expressions = $expressions;
$this->variableNames = array_unique(
array_merge(
...array_map(
static fn (Expression $expression): array => $expression->variableNames,
$expressions
)
)
);
}
/**
* @throws SyntaxError if the template contains invalid expressions
* @throws SyntaxError if the template contains invalid variable specification
*/
public static function new(Stringable|string $template): self
{
$template = (string) $template;
/** @var string $remainder */
$remainder = preg_replace(self::REGEXP_EXPRESSION_DETECTOR, '', $template);
if (str_contains($remainder, '{') || str_contains($remainder, '}')) {
throw new SyntaxError('The template "'.$template.'" contains invalid expressions.');
}
preg_match_all(self::REGEXP_EXPRESSION_DETECTOR, $template, $founds, PREG_SET_ORDER);
return new self($template, ...array_values(
array_reduce($founds, function (array $carry, array $found): array {
if (!isset($carry[$found['expression']])) {
$carry[$found['expression']] = Expression::new($found['expression']);
}
return $carry;
}, [])
));
}
/**
* @throws TemplateCanNotBeExpanded if the variables are invalid
*/
public function expand(iterable $variables = []): string
{
if (!$variables instanceof VariableBag) {
$variables = new VariableBag($variables);
}
return $this->expandAll($variables);
}
/**
* @throws TemplateCanNotBeExpanded if the variables are invalid or missing
*/
public function expandOrFail(iterable $variables = []): string
{
if (!$variables instanceof VariableBag) {
$variables = new VariableBag($variables);
}
$missing = array_filter($this->variableNames, fn (string $name): bool => !isset($variables[$name]));
if ([] !== $missing) {
throw TemplateCanNotBeExpanded::dueToMissingVariables(...$missing);
}
return $this->expandAll($variables);
}
private function expandAll(VariableBag $variables): string
{
return array_reduce(
$this->expressions,
fn (string $uri, Expression $expr): string => str_replace($expr->value, $expr->expand($variables), $uri),
$this->value
);
}
public function __toString(): string
{
return $this->value;
}
/**
* DEPRECATION WARNING! This method will be removed in the next major point release.
*
* @throws SyntaxError if the template contains invalid expressions
* @throws SyntaxError if the template contains invalid variable specification
* @deprecated Since version 7.0.0
* @codeCoverageIgnore
* @see Template::new()
*
* Create a new instance from a string.
*
*/
#[Deprecated(message:'use League\Uri\UriTemplate\Template::new() instead', since:'league/uri:7.0.0')]
public static function createFromString(Stringable|string $template): self
{
return self::new($template);
}
}

View File

@@ -0,0 +1,44 @@
<?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\UriTemplate;
use InvalidArgumentException;
use League\Uri\Contracts\UriException;
class TemplateCanNotBeExpanded extends InvalidArgumentException implements UriException
{
public readonly array $variablesNames;
public function __construct(string $message = '', string ...$variableNames)
{
parent::__construct($message, 0, null);
$this->variablesNames = $variableNames;
}
public static function dueToUnableToProcessValueListWithPrefix(string $variableName): self
{
return new self('The ":" modifier cannot be applied on "'.$variableName.'" since it is a list of values.', $variableName);
}
public static function dueToNestedListOfValue(string $variableName): self
{
return new self('The "'.$variableName.'" cannot be a nested list.', $variableName);
}
public static function dueToMissingVariables(string ...$variableNames): self
{
return new self('The following required variables are missing: `'.implode('`, `', $variableNames).'`.', ...$variableNames);
}
}

View File

@@ -0,0 +1,73 @@
<?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\UriTemplate;
use League\Uri\Exceptions\SyntaxError;
use function preg_match;
/**
* @internal The class exposes the internal representation of a Var Specifier
* @link https://www.rfc-editor.org/rfc/rfc6570#section-2.3
*/
final class VarSpecifier
{
/**
* Variables specification regular expression pattern.
*
* @link https://tools.ietf.org/html/rfc6570#section-2.3
*/
private const REGEXP_VARSPEC = '/^(?<name>(?:[A-z0-9_\.]|%[0-9a-fA-F]{2})+)(?<modifier>\:(?<position>\d+)|\*)?$/';
private const MODIFIER_POSITION_MAX_POSITION = 10_000;
private function __construct(
public readonly string $name,
public readonly string $modifier,
public readonly int $position
) {
}
public static function new(string $specification): self
{
if (1 !== preg_match(self::REGEXP_VARSPEC, $specification, $parsed)) {
throw new SyntaxError('The variable specification "'.$specification.'" is invalid.');
}
$properties = ['name' => $parsed['name'], 'modifier' => $parsed['modifier'] ?? '', 'position' => $parsed['position'] ?? ''];
if ('' !== $properties['position']) {
$properties['position'] = (int) $properties['position'];
$properties['modifier'] = ':';
}
if ('' === $properties['position']) {
$properties['position'] = 0;
}
if (self::MODIFIER_POSITION_MAX_POSITION <= $properties['position']) {
throw new SyntaxError('The variable specification "'.$specification.'" is invalid the position modifier must be lower than 10000.');
}
return new self($properties['name'], $properties['modifier'], $properties['position']);
}
public function toString(): string
{
return $this->name.$this->modifier.match (true) {
0 < $this->position => $this->position,
default => '',
};
}
}

View File

@@ -0,0 +1,151 @@
<?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\UriTemplate;
use ArrayAccess;
use Closure;
use Countable;
use IteratorAggregate;
use Stringable;
use Traversable;
use function array_filter;
use function is_bool;
use function is_scalar;
use const ARRAY_FILTER_USE_BOTH;
/**
* @internal The class exposes the internal representation of variable bags
*
* @phpstan-type InputValue string|bool|int|float|array<string|bool|int|float>
*
* @implements ArrayAccess<string, InputValue>
* @implements IteratorAggregate<string, InputValue>
*/
final class VariableBag implements ArrayAccess, Countable, IteratorAggregate
{
/**
* @var array<string,string|array<string>>
*/
private array $variables = [];
/**
* @param iterable<array-key, InputValue> $variables
*/
public function __construct(iterable $variables = [])
{
foreach ($variables as $name => $value) {
$this->assign((string) $name, $value);
}
}
public function count(): int
{
return count($this->variables);
}
public function getIterator(): Traversable
{
yield from $this->variables;
}
public function offsetExists(mixed $offset): bool
{
return array_key_exists($offset, $this->variables);
}
public function offsetUnset(mixed $offset): void
{
unset($this->variables[$offset]);
}
public function offsetSet(mixed $offset, mixed $value): void
{
$this->assign($offset, $value); /* @phpstan-ignore-line */
}
public function offsetGet(mixed $offset): mixed
{
return $this->fetch($offset);
}
/**
* Tells whether the bag is empty or not.
*/
public function isEmpty(): bool
{
return [] === $this->variables;
}
/**
* Tells whether the bag is empty or not.
*/
public function isNotEmpty(): bool
{
return [] !== $this->variables;
}
/**
* Fetches the variable value if none found returns null.
*
* @return null|string|array<string>
*/
public function fetch(string $name): null|string|array
{
return $this->variables[$name] ?? null;
}
/**
* @param Stringable|InputValue $value
*/
public function assign(string $name, Stringable|string|bool|int|float|array|null $value): void
{
$this->variables[$name] = $this->normalizeValue($value, $name, true);
}
/**
* @param Stringable|InputValue $value
*
* @throws TemplateCanNotBeExpanded if the value contains nested list
*/
private function normalizeValue(
Stringable|string|float|int|bool|array|null $value,
string $name,
bool $isNestedListAllowed
): array|string {
return match (true) {
is_bool($value) => true === $value ? '1' : '0',
(null === $value || is_scalar($value) || $value instanceof Stringable) => (string) $value,
!$isNestedListAllowed => throw TemplateCanNotBeExpanded::dueToNestedListOfValue($name),
default => array_map(fn ($var): array|string => self::normalizeValue($var, $name, false), $value),
};
}
/**
* Replaces elements from passed variables into the current instance.
*/
public function replace(VariableBag $variables): self
{
return new self($this->variables + $variables->variables);
}
/**
* Filters elements using the closure.
*/
public function filter(Closure $fn): self
{
return new self(array_filter($this->variables, $fn, ARRAY_FILTER_USE_BOTH));
}
}

76
vendor/league/uri/composer.json vendored Normal file
View File

@@ -0,0 +1,76 @@
{
"name": "league/uri",
"type": "library",
"description" : "URI manipulation library",
"keywords": [
"url",
"uri",
"rfc3986",
"rfc3987",
"rfc6570",
"psr-7",
"parse_url",
"http",
"https",
"ws",
"ftp",
"data-uri",
"file-uri",
"middleware",
"parse_str",
"query-string",
"querystring",
"hostname",
"uri-template"
],
"license": "MIT",
"homepage": "https://uri.thephpleague.com",
"authors": [
{
"name" : "Ignace Nyamagana Butera",
"email" : "nyamsprod@gmail.com",
"homepage" : "https://nyamsprod.com"
}
],
"support": {
"forum": "https://thephpleague.slack.com",
"docs": "https://uri.thephpleague.com",
"issues": "https://github.com/thephpleague/uri-src/issues"
},
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/nyamsprod"
}
],
"require": {
"php": "^8.1",
"league/uri-interfaces": "^7.5"
},
"autoload": {
"psr-4": {
"League\\Uri\\": ""
}
},
"conflict": {
"league/uri-schemes": "^1.0"
},
"suggest": {
"ext-bcmath": "to improve IPV4 host parsing",
"ext-fileinfo": "to create Data URI from file contennts",
"ext-gmp": "to improve IPV4 host parsing",
"ext-intl": "to handle IDN host with the best performance",
"jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain",
"league/uri-components" : "Needed to easily manipulate URI objects components",
"php-64bit": "to improve IPV4 host parsing",
"symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
},
"extra": {
"branch-alias": {
"dev-master": "7.x-dev"
}
},
"config": {
"sort-packages": true
}
}