aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/league
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/league')
-rw-r--r--vendor/league/uri-interfaces/Contracts/AuthorityInterface.php93
-rw-r--r--vendor/league/uri-interfaces/Contracts/DataPathInterface.php95
-rw-r--r--vendor/league/uri-interfaces/Contracts/DomainHostInterface.php117
-rw-r--r--vendor/league/uri-interfaces/Contracts/FragmentInterface.php22
-rw-r--r--vendor/league/uri-interfaces/Contracts/HostInterface.php56
-rw-r--r--vendor/league/uri-interfaces/Contracts/IpHostInterface.php49
-rw-r--r--vendor/league/uri-interfaces/Contracts/PathInterface.php90
-rw-r--r--vendor/league/uri-interfaces/Contracts/PortInterface.php22
-rw-r--r--vendor/league/uri-interfaces/Contracts/QueryInterface.php253
-rw-r--r--vendor/league/uri-interfaces/Contracts/SegmentedPathInterface.php149
-rw-r--r--vendor/league/uri-interfaces/Contracts/UriAccess.php26
-rw-r--r--vendor/league/uri-interfaces/Contracts/UriComponentInterface.php75
-rw-r--r--vendor/league/uri-interfaces/Contracts/UriException.php20
-rw-r--r--vendor/league/uri-interfaces/Contracts/UriInterface.php314
-rw-r--r--vendor/league/uri-interfaces/Contracts/UserInfoInterface.php62
-rw-r--r--vendor/league/uri-interfaces/Encoder.php176
-rw-r--r--vendor/league/uri-interfaces/Exceptions/ConversionFailed.php46
-rw-r--r--vendor/league/uri-interfaces/Exceptions/MissingFeature.php21
-rw-r--r--vendor/league/uri-interfaces/Exceptions/OffsetOutOfBounds.php18
-rw-r--r--vendor/league/uri-interfaces/Exceptions/SyntaxError.php21
-rw-r--r--vendor/league/uri-interfaces/FeatureDetection.php56
-rw-r--r--vendor/league/uri-interfaces/IPv4/BCMathCalculator.php85
-rw-r--r--vendor/league/uri-interfaces/IPv4/Calculator.php95
-rw-r--r--vendor/league/uri-interfaces/IPv4/Converter.php303
-rw-r--r--vendor/league/uri-interfaces/IPv4/GMPCalculator.php70
-rw-r--r--vendor/league/uri-interfaces/IPv4/NativeCalculator.php60
-rw-r--r--vendor/league/uri-interfaces/IPv6/Converter.php137
-rw-r--r--vendor/league/uri-interfaces/Idna/Converter.php218
-rw-r--r--vendor/league/uri-interfaces/Idna/Error.php64
-rw-r--r--vendor/league/uri-interfaces/Idna/Option.php179
-rw-r--r--vendor/league/uri-interfaces/Idna/Result.php64
-rw-r--r--vendor/league/uri-interfaces/KeyValuePair/Converter.php209
-rw-r--r--vendor/league/uri-interfaces/LICENSE20
-rw-r--r--vendor/league/uri-interfaces/QueryString.php276
-rw-r--r--vendor/league/uri-interfaces/UriString.php513
-rw-r--r--vendor/league/uri-interfaces/composer.json70
-rw-r--r--vendor/league/uri/BaseUri.php658
-rw-r--r--vendor/league/uri/Http.php327
-rw-r--r--vendor/league/uri/HttpFactory.php25
-rw-r--r--vendor/league/uri/LICENSE20
-rw-r--r--vendor/league/uri/Uri.php1328
-rw-r--r--vendor/league/uri/UriInfo.php105
-rw-r--r--vendor/league/uri/UriResolver.php56
-rw-r--r--vendor/league/uri/UriTemplate.php123
-rw-r--r--vendor/league/uri/UriTemplate/Expression.php99
-rw-r--r--vendor/league/uri/UriTemplate/Operator.php225
-rw-r--r--vendor/league/uri/UriTemplate/Template.php145
-rw-r--r--vendor/league/uri/UriTemplate/TemplateCanNotBeExpanded.php44
-rw-r--r--vendor/league/uri/UriTemplate/VarSpecifier.php73
-rw-r--r--vendor/league/uri/UriTemplate/VariableBag.php151
-rw-r--r--vendor/league/uri/composer.json76
51 files changed, 7599 insertions, 0 deletions
diff --git a/vendor/league/uri-interfaces/Contracts/AuthorityInterface.php b/vendor/league/uri-interfaces/Contracts/AuthorityInterface.php
new file mode 100644
index 000000000..9c364f33e
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/AuthorityInterface.php
@@ -0,0 +1,93 @@
+<?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\Contracts;
+
+use League\Uri\Exceptions\MissingFeature;
+use League\Uri\Exceptions\SyntaxError;
+use Stringable;
+
+interface AuthorityInterface extends UriComponentInterface
+{
+ /**
+ * Returns the host component of the authority.
+ */
+ public function getHost(): ?string;
+
+ /**
+ * Returns the port component of the authority.
+ */
+ public function getPort(): ?int;
+
+ /**
+ * Returns the user information component of the authority.
+ */
+ public function getUserInfo(): ?string;
+
+ /**
+ * Returns an associative array containing all the Authority components.
+ *
+ * The returned a hashmap similar to PHP's parse_url return value
+ *
+ * @link https://tools.ietf.org/html/rfc3986
+ *
+ * @return array{user: ?string, pass : ?string, host: ?string, port: ?int}
+ */
+ public function components(): array;
+
+ /**
+ * Return an instance with the specified host.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified host.
+ *
+ * A null value provided for the host is equivalent to removing the host
+ * information.
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in an object in invalid state.
+ * @throws MissingFeature for component or transformations
+ * requiring IDN support when IDN support is not present
+ * or misconfigured.
+ */
+ public function withHost(Stringable|string|null $host): self;
+
+ /**
+ * Return an instance with the specified port.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified port.
+ *
+ * A null value provided for the port is equivalent to removing the port
+ * information.
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in an object in invalid state.
+ */
+ public function withPort(?int $port): self;
+
+ /**
+ * Return an instance with the specified user information.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified user information.
+ *
+ * Password is optional, but the user information MUST include the
+ * user; a null value for the user is equivalent to removing user
+ * information.
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in an object in invalid state.
+ */
+ public function withUserInfo(Stringable|string|null $user, Stringable|string|null $password = null): self;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/DataPathInterface.php b/vendor/league/uri-interfaces/Contracts/DataPathInterface.php
new file mode 100644
index 000000000..01f9c402a
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/DataPathInterface.php
@@ -0,0 +1,95 @@
+<?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\Contracts;
+
+use SplFileObject;
+use Stringable;
+
+interface DataPathInterface extends PathInterface
+{
+ /**
+ * Retrieve the data mime type associated to the URI.
+ *
+ * If no mimetype is present, this method MUST return the default mimetype 'text/plain'.
+ *
+ * @see http://tools.ietf.org/html/rfc2397#section-2
+ */
+ public function getMimeType(): string;
+
+ /**
+ * Retrieve the parameters associated with the Mime Type of the URI.
+ *
+ * If no parameters is present, this method MUST return the default parameter 'charset=US-ASCII'.
+ *
+ * @see http://tools.ietf.org/html/rfc2397#section-2
+ */
+ public function getParameters(): string;
+
+ /**
+ * Retrieve the mediatype associated with the URI.
+ *
+ * If no mediatype is present, this method MUST return the default parameter 'text/plain;charset=US-ASCII'.
+ *
+ * @see http://tools.ietf.org/html/rfc2397#section-3
+ *
+ * @return string The URI scheme.
+ */
+ public function getMediaType(): string;
+
+ /**
+ * Retrieves the data string.
+ *
+ * Retrieves the data part of the path. If no data part is provided return
+ * an empty string
+ */
+ public function getData(): string;
+
+ /**
+ * Tells whether the data is binary safe encoded.
+ */
+ public function isBinaryData(): bool;
+
+ /**
+ * Save the data to a specific file.
+ */
+ public function save(string $path, string $mode = 'w'): SplFileObject;
+
+ /**
+ * Returns an instance where the data part is base64 encoded.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance where the data part is base64 encoded
+ */
+ public function toBinary(): self;
+
+ /**
+ * Returns an instance where the data part is url encoded following RFC3986 rules.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance where the data part is url encoded
+ */
+ public function toAscii(): self;
+
+ /**
+ * Return an instance with the specified mediatype parameters.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified mediatype parameters.
+ *
+ * Users must provide encoded characters.
+ *
+ * An empty parameters value is equivalent to removing the parameter.
+ */
+ public function withParameters(Stringable|string $parameters): self;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/DomainHostInterface.php b/vendor/league/uri-interfaces/Contracts/DomainHostInterface.php
new file mode 100644
index 000000000..66758044d
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/DomainHostInterface.php
@@ -0,0 +1,117 @@
+<?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\Contracts;
+
+use Countable;
+use Iterator;
+use IteratorAggregate;
+use League\Uri\Exceptions\SyntaxError;
+use Stringable;
+
+/**
+ * @extends IteratorAggregate<string>
+ */
+interface DomainHostInterface extends Countable, HostInterface, IteratorAggregate
+{
+ /**
+ * Returns the labels total number.
+ */
+ public function count(): int;
+
+ /**
+ * Iterate over the Domain labels.
+ *
+ * @return Iterator<string>
+ */
+ public function getIterator(): Iterator;
+
+ /**
+ * Retrieves a single host label.
+ *
+ * If the label offset has not been set, returns the null value.
+ */
+ public function get(int $offset): ?string;
+
+ /**
+ * Returns the associated key for a specific label or all the keys.
+ *
+ * @return int[]
+ */
+ public function keys(?string $label = null): array;
+
+ /**
+ * Tells whether the domain is absolute.
+ */
+ public function isAbsolute(): bool;
+
+ /**
+ * Prepends a label to the host.
+ */
+ public function prepend(Stringable|string $label): self;
+
+ /**
+ * Appends a label to the host.
+ */
+ public function append(Stringable|string $label): self;
+
+ /**
+ * Extracts a slice of $length elements starting at position $offset from the host.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the selected slice.
+ *
+ * If $length is null it returns all elements from $offset to the end of the Domain.
+ */
+ public function slice(int $offset, ?int $length = null): self;
+
+ /**
+ * Returns an instance with its Root label.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-3.2.2
+ */
+ public function withRootLabel(): self;
+
+ /**
+ * Returns an instance without its Root label.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-3.2.2
+ */
+ public function withoutRootLabel(): self;
+
+ /**
+ * Returns an instance with the modified label.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the new label
+ *
+ * If $key is non-negative, the added label will be the label at $key position from the start.
+ * If $key is negative, the added label will be the label at $key position from the end.
+ *
+ * @throws SyntaxError If the key is invalid
+ */
+ public function withLabel(int $key, Stringable|string $label): self;
+
+ /**
+ * Returns an instance without the specified label.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the modified component
+ *
+ * If $key is non-negative, the removed label will be the label at $key position from the start.
+ * If $key is negative, the removed label will be the label at $key position from the end.
+ *
+ * @throws SyntaxError If the key is invalid
+ */
+ public function withoutLabel(int ...$keys): self;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/FragmentInterface.php b/vendor/league/uri-interfaces/Contracts/FragmentInterface.php
new file mode 100644
index 000000000..3d80f0661
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/FragmentInterface.php
@@ -0,0 +1,22 @@
+<?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\Contracts;
+
+interface FragmentInterface extends UriComponentInterface
+{
+ /**
+ * Returns the decoded fragment.
+ */
+ public function decoded(): ?string;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/HostInterface.php b/vendor/league/uri-interfaces/Contracts/HostInterface.php
new file mode 100644
index 000000000..16212bfe1
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/HostInterface.php
@@ -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\Contracts;
+
+interface HostInterface extends UriComponentInterface
+{
+ /**
+ * Returns the ascii representation.
+ */
+ public function toAscii(): ?string;
+
+ /**
+ * Returns the unicode representation.
+ */
+ public function toUnicode(): ?string;
+
+ /**
+ * Returns the IP version.
+ *
+ * If the host is a not an IP this method will return null
+ */
+ public function getIpVersion(): ?string;
+
+ /**
+ * Returns the IP component If the Host is an IP address.
+ *
+ * If the host is a not an IP this method will return null
+ */
+ public function getIp(): ?string;
+
+ /**
+ * Tells whether the host is a domain name.
+ */
+ public function isDomain(): bool;
+
+ /**
+ * Tells whether the host is an IP Address.
+ */
+ public function isIp(): bool;
+
+ /**
+ * Tells whether the host is a registered name.
+ */
+ public function isRegisteredName(): bool;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/IpHostInterface.php b/vendor/league/uri-interfaces/Contracts/IpHostInterface.php
new file mode 100644
index 000000000..7daac22b3
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/IpHostInterface.php
@@ -0,0 +1,49 @@
+<?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\Contracts;
+
+interface IpHostInterface extends HostInterface
+{
+ /**
+ * Tells whether the host is an IPv4 address.
+ */
+ public function isIpv4(): bool;
+
+ /**
+ * Tells whether the host is an IPv6 address.
+ */
+ public function isIpv6(): bool;
+
+ /**
+ * Tells whether the host is an IPv6 address.
+ */
+ public function isIpFuture(): bool;
+
+ /**
+ * Tells whether the host has a ZoneIdentifier.
+ *
+ * @see http://tools.ietf.org/html/rfc6874#section-4
+ */
+ public function hasZoneIdentifier(): bool;
+
+ /**
+ * Returns a host without its zone identifier according to RFC6874.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance without the host zone identifier according to RFC6874
+ *
+ * @see http://tools.ietf.org/html/rfc6874#section-4
+ */
+ public function withoutZoneIdentifier(): self;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/PathInterface.php b/vendor/league/uri-interfaces/Contracts/PathInterface.php
new file mode 100644
index 000000000..f99b76270
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/PathInterface.php
@@ -0,0 +1,90 @@
+<?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\Contracts;
+
+use League\Uri\Exceptions\SyntaxError;
+
+interface PathInterface extends UriComponentInterface
+{
+ /**
+ * Returns the decoded path.
+ */
+ public function decoded(): string;
+
+ /**
+ * Tells whether the path is absolute or relative.
+ */
+ public function isAbsolute(): bool;
+
+ /**
+ * Tells whether the path has a trailing slash.
+ */
+ public function hasTrailingSlash(): bool;
+
+ /**
+ * Returns an instance without dot segments.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the path component normalized by removing
+ * the dot segment.
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in a object in invalid state.
+ */
+ public function withoutDotSegments(): self;
+
+ /**
+ * Returns an instance with a leading slash.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the path component with a leading slash
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in a object in invalid state.
+ */
+ public function withLeadingSlash(): self;
+
+ /**
+ * Returns an instance without a leading slash.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the path component without a leading slash
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in a object in invalid state.
+ */
+ public function withoutLeadingSlash(): self;
+
+ /**
+ * 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
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in a object in invalid state.
+ */
+ public function withTrailingSlash(): self;
+
+ /**
+ * Returns an instance without a trailing slash.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the path component without a trailing slash
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in a object in invalid state.
+ */
+ public function withoutTrailingSlash(): self;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/PortInterface.php b/vendor/league/uri-interfaces/Contracts/PortInterface.php
new file mode 100644
index 000000000..7230c4ad1
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/PortInterface.php
@@ -0,0 +1,22 @@
+<?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\Contracts;
+
+interface PortInterface extends UriComponentInterface
+{
+ /**
+ * Returns the integer representation of the Port.
+ */
+ public function toInt(): ?int;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/QueryInterface.php b/vendor/league/uri-interfaces/Contracts/QueryInterface.php
new file mode 100644
index 000000000..fed486e34
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/QueryInterface.php
@@ -0,0 +1,253 @@
+<?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\Contracts;
+
+use Countable;
+use Deprecated;
+use Iterator;
+use IteratorAggregate;
+use Stringable;
+
+/**
+ * @extends IteratorAggregate<array{0:string, 1:string|null}>
+ *
+ * @method self withoutPairByKey(string ...$keys) Returns an instance without pairs with the specified keys.
+ * @method self withoutPairByValue(Stringable|string|int|bool|null ...$values) Returns an instance without pairs with the specified values.
+ * @method self withoutPairByKeyValue(string $key, Stringable|string|int|bool|null $value) Returns an instance without pairs with the specified key/value pair
+ * @method bool hasPair(string $key, ?string $value) Tells whether the pair exists in the query.
+ * @method ?string toFormData() Returns the string representation using the applicat/www-form-urlencoded rules
+ * @method ?string toRFC3986() Returns the string representation using RFC3986 rules
+ */
+interface QueryInterface extends Countable, IteratorAggregate, UriComponentInterface
+{
+ /**
+ * Returns the query separator.
+ *
+ * @return non-empty-string
+ */
+ public function getSeparator(): string;
+
+ /**
+ * Returns the number of key/value pairs present in the object.
+ */
+ public function count(): int;
+
+ /**
+ * Returns an iterator allowing to go through all key/value pairs contained in this object.
+ *
+ * The pair is represented as an array where the first value is the pair key
+ * and the second value the pair value.
+ *
+ * The key of each pair is a string
+ * The value of each pair is a scalar or the null value
+ *
+ * @return Iterator<int, array{0:string, 1:string|null}>
+ */
+ public function getIterator(): Iterator;
+
+ /**
+ * Returns an iterator allowing to go through all key/value pairs contained in this object.
+ *
+ * The return type is as an Iterator where its offset is the pair key and its value the pair value.
+ *
+ * The key of each pair is a string
+ * The value of each pair is a scalar or the null value
+ *
+ * @return iterable<string, string|null>
+ */
+ public function pairs(): iterable;
+
+ /**
+ * Tells whether a list of pair with a specific key exists.
+ *
+ * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
+ */
+ public function has(string ...$keys): bool;
+
+ /**
+ * Returns the first value associated to the given pair name.
+ *
+ * If no value is found null is returned
+ *
+ * @see https://url.spec.whatwg.org/#dom-urlsearchparams-get
+ */
+ public function get(string $key): ?string;
+
+ /**
+ * Returns all the values associated to the given pair name as an array or all
+ * the instance pairs.
+ *
+ * If no value is found an empty array is returned
+ *
+ * @see https://url.spec.whatwg.org/#dom-urlsearchparams-getall
+ *
+ * @return array<int, string|null>
+ */
+ public function getAll(string $key): array;
+
+ /**
+ * Returns the store PHP variables as elements of an array.
+ *
+ * The result is similar as PHP parse_str when used with its
+ * second argument with the difference that variable names are
+ * not mangled.
+ *
+ * @see http://php.net/parse_str
+ * @see https://wiki.php.net/rfc/on_demand_name_mangling
+ *
+ * @return array the collection of stored PHP variables or the empty array if no input is given,
+ */
+ public function parameters(): array;
+
+ /**
+ * Returns the value attached to the specific key.
+ *
+ * The result is similar to PHP parse_str with the difference that variable
+ * names are not mangled.
+ *
+ * If a key is submitted it will return the value attached to it or null
+ *
+ * @see http://php.net/parse_str
+ * @see https://wiki.php.net/rfc/on_demand_name_mangling
+ *
+ * @return mixed the collection of stored PHP variables or the empty array if no input is given,
+ * the single value of a stored PHP variable or null if the variable is not present in the collection
+ */
+ public function parameter(string $name): mixed;
+
+ /**
+ * Tells whether a list of variable with specific names exists.
+ *
+ * @see https://url.spec.whatwg.org/#dom-urlsearchparams-has
+ */
+ public function hasParameter(string ...$names): bool;
+
+ /**
+ * Returns the RFC1738 encoded query.
+ */
+ public function toRFC1738(): ?string;
+
+ /**
+ * Returns an instance with a different separator.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the query component with a different separator
+ */
+ public function withSeparator(string $separator): self;
+
+ /**
+ * Returns an instance with the new pairs set to it.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the modified query
+ *
+ * @see ::withPair
+ */
+ public function merge(Stringable|string $query): self;
+
+ /**
+ * Returns an instance with the new pairs appended to it.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the modified query
+ *
+ * If the pair already exists the value will be added to it.
+ */
+ public function append(Stringable|string $query): self;
+
+ /**
+ * Returns a new instance with a specified key/value pair appended as a new pair.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the modified query
+ */
+ public function appendTo(string $key, Stringable|string|int|bool|null $value): self;
+
+ /**
+ * Sorts the query string by offset, maintaining offset to data correlations.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the modified query
+ *
+ * @see https://url.spec.whatwg.org/#dom-urlsearchparams-sort
+ */
+ public function sort(): self;
+
+ /**
+ * Returns an instance without duplicate key/value pair.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the query component normalized by removing
+ * duplicate pairs whose key/value are the same.
+ */
+ public function withoutDuplicates(): self;
+
+ /**
+ * Returns an instance without empty key/value where the value is the null value.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the query component normalized by removing
+ * empty pairs.
+ *
+ * A pair is considered empty if its value is equal to the null value
+ */
+ public function withoutEmptyPairs(): self;
+
+ /**
+ * Returns an instance where numeric indices associated to PHP's array like key are removed.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the query component normalized so that numeric indexes
+ * are removed from the pair key value.
+ *
+ * i.e.: toto[3]=bar[3]&foo=bar becomes toto[]=bar[3]&foo=bar
+ */
+ public function withoutNumericIndices(): self;
+
+ /**
+ * Returns an instance with a new key/value pair added to it.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the modified query
+ *
+ * If the pair already exists the value will replace the existing value.
+ *
+ * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set
+ */
+ public function withPair(string $key, Stringable|string|int|float|bool|null $value): self;
+
+ /**
+ * DEPRECATION WARNING! This method will be removed in the next major point release.
+ *
+ * @deprecated Since version 7.3.0
+ * @codeCoverageIgnore
+ * @see QueryInterface::withoutPairByKey()
+ *
+ * Returns an instance without the specified keys.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the modified component
+ */
+ #[Deprecated(message:'use League\Uri\Contracts\QueryInterface::withoutPairByKey() instead', since:'league/uri-interfaces:7.3.0')]
+ public function withoutPair(string ...$keys): self;
+
+ /**
+ * Returns an instance without the specified params.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the modified component without PHP's value.
+ * PHP's mangled is not taken into account.
+ */
+ public function withoutParameters(string ...$names): self;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/SegmentedPathInterface.php b/vendor/league/uri-interfaces/Contracts/SegmentedPathInterface.php
new file mode 100644
index 000000000..fa5a78d22
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/SegmentedPathInterface.php
@@ -0,0 +1,149 @@
+<?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\Contracts;
+
+use Countable;
+use Iterator;
+use IteratorAggregate;
+use League\Uri\Exceptions\SyntaxError;
+use Stringable;
+
+/**
+ * @extends IteratorAggregate<string>
+ */
+interface SegmentedPathInterface extends Countable, IteratorAggregate, PathInterface
+{
+ /**
+ * Returns the total number of segments in the path.
+ */
+ public function count(): int;
+
+ /**
+ * Iterate over the path segment.
+ *
+ * @return Iterator<string>
+ */
+ public function getIterator(): Iterator;
+
+ /**
+ * Returns parent directory's path.
+ */
+ public function getDirname(): string;
+
+ /**
+ * Returns the path basename.
+ */
+ public function getBasename(): string;
+
+ /**
+ * Returns the basename extension.
+ */
+ public function getExtension(): string;
+
+ /**
+ * Retrieves a single path segment.
+ *
+ * If the segment offset has not been set, returns null.
+ */
+ public function get(int $offset): ?string;
+
+ /**
+ * Returns the associated key for a specific segment.
+ *
+ * If a value is specified only the keys associated with
+ * the given value will be returned
+ *
+ * @return array<int>
+ */
+ public function keys(Stringable|string|null $segment = null): array;
+
+ /**
+ * Appends a segment to the path.
+ */
+ public function append(Stringable|string $segment): self;
+
+ /**
+ * Extracts a slice of $length elements starting at position $offset from the host.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the selected slice.
+ *
+ * If $length is null it returns all elements from $offset to the end of the Path.
+ */
+ public function slice(int $offset, ?int $length = null): self;
+
+ /**
+ * Prepends a segment to the path.
+ */
+ public function prepend(Stringable|string $segment): self;
+
+ /**
+ * Returns an instance with the modified segment.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the new segment
+ *
+ * If $key is non-negative, the added segment will be the segment at $key position from the start.
+ * If $key is negative, the added segment will be the segment at $key position from the end.
+ *
+ * @throws SyntaxError If the key is invalid
+ */
+ public function withSegment(int $key, Stringable|string $segment): self;
+
+ /**
+ * Returns an instance without the specified segment.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the modified component
+ *
+ * If $key is non-negative, the removed segment will be the segment at $key position from the start.
+ * If $key is negative, the removed segment will be the segment at $key position from the end.
+ *
+ * @throws SyntaxError If the key is invalid
+ */
+ public function withoutSegment(int ...$keys): self;
+
+ /**
+ * Returns an instance without duplicate delimiters.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the path component normalized by removing
+ * multiple consecutive empty segment
+ */
+ public function withoutEmptySegments(): self;
+
+ /**
+ * Returns an instance with the specified parent directory's path.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the extension basename modified.
+ */
+ public function withDirname(Stringable|string $path): self;
+
+ /**
+ * Returns an instance with the specified basename.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the extension basename modified.
+ */
+ public function withBasename(Stringable|string $basename): self;
+
+ /**
+ * Returns an instance with the specified basename extension.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the extension basename modified.
+ */
+ public function withExtension(Stringable|string $extension): self;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/UriAccess.php b/vendor/league/uri-interfaces/Contracts/UriAccess.php
new file mode 100644
index 000000000..7c37cdadb
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/UriAccess.php
@@ -0,0 +1,26 @@
+<?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\Contracts;
+
+use Psr\Http\Message\UriInterface as Psr7UriInterface;
+
+interface UriAccess
+{
+ public function getUri(): UriInterface|Psr7UriInterface;
+
+ /**
+ * Returns the RFC3986 string representation of the complete URI.
+ */
+ public function getUriString(): string;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/UriComponentInterface.php b/vendor/league/uri-interfaces/Contracts/UriComponentInterface.php
new file mode 100644
index 000000000..e478516e8
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/UriComponentInterface.php
@@ -0,0 +1,75 @@
+<?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\Contracts;
+
+use JsonSerializable;
+use Stringable;
+
+interface UriComponentInterface extends JsonSerializable, Stringable
+{
+ /**
+ * Returns the instance string representation.
+ *
+ * If the instance is defined, the value returned MUST be percent-encoded,
+ * but MUST NOT double-encode any characters. To determine what characters
+ * to encode, please refer to RFC 3986, Sections 2 and 3.
+ *
+ * If the instance is not defined null is returned
+ */
+ public function value(): ?string;
+
+ /**
+ * Returns the instance string representation.
+ *
+ * If the instance is defined, the value returned MUST be percent-encoded,
+ * but MUST NOT double-encode any characters. To determine what characters
+ * to encode, please refer to RFC 3986, Sections 2 and 3.
+ *
+ * If the instance is not defined an empty string is returned
+ */
+ public function toString(): string;
+
+ /**
+ * Returns the instance string representation.
+ *
+ * If the instance is defined, the value returned MUST be percent-encoded,
+ * but MUST NOT double-encode any characters. To determine what characters
+ * to encode, please refer to RFC 3986, Sections 2 and 3.
+ *
+ * If the instance is not defined an empty string is returned
+ */
+ public function __toString(): string;
+
+ /**
+ * Returns the instance json representation.
+ *
+ * If the instance is defined, the value returned MUST be percent-encoded,
+ * but MUST NOT double-encode any characters. To determine what characters
+ * to encode, please refer to RFC 3986 or RFC 1738.
+ *
+ * If the instance is not defined null is returned
+ */
+ public function jsonSerialize(): ?string;
+
+ /**
+ * Returns the instance string representation with its optional URI delimiters.
+ *
+ * The value returned MUST be percent-encoded, but MUST NOT double-encode any
+ * characters. To determine what characters to encode, please refer to RFC 3986,
+ * Sections 2 and 3.
+ *
+ * If the instance is not defined an empty string is returned
+ */
+ public function getUriComponent(): string;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/UriException.php b/vendor/league/uri-interfaces/Contracts/UriException.php
new file mode 100644
index 000000000..c0fec2a12
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/UriException.php
@@ -0,0 +1,20 @@
+<?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\Contracts;
+
+use Throwable;
+
+interface UriException extends Throwable
+{
+}
diff --git a/vendor/league/uri-interfaces/Contracts/UriInterface.php b/vendor/league/uri-interfaces/Contracts/UriInterface.php
new file mode 100644
index 000000000..1fde6b966
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/UriInterface.php
@@ -0,0 +1,314 @@
+<?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\Contracts;
+
+use JsonSerializable;
+use League\Uri\Exceptions\MissingFeature;
+use League\Uri\Exceptions\SyntaxError;
+use League\Uri\UriString;
+use Stringable;
+
+/**
+ * @phpstan-import-type ComponentMap from UriString
+ *
+ * @method string|null getUsername() returns the user component of the URI.
+ * @method string|null getPassword() returns the scheme-specific information about how to gain authorization to access the resource.
+ * @method array toComponents() returns an associative array containing all the URI components.
+ */
+interface UriInterface extends JsonSerializable, Stringable
+{
+ /**
+ * Returns the string representation as a URI reference.
+ *
+ * @see http://tools.ietf.org/html/rfc3986#section-4.1
+ */
+ public function __toString(): string;
+
+ /**
+ * Returns the string representation as a URI reference.
+ *
+ * @see http://tools.ietf.org/html/rfc3986#section-4.1
+ */
+ public function toString(): string;
+
+ /**
+ * Returns the string representation as a URI reference.
+ *
+ * @see http://tools.ietf.org/html/rfc3986#section-4.1
+ * @see ::__toString
+ */
+ public function jsonSerialize(): string;
+
+ /**
+ * Retrieve the scheme component of the URI.
+ *
+ * If no scheme is present, this method MUST return a null value.
+ *
+ * The value returned MUST be normalized to lowercase, per RFC 3986
+ * Section 3.1.
+ *
+ * The trailing ":" character is not part of the scheme and MUST NOT be
+ * added.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-3.1
+ */
+ public function getScheme(): ?string;
+
+ /**
+ * Retrieve the authority component of the URI.
+ *
+ * If no scheme is present, this method MUST return a null value.
+ *
+ * If the port component is not set or is the standard port for the current
+ * scheme, it SHOULD NOT be included.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-3.2
+ */
+ public function getAuthority(): ?string;
+
+ /**
+ * Retrieve the user information component of the URI.
+ *
+ * If no scheme is present, this method MUST return a null value.
+ *
+ * If a user is present in the URI, this will return that value;
+ * additionally, if the password is also present, it will be appended to the
+ * user value, with a colon (":") separating the values.
+ *
+ * The trailing "@" character is not part of the user information and MUST
+ * NOT be added.
+ */
+ public function getUserInfo(): ?string;
+
+ /**
+ * Retrieve the host component of the URI.
+ *
+ * If no host is present this method MUST return a null value.
+ *
+ * The value returned MUST be normalized to lowercase, per RFC 3986
+ * Section 3.2.2.
+ *
+ * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
+ */
+ public function getHost(): ?string;
+
+ /**
+ * Retrieve the port component of the URI.
+ *
+ * If a port is present, and it is non-standard for the current scheme,
+ * this method MUST return it as an integer. If the port is the standard port
+ * used with the current scheme, this method SHOULD return null.
+ *
+ * If no port is present, and no scheme is present, this method MUST return
+ * a null value.
+ *
+ * If no port is present, but a scheme is present, this method MAY return
+ * the standard port for that scheme, but SHOULD return null.
+ */
+ public function getPort(): ?int;
+
+ /**
+ * Retrieve the path component of the URI.
+ *
+ * The path can either be empty or absolute (starting with a slash) or
+ * rootless (not starting with a slash). Implementations MUST support all
+ * three syntaxes.
+ *
+ * Normally, the empty path "" and absolute path "/" are considered equal as
+ * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
+ * do this normalization because in contexts with a trimmed base path, e.g.
+ * the front controller, this difference becomes significant. It's the task
+ * of the user to handle both "" and "/".
+ *
+ * The value returned MUST be percent-encoded, but MUST NOT double-encode
+ * any characters. To determine what characters to encode, please refer to
+ * RFC 3986, Sections 2 and 3.3.
+ *
+ * As an example, if the value should include a slash ("/") not intended as
+ * delimiter between path segments, that value MUST be passed in encoded
+ * form (e.g., "%2F") to the instance.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-2
+ * @see https://tools.ietf.org/html/rfc3986#section-3.3
+ */
+ public function getPath(): string;
+
+ /**
+ * Retrieve the query string of the URI.
+ *
+ * If no host is present this method MUST return a null value.
+ *
+ * The leading "?" character is not part of the query and MUST NOT be
+ * added.
+ *
+ * The value returned MUST be percent-encoded, but MUST NOT double-encode
+ * any characters. To determine what characters to encode, please refer to
+ * RFC 3986, Sections 2 and 3.4.
+ *
+ * As an example, if a value in a key/value pair of the query string should
+ * include an ampersand ("&") not intended as a delimiter between values,
+ * that value MUST be passed in encoded form (e.g., "%26") to the instance.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-2
+ * @see https://tools.ietf.org/html/rfc3986#section-3.4
+ */
+ public function getQuery(): ?string;
+
+ /**
+ * Retrieve the fragment component of the URI.
+ *
+ * If no host is present this method MUST return a null value.
+ *
+ * The leading "#" character is not part of the fragment and MUST NOT be
+ * added.
+ *
+ * The value returned MUST be percent-encoded, but MUST NOT double-encode
+ * any characters. To determine what characters to encode, please refer to
+ * RFC 3986, Sections 2 and 3.5.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-2
+ * @see https://tools.ietf.org/html/rfc3986#section-3.5
+ */
+ public function getFragment(): ?string;
+
+ /**
+ * Returns an associative array containing all the URI components.
+ *
+ * The returned array is similar to PHP's parse_url return value with the following
+ * differences:
+ *
+ * <ul>
+ * <li>All components are present in the returned array</li>
+ * <li>Empty and undefined component are treated differently. And empty component is
+ * set to the empty string while an undefined component is set to the `null` value.</li>
+ * </ul>
+ *
+ * @link https://tools.ietf.org/html/rfc3986
+ *
+ * @return ComponentMap
+ */
+ public function getComponents(): array;
+
+ /**
+ * Return an instance with the specified scheme.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified scheme.
+ *
+ * A null value provided for the scheme is equivalent to removing the scheme
+ * information.
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in an object in invalid state.
+ */
+ public function withScheme(Stringable|string|null $scheme): self;
+
+ /**
+ * Return an instance with the specified user information.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified user information.
+ *
+ * Password is optional, but the user information MUST include the
+ * user; a null value for the user is equivalent to removing user
+ * information.
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in an object in invalid state.
+ */
+ public function withUserInfo(Stringable|string|null $user, Stringable|string|null $password = null): self;
+
+ /**
+ * Return an instance with the specified host.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified host.
+ *
+ * A null value provided for the host is equivalent to removing the host
+ * information.
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in an object in invalid state.
+ * @throws MissingFeature for component or transformations
+ * requiring IDN support when IDN support is not present
+ * or misconfigured.
+ */
+ public function withHost(Stringable|string|null $host): self;
+
+ /**
+ * Return an instance with the specified port.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified port.
+ *
+ * A null value provided for the port is equivalent to removing the port
+ * information.
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in an object in invalid state.
+ */
+ public function withPort(?int $port): self;
+
+ /**
+ * Return an instance with the specified path.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified path.
+ *
+ * The path can either be empty or absolute (starting with a slash) or
+ * rootless (not starting with a slash). Implementations MUST support all
+ * three syntaxes.
+ *
+ * Users can provide both encoded and decoded path characters.
+ * Implementations ensure the correct encoding as outlined in getPath().
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in an object in invalid state.
+ */
+ public function withPath(Stringable|string $path): self;
+
+ /**
+ * Return an instance with the specified query string.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified query string.
+ *
+ * Users can provide both encoded and decoded query characters.
+ * Implementations ensure the correct encoding as outlined in getQuery().
+ *
+ * A null value provided for the query is equivalent to removing the query
+ * information.
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in an object in invalid state.
+ */
+ public function withQuery(Stringable|string|null $query): self;
+
+ /**
+ * Return an instance with the specified URI fragment.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified URI fragment.
+ *
+ * Users can provide both encoded and decoded fragment characters.
+ * Implementations ensure the correct encoding as outlined in getFragment().
+ *
+ * A null value provided for the fragment is equivalent to removing the fragment
+ * information.
+ *
+ * @throws SyntaxError for invalid component or transformations
+ * that would result in an object in invalid state.
+ */
+ public function withFragment(Stringable|string|null $fragment): self;
+}
diff --git a/vendor/league/uri-interfaces/Contracts/UserInfoInterface.php b/vendor/league/uri-interfaces/Contracts/UserInfoInterface.php
new file mode 100644
index 000000000..bd36b86de
--- /dev/null
+++ b/vendor/league/uri-interfaces/Contracts/UserInfoInterface.php
@@ -0,0 +1,62 @@
+<?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\Contracts;
+
+use Stringable;
+
+interface UserInfoInterface extends UriComponentInterface
+{
+ /**
+ * Returns the user component part.
+ */
+ public function getUser(): ?string;
+
+ /**
+ * Returns the pass component part.
+ */
+ public function getPass(): ?string;
+
+ /**
+ * Returns an associative array containing all the User Info components.
+ *
+ * The returned a hashmap similar to PHP's parse_url return value
+ *
+ * @link https://tools.ietf.org/html/rfc3986
+ *
+ * @return array{user: ?string, pass : ?string}
+ */
+ public function components(): array;
+
+ /**
+ * Returns an instance with the specified user and/or pass.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified new username
+ * otherwise it returns the same instance unchanged.
+ *
+ * A variable equal to null is equivalent to removing the complete user information.
+ */
+ public function withUser(Stringable|string|null $username): self;
+
+ /**
+ * Returns an instance with the specified user and/or pass.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified password if the user is specified
+ * otherwise it returns the same instance unchanged.
+ *
+ * An empty user is equivalent to removing the user information.
+ */
+ public function withPass(Stringable|string|null $password): self;
+}
diff --git a/vendor/league/uri-interfaces/Encoder.php b/vendor/league/uri-interfaces/Encoder.php
new file mode 100644
index 000000000..4324e03c8
--- /dev/null
+++ b/vendor/league/uri-interfaces/Encoder.php
@@ -0,0 +1,176 @@
+<?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 Closure;
+use League\Uri\Contracts\UriComponentInterface;
+use League\Uri\Exceptions\SyntaxError;
+use SensitiveParameter;
+use Stringable;
+
+use function preg_match;
+use function preg_replace_callback;
+use function rawurldecode;
+use function rawurlencode;
+use function strtoupper;
+
+final class Encoder
+{
+ private const REGEXP_CHARS_INVALID = '/[\x00-\x1f\x7f]/';
+ private const REGEXP_CHARS_ENCODED = ',%[A-Fa-f0-9]{2},';
+ private const REGEXP_CHARS_PREVENTS_DECODING = ',%
+ 2[A-F|1-2|4-9]|
+ 3[0-9|B|D]|
+ 4[1-9|A-F]|
+ 5[0-9|A|F]|
+ 6[1-9|A-F]|
+ 7[0-9|E]
+ ,ix';
+ private const REGEXP_PART_SUBDELIM = "\!\$&'\(\)\*\+,;\=%";
+ private const REGEXP_PART_UNRESERVED = 'A-Za-z\d_\-.~';
+ private const REGEXP_PART_ENCODED = '%(?![A-Fa-f\d]{2})';
+
+ /**
+ * Encode User.
+ *
+ * All generic delimiters MUST be encoded
+ */
+ public static function encodeUser(Stringable|string|null $component): ?string
+ {
+ static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.']+|'.self::REGEXP_PART_ENCODED.'/';
+
+ return self::encode($component, $pattern);
+ }
+
+ /**
+ * Encode Password.
+ *
+ * Generic delimiters ":" MUST NOT be encoded
+ */
+ public static function encodePassword(#[SensitiveParameter] Stringable|string|null $component): ?string
+ {
+ static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.':]+|'.self::REGEXP_PART_ENCODED.'/';
+
+ return self::encode($component, $pattern);
+ }
+
+ /**
+ * Encode Path.
+ *
+ * Generic delimiters ":", "@", and "/" MUST NOT be encoded
+ */
+ public static function encodePath(Stringable|string|null $component): string
+ {
+ static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.':@\/]+|'.self::REGEXP_PART_ENCODED.'/';
+
+ return (string) self::encode($component, $pattern);
+ }
+
+ /**
+ * Encode Query or Fragment.
+ *
+ * Generic delimiters ":", "@", "?", and "/" MUST NOT be encoded
+ */
+ public static function encodeQueryOrFragment(Stringable|string|null $component): ?string
+ {
+ static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.self::REGEXP_PART_SUBDELIM.':@\/?]+|'.self::REGEXP_PART_ENCODED.'/';
+
+ return self::encode($component, $pattern);
+ }
+
+ public static function encodeQueryKeyValue(mixed $component): ?string
+ {
+ static $pattern = '/[^'.self::REGEXP_PART_UNRESERVED.']+|'.self::REGEXP_PART_ENCODED.'/';
+
+ $encodeMatches = static fn (array $matches): string => match (1) {
+ preg_match('/[^'.self::REGEXP_PART_UNRESERVED.']/', rawurldecode($matches[0])) => rawurlencode($matches[0]),
+ default => $matches[0],
+ };
+
+ $component = self::filterComponent($component);
+
+ return match (true) {
+ !is_scalar($component) => throw new SyntaxError(sprintf('A pair key/value must be a scalar value `%s` given.', gettype($component))),
+ 1 === preg_match(self::REGEXP_CHARS_INVALID, $component) => rawurlencode($component),
+ 1 === preg_match($pattern, $component) => (string) preg_replace_callback($pattern, $encodeMatches(...), $component),
+ default => $component,
+ };
+ }
+
+ /**
+ * Decodes the URI component without decoding the unreserved characters which are already encoded.
+ */
+ public static function decodePartial(Stringable|string|int|null $component): ?string
+ {
+ $decodeMatches = static fn (array $matches): string => match (1) {
+ preg_match(self::REGEXP_CHARS_PREVENTS_DECODING, $matches[0]) => strtoupper($matches[0]),
+ default => rawurldecode($matches[0]),
+ };
+
+ return self::decode($component, $decodeMatches);
+ }
+
+ /**
+ * Decodes all the URI component characters.
+ */
+ public static function decodeAll(Stringable|string|int|null $component): ?string
+ {
+ $decodeMatches = static fn (array $matches): string => rawurldecode($matches[0]);
+
+ return self::decode($component, $decodeMatches);
+ }
+
+ private static function filterComponent(mixed $component): ?string
+ {
+ return match (true) {
+ true === $component => '1',
+ false === $component => '0',
+ $component instanceof UriComponentInterface => $component->value(),
+ $component instanceof Stringable,
+ is_scalar($component) => (string) $component,
+ null === $component => null,
+ default => throw new SyntaxError(sprintf('The component must be a scalar value `%s` given.', gettype($component))),
+ };
+ }
+
+ private static function encode(Stringable|string|int|bool|null $component, string $pattern): ?string
+ {
+ $component = self::filterComponent($component);
+ $encodeMatches = static fn (array $matches): string => match (1) {
+ preg_match('/[^'.self::REGEXP_PART_UNRESERVED.']/', rawurldecode($matches[0])) => rawurlencode($matches[0]),
+ default => $matches[0],
+ };
+
+ return match (true) {
+ null === $component,
+ '' === $component => $component,
+ default => (string) preg_replace_callback($pattern, $encodeMatches(...), $component),
+ };
+ }
+
+ /**
+ * Decodes all the URI component characters.
+ */
+ private static function decode(Stringable|string|int|null $component, Closure $decodeMatches): ?string
+ {
+ $component = self::filterComponent($component);
+
+ return match (true) {
+ null === $component => null,
+ 1 === preg_match(self::REGEXP_CHARS_INVALID, $component) => throw new SyntaxError('Invalid component string: '.$component.'.'),
+ 1 === preg_match(self::REGEXP_CHARS_ENCODED, $component) => preg_replace_callback(self::REGEXP_CHARS_ENCODED, $decodeMatches(...), $component),
+ default => $component,
+ };
+ }
+}
diff --git a/vendor/league/uri-interfaces/Exceptions/ConversionFailed.php b/vendor/league/uri-interfaces/Exceptions/ConversionFailed.php
new file mode 100644
index 000000000..973ffb320
--- /dev/null
+++ b/vendor/league/uri-interfaces/Exceptions/ConversionFailed.php
@@ -0,0 +1,46 @@
+<?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\Exceptions;
+
+use League\Uri\Idna\Error;
+use League\Uri\Idna\Result;
+use Stringable;
+
+final class ConversionFailed extends SyntaxError
+{
+ private function __construct(
+ string $message,
+ private readonly string $host,
+ private readonly Result $result
+ ) {
+ parent::__construct($message);
+ }
+
+ public static function dueToIdnError(Stringable|string $host, Result $result): self
+ {
+ $reasons = array_map(fn (Error $error): string => $error->description(), $result->errors());
+
+ return new self('Host `'.$host.'` is invalid: '.implode('; ', $reasons).'.', (string) $host, $result);
+ }
+
+ public function getHost(): string
+ {
+ return $this->host;
+ }
+
+ public function getResult(): Result
+ {
+ return $this->result;
+ }
+}
diff --git a/vendor/league/uri-interfaces/Exceptions/MissingFeature.php b/vendor/league/uri-interfaces/Exceptions/MissingFeature.php
new file mode 100644
index 000000000..034e969fc
--- /dev/null
+++ b/vendor/league/uri-interfaces/Exceptions/MissingFeature.php
@@ -0,0 +1,21 @@
+<?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\Exceptions;
+
+use League\Uri\Contracts\UriException;
+use RuntimeException;
+
+class MissingFeature extends RuntimeException implements UriException
+{
+}
diff --git a/vendor/league/uri-interfaces/Exceptions/OffsetOutOfBounds.php b/vendor/league/uri-interfaces/Exceptions/OffsetOutOfBounds.php
new file mode 100644
index 000000000..737a5cc24
--- /dev/null
+++ b/vendor/league/uri-interfaces/Exceptions/OffsetOutOfBounds.php
@@ -0,0 +1,18 @@
+<?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\Exceptions;
+
+class OffsetOutOfBounds extends SyntaxError
+{
+}
diff --git a/vendor/league/uri-interfaces/Exceptions/SyntaxError.php b/vendor/league/uri-interfaces/Exceptions/SyntaxError.php
new file mode 100644
index 000000000..f44cca6c9
--- /dev/null
+++ b/vendor/league/uri-interfaces/Exceptions/SyntaxError.php
@@ -0,0 +1,21 @@
+<?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\Exceptions;
+
+use InvalidArgumentException;
+use League\Uri\Contracts\UriException;
+
+class SyntaxError extends InvalidArgumentException implements UriException
+{
+}
diff --git a/vendor/league/uri-interfaces/FeatureDetection.php b/vendor/league/uri-interfaces/FeatureDetection.php
new file mode 100644
index 000000000..b3e9b09cd
--- /dev/null
+++ b/vendor/league/uri-interfaces/FeatureDetection.php
@@ -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 finfo;
+use League\Uri\Exceptions\MissingFeature;
+use League\Uri\IPv4\Calculator;
+
+use const PHP_INT_SIZE;
+
+/**
+ * Allow detecting features needed to make the packages work.
+ */
+final class FeatureDetection
+{
+ public static function supportsFileDetection(): void
+ {
+ static $isSupported = null;
+ $isSupported = $isSupported ?? class_exists(finfo::class);
+
+ if (!$isSupported) {
+ throw new MissingFeature('Support for file type detection requires the `fileinfo` extension.');
+ }
+ }
+
+ public static function supportsIdn(): void
+ {
+ static $isSupported = null;
+ $isSupported = $isSupported ?? (function_exists('\idn_to_ascii') && defined('\INTL_IDNA_VARIANT_UTS46'));
+
+ if (!$isSupported) {
+ throw new MissingFeature('Support for IDN host requires the `intl` extension for best performance or run "composer require symfony/polyfill-intl-idn" to install a polyfill.');
+ }
+ }
+
+ public static function supportsIPv4Conversion(): void
+ {
+ static $isSupported = null;
+ $isSupported = $isSupported ?? (extension_loaded('gmp') || extension_loaded('bcmath') || (4 < PHP_INT_SIZE));
+
+ if (!$isSupported) {
+ throw new MissingFeature('A '.Calculator::class.' implementation could not be automatically loaded. To perform IPv4 conversion use a x.64 PHP build or install one of the following extension GMP or BCMath. You can also ship your own implmentation.');
+ }
+ }
+}
diff --git a/vendor/league/uri-interfaces/IPv4/BCMathCalculator.php b/vendor/league/uri-interfaces/IPv4/BCMathCalculator.php
new file mode 100644
index 000000000..b12ac9954
--- /dev/null
+++ b/vendor/league/uri-interfaces/IPv4/BCMathCalculator.php
@@ -0,0 +1,85 @@
+<?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\IPv4;
+
+use function bcadd;
+use function bccomp;
+use function bcdiv;
+use function bcmod;
+use function bcmul;
+use function bcpow;
+use function bcsub;
+use function str_split;
+
+final class BCMathCalculator implements Calculator
+{
+ private const SCALE = 0;
+ private const CONVERSION_TABLE = [
+ '0' => '0', '1' => '1', '2' => '2', '3' => '3',
+ '4' => '4', '5' => '5', '6' => '6', '7' => '7',
+ '8' => '8', '9' => '9', 'a' => '10', 'b' => '11',
+ 'c' => '12', 'd' => '13', 'e' => '14', 'f' => '15',
+ ];
+
+ public function baseConvert(mixed $value, int $base): string
+ {
+ $value = (string) $value;
+ if (10 === $base) {
+ return $value;
+ }
+
+ $base = (string) $base;
+ $decimal = '0';
+ foreach (str_split($value) as $char) {
+ $decimal = bcadd($this->multiply($decimal, $base), self::CONVERSION_TABLE[$char], self::SCALE);
+ }
+
+ return $decimal;
+ }
+
+ public function pow(mixed $value, int $exponent): string
+ {
+ return bcpow((string) $value, (string) $exponent, self::SCALE);
+ }
+
+ public function compare(mixed $value1, $value2): int
+ {
+ return bccomp((string) $value1, (string) $value2, self::SCALE);
+ }
+
+ public function multiply(mixed $value1, $value2): string
+ {
+ return bcmul((string) $value1, (string) $value2, self::SCALE);
+ }
+
+ public function div(mixed $value, mixed $base): string
+ {
+ return bcdiv((string) $value, (string) $base, self::SCALE);
+ }
+
+ public function mod(mixed $value, mixed $base): string
+ {
+ return bcmod((string) $value, (string) $base, self::SCALE);
+ }
+
+ public function add(mixed $value1, mixed $value2): string
+ {
+ return bcadd((string) $value1, (string) $value2, self::SCALE);
+ }
+
+ public function sub(mixed $value1, mixed $value2): string
+ {
+ return bcsub((string) $value1, (string) $value2, self::SCALE);
+ }
+}
diff --git a/vendor/league/uri-interfaces/IPv4/Calculator.php b/vendor/league/uri-interfaces/IPv4/Calculator.php
new file mode 100644
index 000000000..78a3c333b
--- /dev/null
+++ b/vendor/league/uri-interfaces/IPv4/Calculator.php
@@ -0,0 +1,95 @@
+<?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\IPv4;
+
+interface Calculator
+{
+ /**
+ * Add numbers.
+ *
+ * @param mixed $value1 a number that will be added to $value2
+ * @param mixed $value2 a number that will be added to $value1
+ *
+ * @return mixed the addition result
+ */
+ public function add(mixed $value1, mixed $value2);
+
+ /**
+ * Subtract one number from another.
+ *
+ * @param mixed $value1 a number that will be subtracted of $value2
+ * @param mixed $value2 a number that will be subtracted to $value1
+ *
+ * @return mixed the subtraction result
+ */
+ public function sub(mixed $value1, mixed $value2);
+
+ /**
+ * Multiply numbers.
+ *
+ * @param mixed $value1 a number that will be multiplied by $value2
+ * @param mixed $value2 a number that will be multiplied by $value1
+ *
+ * @return mixed the multiplication result
+ */
+ public function multiply(mixed $value1, mixed $value2);
+
+ /**
+ * Divide numbers.
+ *
+ * @param mixed $value The number being divided.
+ * @param mixed $base The number that $value is being divided by.
+ *
+ * @return mixed the result of the division
+ */
+ public function div(mixed $value, mixed $base);
+
+ /**
+ * Raise an number to the power of exponent.
+ *
+ * @param mixed $value scalar, the base to use
+ *
+ * @return mixed the value raised to the power of exp.
+ */
+ public function pow(mixed $value, int $exponent);
+
+ /**
+ * Returns the int point remainder (modulo) of the division of the arguments.
+ *
+ * @param mixed $value The dividend
+ * @param mixed $base The divisor
+ *
+ * @return mixed the remainder
+ */
+ public function mod(mixed $value, mixed $base);
+
+ /**
+ * Number comparison.
+ *
+ * @param mixed $value1 the first value
+ * @param mixed $value2 the second value
+ *
+ * @return int Returns < 0 if value1 is less than value2; > 0 if value1 is greater than value2, and 0 if they are equal.
+ */
+ public function compare(mixed $value1, mixed $value2): int;
+
+ /**
+ * Get the decimal integer value of a variable.
+ *
+ * @param mixed $value The scalar value being converted to an integer
+ *
+ * @return mixed the integer value
+ */
+ public function baseConvert(mixed $value, int $base);
+}
diff --git a/vendor/league/uri-interfaces/IPv4/Converter.php b/vendor/league/uri-interfaces/IPv4/Converter.php
new file mode 100644
index 000000000..71c0bb9fc
--- /dev/null
+++ b/vendor/league/uri-interfaces/IPv4/Converter.php
@@ -0,0 +1,303 @@
+<?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\IPv4;
+
+use League\Uri\Exceptions\MissingFeature;
+use League\Uri\FeatureDetection;
+use Stringable;
+
+use function array_pop;
+use function count;
+use function explode;
+use function extension_loaded;
+use function ltrim;
+use function preg_match;
+use function str_ends_with;
+use function substr;
+
+use const FILTER_FLAG_IPV4;
+use const FILTER_FLAG_IPV6;
+use const FILTER_VALIDATE_IP;
+
+final class Converter
+{
+ private const REGEXP_IPV4_HOST = '/
+ (?(DEFINE) # . is missing as it is used to separate labels
+ (?<hexadecimal>0x[[:xdigit:]]*)
+ (?<octal>0[0-7]*)
+ (?<decimal>\d+)
+ (?<ipv4_part>(?:(?&hexadecimal)|(?&octal)|(?&decimal))*)
+ )
+ ^(?:(?&ipv4_part)\.){0,3}(?&ipv4_part)\.?$
+ /x';
+ private const REGEXP_IPV4_NUMBER_PER_BASE = [
+ '/^0x(?<number>[[:xdigit:]]*)$/' => 16,
+ '/^0(?<number>[0-7]*)$/' => 8,
+ '/^(?<number>\d+)$/' => 10,
+ ];
+
+ private const IPV6_6TO4_PREFIX = '2002:';
+ private const IPV4_MAPPED_PREFIX = '::ffff:';
+
+ private readonly mixed $maxIPv4Number;
+
+ public function __construct(
+ private readonly Calculator $calculator
+ ) {
+ $this->maxIPv4Number = $calculator->sub($calculator->pow(2, 32), 1);
+ }
+
+ /**
+ * Returns an instance using a GMP calculator.
+ */
+ public static function fromGMP(): self
+ {
+ return new self(new GMPCalculator());
+ }
+
+ /**
+ * Returns an instance using a Bcmath calculator.
+ */
+ public static function fromBCMath(): self
+ {
+ return new self(new BCMathCalculator());
+ }
+
+ /**
+ * Returns an instance using a PHP native calculator (requires 64bits PHP).
+ */
+ public static function fromNative(): self
+ {
+ return new self(new NativeCalculator());
+ }
+
+ /**
+ * Returns an instance using a detected calculator depending on the PHP environment.
+ *
+ * @throws MissingFeature If no Calculator implementing object can be used on the platform
+ *
+ * @codeCoverageIgnore
+ */
+ public static function fromEnvironment(): self
+ {
+ FeatureDetection::supportsIPv4Conversion();
+
+ return match (true) {
+ extension_loaded('gmp') => self::fromGMP(),
+ extension_loaded('bcmath') => self::fromBCMath(),
+ default => self::fromNative(),
+ };
+ }
+
+ public function isIpv4(Stringable|string|null $host): bool
+ {
+ if (null === $host) {
+ return false;
+ }
+
+ if (null !== $this->toDecimal($host)) {
+ return true;
+ }
+
+ $host = (string) $host;
+ if (false === filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ return false;
+ }
+
+ $ipAddress = strtolower((string) inet_ntop((string) inet_pton($host)));
+ if (str_starts_with($ipAddress, self::IPV4_MAPPED_PREFIX)) {
+ return false !== filter_var(substr($ipAddress, 7), FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
+ }
+
+ if (!str_starts_with($ipAddress, self::IPV6_6TO4_PREFIX)) {
+ return false;
+ }
+
+ $hexParts = explode(':', substr($ipAddress, 5, 9));
+
+ return count($hexParts) > 1
+ && false !== long2ip((int) hexdec($hexParts[0]) * 65536 + (int) hexdec($hexParts[1]));
+ }
+
+ public function toIPv6Using6to4(Stringable|string|null $host): ?string
+ {
+ $host = $this->toDecimal($host);
+ if (null === $host) {
+ return null;
+ }
+
+ /** @var array<string> $parts */
+ $parts = array_map(
+ fn (string $part): string => sprintf('%02x', $part),
+ explode('.', $host)
+ );
+
+ return '['.self::IPV6_6TO4_PREFIX.$parts[0].$parts[1].':'.$parts[2].$parts[3].'::]';
+ }
+
+ public function toIPv6UsingMapping(Stringable|string|null $host): ?string
+ {
+ $host = $this->toDecimal($host);
+ if (null === $host) {
+ return null;
+ }
+
+ return '['.self::IPV4_MAPPED_PREFIX.$host.']';
+ }
+
+ public function toOctal(Stringable|string|null $host): ?string
+ {
+ $host = $this->toDecimal($host);
+
+ return match (null) {
+ $host => null,
+ default => implode('.', array_map(
+ fn ($value) => str_pad(decoct((int) $value), 4, '0', STR_PAD_LEFT),
+ explode('.', $host)
+ )),
+ };
+ }
+
+ public function toHexadecimal(Stringable|string|null $host): ?string
+ {
+ $host = $this->toDecimal($host);
+
+ return match (null) {
+ $host => null,
+ default => '0x'.implode('', array_map(
+ fn ($value) => dechex((int) $value),
+ explode('.', $host)
+ )),
+ };
+ }
+
+ /**
+ * Tries to convert a IPv4 hexadecimal or a IPv4 octal notation into a IPv4 dot-decimal notation if possible
+ * otherwise returns null.
+ *
+ * @see https://url.spec.whatwg.org/#concept-ipv4-parser
+ */
+ public function toDecimal(Stringable|string|null $host): ?string
+ {
+ $host = (string) $host;
+ if (str_starts_with($host, '[') && str_ends_with($host, ']')) {
+ $host = substr($host, 1, -1);
+ if (false === filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ return null;
+ }
+
+ $ipAddress = strtolower((string) inet_ntop((string) inet_pton($host)));
+ if (str_starts_with($ipAddress, self::IPV4_MAPPED_PREFIX)) {
+ return substr($ipAddress, 7);
+ }
+
+ if (!str_starts_with($ipAddress, self::IPV6_6TO4_PREFIX)) {
+ return null;
+ }
+
+ $hexParts = explode(':', substr($ipAddress, 5, 9));
+
+ return (string) match (true) {
+ count($hexParts) < 2 => null,
+ default => long2ip((int) hexdec($hexParts[0]) * 65536 + (int) hexdec($hexParts[1])),
+ };
+ }
+
+ if (1 !== preg_match(self::REGEXP_IPV4_HOST, $host)) {
+ return null;
+ }
+
+ if (str_ends_with($host, '.')) {
+ $host = substr($host, 0, -1);
+ }
+
+ $numbers = [];
+ foreach (explode('.', $host) as $label) {
+ $number = $this->labelToNumber($label);
+ if (null === $number) {
+ return null;
+ }
+
+ $numbers[] = $number;
+ }
+
+ $ipv4 = array_pop($numbers);
+ $max = $this->calculator->pow(256, 6 - count($numbers));
+ if ($this->calculator->compare($ipv4, $max) > 0) {
+ return null;
+ }
+
+ foreach ($numbers as $offset => $number) {
+ if ($this->calculator->compare($number, 255) > 0) {
+ return null;
+ }
+
+ $ipv4 = $this->calculator->add($ipv4, $this->calculator->multiply(
+ $number,
+ $this->calculator->pow(256, 3 - $offset)
+ ));
+ }
+
+ return $this->long2Ip($ipv4);
+ }
+
+ /**
+ * Converts a domain label into a IPv4 integer part.
+ *
+ * @see https://url.spec.whatwg.org/#ipv4-number-parser
+ *
+ * @return mixed returns null if it cannot correctly convert the label
+ */
+ private function labelToNumber(string $label): mixed
+ {
+ foreach (self::REGEXP_IPV4_NUMBER_PER_BASE as $regexp => $base) {
+ if (1 !== preg_match($regexp, $label, $matches)) {
+ continue;
+ }
+
+ $number = ltrim($matches['number'], '0');
+ if ('' === $number) {
+ return 0;
+ }
+
+ $number = $this->calculator->baseConvert($number, $base);
+ if (0 <= $this->calculator->compare($number, 0) && 0 >= $this->calculator->compare($number, $this->maxIPv4Number)) {
+ return $number;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Generates the dot-decimal notation for IPv4.
+ *
+ * @see https://url.spec.whatwg.org/#concept-ipv4-parser
+ *
+ * @param mixed $ipAddress the number representation of the IPV4address
+ */
+ private function long2Ip(mixed $ipAddress): string
+ {
+ $output = '';
+ for ($offset = 0; $offset < 4; $offset++) {
+ $output = $this->calculator->mod($ipAddress, 256).$output;
+ if ($offset < 3) {
+ $output = '.'.$output;
+ }
+ $ipAddress = $this->calculator->div($ipAddress, 256);
+ }
+
+ return $output;
+ }
+}
diff --git a/vendor/league/uri-interfaces/IPv4/GMPCalculator.php b/vendor/league/uri-interfaces/IPv4/GMPCalculator.php
new file mode 100644
index 000000000..34db5dfc4
--- /dev/null
+++ b/vendor/league/uri-interfaces/IPv4/GMPCalculator.php
@@ -0,0 +1,70 @@
+<?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\IPv4;
+
+use GMP;
+
+use function gmp_add;
+use function gmp_cmp;
+use function gmp_div_q;
+use function gmp_init;
+use function gmp_mod;
+use function gmp_mul;
+use function gmp_pow;
+use function gmp_sub;
+
+use const GMP_ROUND_MINUSINF;
+
+final class GMPCalculator implements Calculator
+{
+ public function baseConvert(mixed $value, int $base): GMP
+ {
+ return gmp_init($value, $base);
+ }
+
+ public function pow(mixed $value, int $exponent): GMP
+ {
+ return gmp_pow($value, $exponent);
+ }
+
+ public function compare(mixed $value1, mixed $value2): int
+ {
+ return gmp_cmp($value1, $value2);
+ }
+
+ public function multiply(mixed $value1, mixed $value2): GMP
+ {
+ return gmp_mul($value1, $value2);
+ }
+
+ public function div(mixed $value, mixed $base): GMP
+ {
+ return gmp_div_q($value, $base, GMP_ROUND_MINUSINF);
+ }
+
+ public function mod(mixed $value, mixed $base): GMP
+ {
+ return gmp_mod($value, $base);
+ }
+
+ public function add(mixed $value1, mixed $value2): GMP
+ {
+ return gmp_add($value1, $value2);
+ }
+
+ public function sub(mixed $value1, mixed $value2): GMP
+ {
+ return gmp_sub($value1, $value2);
+ }
+}
diff --git a/vendor/league/uri-interfaces/IPv4/NativeCalculator.php b/vendor/league/uri-interfaces/IPv4/NativeCalculator.php
new file mode 100644
index 000000000..7ac2c7671
--- /dev/null
+++ b/vendor/league/uri-interfaces/IPv4/NativeCalculator.php
@@ -0,0 +1,60 @@
+<?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\IPv4;
+
+use function floor;
+use function intval;
+
+final class NativeCalculator implements Calculator
+{
+ public function baseConvert(mixed $value, int $base): int
+ {
+ return intval((string) $value, $base);
+ }
+
+ public function pow(mixed $value, int $exponent)
+ {
+ return $value ** $exponent;
+ }
+
+ public function compare(mixed $value1, mixed $value2): int
+ {
+ return $value1 <=> $value2;
+ }
+
+ public function multiply(mixed $value1, mixed $value2): int
+ {
+ return $value1 * $value2;
+ }
+
+ public function div(mixed $value, mixed $base): int
+ {
+ return (int) floor($value / $base);
+ }
+
+ public function mod(mixed $value, mixed $base): int
+ {
+ return $value % $base;
+ }
+
+ public function add(mixed $value1, mixed $value2): int
+ {
+ return $value1 + $value2;
+ }
+
+ public function sub(mixed $value1, mixed $value2): int
+ {
+ return $value1 - $value2;
+ }
+}
diff --git a/vendor/league/uri-interfaces/IPv6/Converter.php b/vendor/league/uri-interfaces/IPv6/Converter.php
new file mode 100644
index 000000000..f645c1da2
--- /dev/null
+++ b/vendor/league/uri-interfaces/IPv6/Converter.php
@@ -0,0 +1,137 @@
+<?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\IPv6;
+
+use Stringable;
+use ValueError;
+
+use function filter_var;
+use function implode;
+use function inet_pton;
+use function str_split;
+use function strtolower;
+use function unpack;
+
+use const FILTER_FLAG_IPV6;
+use const FILTER_VALIDATE_IP;
+
+final class Converter
+{
+ /**
+ * Significant 10 bits of IP to detect Zone ID regular expression pattern.
+ *
+ * @var string
+ */
+ private const HOST_ADDRESS_BLOCK = "\xfe\x80";
+
+ public static function compressIp(string $ipAddress): string
+ {
+ return match (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ false => throw new ValueError('The submitted IP is not a valid IPv6 address.'),
+ default => strtolower((string) inet_ntop((string) inet_pton($ipAddress))),
+ };
+ }
+
+ public static function expandIp(string $ipAddress): string
+ {
+ if (false === filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ throw new ValueError('The submitted IP is not a valid IPv6 address.');
+ }
+
+ $hex = (array) unpack('H*hex', (string) inet_pton($ipAddress));
+
+ return implode(':', str_split(strtolower($hex['hex'] ?? ''), 4));
+ }
+
+ public static function compress(Stringable|string|null $host): ?string
+ {
+ $components = self::parse($host);
+ if (null === $components['ipAddress']) {
+ return match ($host) {
+ null => $host,
+ default => (string) $host,
+ };
+ }
+
+ $components['ipAddress'] = self::compressIp($components['ipAddress']);
+
+ return self::build($components);
+ }
+
+ public static function expand(Stringable|string|null $host): ?string
+ {
+ $components = self::parse($host);
+ if (null === $components['ipAddress']) {
+ return match ($host) {
+ null => $host,
+ default => (string) $host,
+ };
+ }
+
+ $components['ipAddress'] = self::expandIp($components['ipAddress']);
+
+ return self::build($components);
+ }
+
+ private static function build(array $components): string
+ {
+ $components['ipAddress'] ??= null;
+ $components['zoneIdentifier'] ??= null;
+
+ if (null === $components['ipAddress']) {
+ return '';
+ }
+
+ return '['.$components['ipAddress'].match ($components['zoneIdentifier']) {
+ null => '',
+ default => '%'.$components['zoneIdentifier'],
+ }.']';
+ }
+
+ /**]
+ * @param Stringable|string|null $host
+ *
+ * @return array{ipAddress:string|null, zoneIdentifier:string|null}
+ */
+ private static function parse(Stringable|string|null $host): array
+ {
+ if (null === $host) {
+ return ['ipAddress' => null, 'zoneIdentifier' => null];
+ }
+
+ $host = (string) $host;
+ if ('' === $host) {
+ return ['ipAddress' => null, 'zoneIdentifier' => null];
+ }
+
+ if (!str_starts_with($host, '[')) {
+ return ['ipAddress' => null, 'zoneIdentifier' => null];
+ }
+
+ if (!str_ends_with($host, ']')) {
+ return ['ipAddress' => null, 'zoneIdentifier' => null];
+ }
+
+ [$ipv6, $zoneIdentifier] = explode('%', substr($host, 1, -1), 2) + [1 => null];
+ if (false === filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ return ['ipAddress' => null, 'zoneIdentifier' => null];
+ }
+
+ return match (true) {
+ null === $zoneIdentifier,
+ is_string($ipv6) && str_starts_with((string)inet_pton($ipv6), self::HOST_ADDRESS_BLOCK) => ['ipAddress' => $ipv6, 'zoneIdentifier' => $zoneIdentifier],
+ default => ['ipAddress' => null, 'zoneIdentifier' => null],
+ };
+ }
+}
diff --git a/vendor/league/uri-interfaces/Idna/Converter.php b/vendor/league/uri-interfaces/Idna/Converter.php
new file mode 100644
index 000000000..b993e9e09
--- /dev/null
+++ b/vendor/league/uri-interfaces/Idna/Converter.php
@@ -0,0 +1,218 @@
+<?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\Idna;
+
+use League\Uri\Exceptions\ConversionFailed;
+use League\Uri\Exceptions\SyntaxError;
+use League\Uri\FeatureDetection;
+use Stringable;
+
+use function idn_to_ascii;
+use function idn_to_utf8;
+use function rawurldecode;
+
+use const INTL_IDNA_VARIANT_UTS46;
+
+/**
+ * @see https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/uidna_8h.html
+ */
+final class Converter
+{
+ private const REGEXP_IDNA_PATTERN = '/[^\x20-\x7f]/';
+ private const MAX_DOMAIN_LENGTH = 253;
+ private const MAX_LABEL_LENGTH = 63;
+
+ /**
+ * General registered name regular expression.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-3.2.2
+ * @see https://regex101.com/r/fptU8V/1
+ */
+ private const REGEXP_REGISTERED_NAME = '/
+ (?(DEFINE)
+ (?<unreserved>[a-z0-9_~\-]) # . is missing as it is used to separate labels
+ (?<sub_delims>[!$&\'()*+,;=])
+ (?<encoded>%[A-F0-9]{2})
+ (?<reg_name>(?:(?&unreserved)|(?&sub_delims)|(?&encoded))*)
+ )
+ ^(?:(?&reg_name)\.)*(?&reg_name)\.?$
+ /ix';
+
+ /**
+ * Converts the input to its IDNA ASCII form or throw on failure.
+ *
+ * @see Converter::toAscii()
+ *
+ * @throws SyntaxError if the string cannot be converted to UNICODE using IDN UTS46 algorithm
+ * @throws ConversionFailed if the conversion returns error
+ */
+ public static function toAsciiOrFail(Stringable|string $domain, Option|int|null $options = null): string
+ {
+ $result = self::toAscii($domain, $options);
+
+ return match (true) {
+ $result->hasErrors() => throw ConversionFailed::dueToIdnError($domain, $result),
+ default => $result->domain(),
+ };
+ }
+
+ /**
+ * Converts the input to its IDNA ASCII form.
+ *
+ * This method returns the string converted to IDN ASCII form
+ *
+ * @throws SyntaxError if the string cannot be converted to ASCII using IDN UTS46 algorithm
+ */
+ public static function toAscii(Stringable|string $domain, Option|int|null $options = null): Result
+ {
+ $domain = rawurldecode((string) $domain);
+
+ if (1 === preg_match(self::REGEXP_IDNA_PATTERN, $domain)) {
+ FeatureDetection::supportsIdn();
+
+ $flags = match (true) {
+ null === $options => Option::forIDNA2008Ascii(),
+ $options instanceof Option => $options,
+ default => Option::new($options),
+ };
+
+ idn_to_ascii($domain, $flags->toBytes(), INTL_IDNA_VARIANT_UTS46, $idnaInfo);
+
+ if ([] === $idnaInfo) {
+ return Result::fromIntl([
+ 'result' => strtolower($domain),
+ 'isTransitionalDifferent' => false,
+ 'errors' => self::validateDomainAndLabelLength($domain),
+ ]);
+ }
+
+ return Result::fromIntl($idnaInfo);
+ }
+
+ $error = Error::NONE->value;
+ if (1 !== preg_match(self::REGEXP_REGISTERED_NAME, $domain)) {
+ $error |= Error::DISALLOWED->value;
+ }
+
+ return Result::fromIntl([
+ 'result' => strtolower($domain),
+ 'isTransitionalDifferent' => false,
+ 'errors' => self::validateDomainAndLabelLength($domain) | $error,
+ ]);
+ }
+
+ /**
+ * Converts the input to its IDNA UNICODE form or throw on failure.
+ *
+ * @see Converter::toUnicode()
+ *
+ * @throws ConversionFailed if the conversion returns error
+ */
+ public static function toUnicodeOrFail(Stringable|string $domain, Option|int|null $options = null): string
+ {
+ $result = self::toUnicode($domain, $options);
+
+ return match (true) {
+ $result->hasErrors() => throw ConversionFailed::dueToIdnError($domain, $result),
+ default => $result->domain(),
+ };
+ }
+
+ /**
+ * Converts the input to its IDNA UNICODE form.
+ *
+ * This method returns the string converted to IDN UNICODE form
+ *
+ * @throws SyntaxError if the string cannot be converted to UNICODE using IDN UTS46 algorithm
+ */
+ public static function toUnicode(Stringable|string $domain, Option|int|null $options = null): Result
+ {
+ $domain = rawurldecode((string) $domain);
+
+ if (false === stripos($domain, 'xn--')) {
+ return Result::fromIntl(['result' => $domain, 'isTransitionalDifferent' => false, 'errors' => Error::NONE->value]);
+ }
+
+ FeatureDetection::supportsIdn();
+
+ $flags = match (true) {
+ null === $options => Option::forIDNA2008Unicode(),
+ $options instanceof Option => $options,
+ default => Option::new($options),
+ };
+
+ idn_to_utf8($domain, $flags->toBytes(), INTL_IDNA_VARIANT_UTS46, $idnaInfo);
+
+ if ([] === $idnaInfo) {
+ return Result::fromIntl(['result' => $domain, 'isTransitionalDifferent' => false, 'errors' => Error::NONE->value]);
+ }
+
+ return Result::fromIntl($idnaInfo);
+ }
+
+ /**
+ * Tells whether the submitted host is a valid IDN regardless of its format.
+ *
+ * Returns false if the host is invalid or if its conversion yield the same result
+ */
+ public static function isIdn(Stringable|string|null $domain): bool
+ {
+ $domain = strtolower(rawurldecode((string) $domain));
+ $result = match (1) {
+ preg_match(self::REGEXP_IDNA_PATTERN, $domain) => self::toAscii($domain),
+ default => self::toUnicode($domain),
+ };
+
+ return match (true) {
+ $result->hasErrors() => false,
+ default => $result->domain() !== $domain,
+ };
+ }
+
+ /**
+ * Adapted from https://github.com/TRowbotham/idna.
+ *
+ * @see https://github.com/TRowbotham/idna/blob/master/src/Idna.php#L236
+ */
+ private static function validateDomainAndLabelLength(string $domain): int
+ {
+ $error = Error::NONE->value;
+ $labels = explode('.', $domain);
+ $maxDomainSize = self::MAX_DOMAIN_LENGTH;
+ $length = count($labels);
+
+ // If the last label is empty, and it is not the first label, then it is the root label.
+ // Increase the max size by 1, making it 254, to account for the root label's "."
+ // delimiter. This also means we don't need to check the last label's length for being too
+ // long.
+ if ($length > 1 && '' === $labels[$length - 1]) {
+ ++$maxDomainSize;
+ array_pop($labels);
+ }
+
+ if (strlen($domain) > $maxDomainSize) {
+ $error |= Error::DOMAIN_NAME_TOO_LONG->value;
+ }
+
+ foreach ($labels as $label) {
+ if (strlen($label) > self::MAX_LABEL_LENGTH) {
+ $error |= Error::LABEL_TOO_LONG->value;
+
+ break;
+ }
+ }
+
+ return $error;
+ }
+}
diff --git a/vendor/league/uri-interfaces/Idna/Error.php b/vendor/league/uri-interfaces/Idna/Error.php
new file mode 100644
index 000000000..eb51e9dd5
--- /dev/null
+++ b/vendor/league/uri-interfaces/Idna/Error.php
@@ -0,0 +1,64 @@
+<?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.
+ */
+
+namespace League\Uri\Idna;
+
+enum Error: int
+{
+ case NONE = 0;
+ case EMPTY_LABEL = 1;
+ case LABEL_TOO_LONG = 2;
+ case DOMAIN_NAME_TOO_LONG = 4;
+ case LEADING_HYPHEN = 8;
+ case TRAILING_HYPHEN = 0x10;
+ case HYPHEN_3_4 = 0x20;
+ case LEADING_COMBINING_MARK = 0x40;
+ case DISALLOWED = 0x80;
+ case PUNYCODE = 0x100;
+ case LABEL_HAS_DOT = 0x200;
+ case INVALID_ACE_LABEL = 0x400;
+ case BIDI = 0x800;
+ case CONTEXTJ = 0x1000;
+ case CONTEXTO_PUNCTUATION = 0x2000;
+ case CONTEXTO_DIGITS = 0x4000;
+
+ public function description(): string
+ {
+ return match ($this) {
+ self::NONE => 'No error has occurred',
+ self::EMPTY_LABEL => 'a non-final domain name label (or the whole domain name) is empty',
+ self::LABEL_TOO_LONG => 'a domain name label is longer than 63 bytes',
+ self::DOMAIN_NAME_TOO_LONG => 'a domain name is longer than 255 bytes in its storage form',
+ self::LEADING_HYPHEN => 'a label starts with a hyphen-minus ("-")',
+ self::TRAILING_HYPHEN => 'a label ends with a hyphen-minus ("-")',
+ self::HYPHEN_3_4 => 'a label contains hyphen-minus ("-") in the third and fourth positions',
+ self::LEADING_COMBINING_MARK => 'a label starts with a combining mark',
+ self::DISALLOWED => 'a label or domain name contains disallowed characters',
+ self::PUNYCODE => 'a label starts with "xn--" but does not contain valid Punycode',
+ self::LABEL_HAS_DOT => 'a label contains a dot=full stop',
+ self::INVALID_ACE_LABEL => 'An ACE label does not contain a valid label string',
+ self::BIDI => 'a label does not meet the IDNA BiDi requirements (for right-to-left characters)',
+ self::CONTEXTJ => 'a label does not meet the IDNA CONTEXTJ requirements',
+ self::CONTEXTO_DIGITS => 'a label does not meet the IDNA CONTEXTO requirements for digits',
+ self::CONTEXTO_PUNCTUATION => 'a label does not meet the IDNA CONTEXTO requirements for punctuation characters. Some punctuation characters "Would otherwise have been DISALLOWED" but are allowed in certain contexts',
+ };
+ }
+
+ public static function filterByErrorBytes(int $errors): array
+ {
+ return array_values(
+ array_filter(
+ self::cases(),
+ fn (self $error): bool => 0 !== ($error->value & $errors)
+ )
+ );
+ }
+}
diff --git a/vendor/league/uri-interfaces/Idna/Option.php b/vendor/league/uri-interfaces/Idna/Option.php
new file mode 100644
index 000000000..777979f13
--- /dev/null
+++ b/vendor/league/uri-interfaces/Idna/Option.php
@@ -0,0 +1,179 @@
+<?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\Idna;
+
+use ReflectionClass;
+use ReflectionClassConstant;
+
+/**
+ * @see https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/uidna_8h.html
+ */
+final class Option
+{
+ private const DEFAULT = 0;
+ private const ALLOW_UNASSIGNED = 1;
+ private const USE_STD3_RULES = 2;
+ private const CHECK_BIDI = 4;
+ private const CHECK_CONTEXTJ = 8;
+ private const NONTRANSITIONAL_TO_ASCII = 0x10;
+ private const NONTRANSITIONAL_TO_UNICODE = 0x20;
+ private const CHECK_CONTEXTO = 0x40;
+
+ private function __construct(private readonly int $value)
+ {
+ }
+
+ private static function cases(): array
+ {
+ static $assoc;
+ if (null === $assoc) {
+ $assoc = [];
+ $fooClass = new ReflectionClass(self::class);
+ foreach ($fooClass->getConstants(ReflectionClassConstant::IS_PRIVATE) as $name => $value) {
+ $assoc[$name] = $value;
+ }
+ }
+
+ return $assoc;
+ }
+
+ public static function new(int $bytes = self::DEFAULT): self
+ {
+ return new self(array_reduce(
+ self::cases(),
+ fn (int $value, int $option) => 0 !== ($option & $bytes) ? ($value | $option) : $value,
+ self::DEFAULT
+ ));
+ }
+
+ public static function forIDNA2008Ascii(): self
+ {
+ return self::new()
+ ->nonTransitionalToAscii()
+ ->checkBidi()
+ ->useSTD3Rules()
+ ->checkContextJ();
+ }
+
+ public static function forIDNA2008Unicode(): self
+ {
+ return self::new()
+ ->nonTransitionalToUnicode()
+ ->checkBidi()
+ ->useSTD3Rules()
+ ->checkContextJ();
+ }
+
+ public function toBytes(): int
+ {
+ return $this->value;
+ }
+
+ /** array<string, int> */
+ public function list(): array
+ {
+ return array_keys(array_filter(
+ self::cases(),
+ fn (int $value) => 0 !== ($value & $this->value)
+ ));
+ }
+
+ public function allowUnassigned(): self
+ {
+ return $this->add(self::ALLOW_UNASSIGNED);
+ }
+
+ public function disallowUnassigned(): self
+ {
+ return $this->remove(self::ALLOW_UNASSIGNED);
+ }
+
+ public function useSTD3Rules(): self
+ {
+ return $this->add(self::USE_STD3_RULES);
+ }
+
+ public function prohibitSTD3Rules(): self
+ {
+ return $this->remove(self::USE_STD3_RULES);
+ }
+
+ public function checkBidi(): self
+ {
+ return $this->add(self::CHECK_BIDI);
+ }
+
+ public function ignoreBidi(): self
+ {
+ return $this->remove(self::CHECK_BIDI);
+ }
+
+ public function checkContextJ(): self
+ {
+ return $this->add(self::CHECK_CONTEXTJ);
+ }
+
+ public function ignoreContextJ(): self
+ {
+ return $this->remove(self::CHECK_CONTEXTJ);
+ }
+
+ public function checkContextO(): self
+ {
+ return $this->add(self::CHECK_CONTEXTO);
+ }
+
+ public function ignoreContextO(): self
+ {
+ return $this->remove(self::CHECK_CONTEXTO);
+ }
+
+ public function nonTransitionalToAscii(): self
+ {
+ return $this->add(self::NONTRANSITIONAL_TO_ASCII);
+ }
+
+ public function transitionalToAscii(): self
+ {
+ return $this->remove(self::NONTRANSITIONAL_TO_ASCII);
+ }
+
+ public function nonTransitionalToUnicode(): self
+ {
+ return $this->add(self::NONTRANSITIONAL_TO_UNICODE);
+ }
+
+ public function transitionalToUnicode(): self
+ {
+ return $this->remove(self::NONTRANSITIONAL_TO_UNICODE);
+ }
+
+ public function add(Option|int|null $option = null): self
+ {
+ return match (true) {
+ null === $option => $this,
+ $option instanceof self => self::new($this->value | $option->value),
+ default => self::new($this->value | $option),
+ };
+ }
+
+ public function remove(Option|int|null $option = null): self
+ {
+ return match (true) {
+ null === $option => $this,
+ $option instanceof self => self::new($this->value & ~$option->value),
+ default => self::new($this->value & ~$option),
+ };
+ }
+}
diff --git a/vendor/league/uri-interfaces/Idna/Result.php b/vendor/league/uri-interfaces/Idna/Result.php
new file mode 100644
index 000000000..02d713f5c
--- /dev/null
+++ b/vendor/league/uri-interfaces/Idna/Result.php
@@ -0,0 +1,64 @@
+<?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\Idna;
+
+/**
+ * @see https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/uidna_8h.html
+ */
+final class Result
+{
+ private function __construct(
+ private readonly string $domain,
+ private readonly bool $isTransitionalDifferent,
+ /** @var array<Error> */
+ private readonly array $errors
+ ) {
+ }
+
+ /**
+ * @param array{result:string, isTransitionalDifferent:bool, errors:int} $infos
+ */
+ public static function fromIntl(array $infos): self
+ {
+ return new self($infos['result'], $infos['isTransitionalDifferent'], Error::filterByErrorBytes($infos['errors']));
+ }
+
+ public function domain(): string
+ {
+ return $this->domain;
+ }
+
+ public function isTransitionalDifferent(): bool
+ {
+ return $this->isTransitionalDifferent;
+ }
+
+ /**
+ * @return array<Error>
+ */
+ public function errors(): array
+ {
+ return $this->errors;
+ }
+
+ public function hasErrors(): bool
+ {
+ return [] !== $this->errors;
+ }
+
+ public function hasError(Error $error): bool
+ {
+ return in_array($error, $this->errors, true);
+ }
+}
diff --git a/vendor/league/uri-interfaces/KeyValuePair/Converter.php b/vendor/league/uri-interfaces/KeyValuePair/Converter.php
new file mode 100644
index 000000000..f208060c6
--- /dev/null
+++ b/vendor/league/uri-interfaces/KeyValuePair/Converter.php
@@ -0,0 +1,209 @@
+<?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\KeyValuePair;
+
+use League\Uri\Contracts\UriComponentInterface;
+use League\Uri\Exceptions\SyntaxError;
+use Stringable;
+
+use function array_combine;
+use function explode;
+use function implode;
+use function is_float;
+use function is_int;
+use function is_string;
+use function json_encode;
+use function preg_match;
+use function str_replace;
+
+use const JSON_PRESERVE_ZERO_FRACTION;
+use const PHP_QUERY_RFC1738;
+use const PHP_QUERY_RFC3986;
+
+final class Converter
+{
+ private const REGEXP_INVALID_CHARS = '/[\x00-\x1f\x7f]/';
+
+ /**
+ * @param non-empty-string $separator the query string separator
+ * @param array<string> $fromRfc3986 contains all the RFC3986 encoded characters to be converted
+ * @param array<string> $toEncoding contains all the expected encoded characters
+ */
+ private function __construct(
+ private readonly string $separator,
+ private readonly array $fromRfc3986 = [],
+ private readonly array $toEncoding = [],
+ ) {
+ if ('' === $this->separator) {
+ throw new SyntaxError('The separator character must be a non empty string.');
+ }
+ }
+
+ /**
+ * @param non-empty-string $separator
+ */
+ public static function new(string $separator): self
+ {
+ return new self($separator);
+ }
+
+ /**
+ * @param non-empty-string $separator
+ */
+ public static function fromRFC3986(string $separator = '&'): self
+ {
+ return self::new($separator);
+ }
+
+ /**
+ * @param non-empty-string $separator
+ */
+ public static function fromRFC1738(string $separator = '&'): self
+ {
+ return self::new($separator)
+ ->withEncodingMap(['%20' => '+']);
+ }
+
+ /**
+ * @param non-empty-string $separator
+ *
+ * @see https://url.spec.whatwg.org/#application/x-www-form-urlencoded
+ */
+ public static function fromFormData(string $separator = '&'): self
+ {
+ return self::new($separator)
+ ->withEncodingMap(['%20' => '+', '%2A' => '*']);
+ }
+
+ public static function fromEncodingType(int $encType): self
+ {
+ return match ($encType) {
+ PHP_QUERY_RFC3986 => self::fromRFC3986(),
+ PHP_QUERY_RFC1738 => self::fromRFC1738(),
+ default => throw new SyntaxError('Unknown or Unsupported encoding.'),
+ };
+ }
+
+ /**
+ * @return non-empty-string
+ */
+ public function separator(): string
+ {
+ return $this->separator;
+ }
+
+ /**
+ * @return array<string, string>
+ */
+ public function encodingMap(): array
+ {
+ return array_combine($this->fromRfc3986, $this->toEncoding);
+ }
+
+ /**
+ * @return array<non-empty-list<string|null>>
+ */
+ public function toPairs(Stringable|string|int|float|bool|null $value): array
+ {
+ $value = match (true) {
+ $value instanceof UriComponentInterface => $value->value(),
+ $value instanceof Stringable, is_int($value) => (string) $value,
+ false === $value => '0',
+ true === $value => '1',
+ default => $value,
+ };
+
+ if (null === $value) {
+ return [];
+ }
+
+ $value = match (1) {
+ preg_match(self::REGEXP_INVALID_CHARS, (string) $value) => throw new SyntaxError('Invalid query string: `'.$value.'`.'),
+ default => str_replace($this->toEncoding, $this->fromRfc3986, (string) $value),
+ };
+
+ return array_map(
+ fn (string $pair): array => explode('=', $pair, 2) + [1 => null],
+ explode($this->separator, $value)
+ );
+ }
+
+ private static function vString(Stringable|string|bool|int|float|null $value): ?string
+ {
+ return match (true) {
+ $value => '1',
+ false === $value => '0',
+ null === $value => null,
+ is_float($value) => (string) json_encode($value, JSON_PRESERVE_ZERO_FRACTION),
+ default => (string) $value,
+ };
+ }
+
+ /**
+ * @param iterable<array{0:string|null, 1:Stringable|string|bool|int|float|null}> $pairs
+ */
+ public function toValue(iterable $pairs): ?string
+ {
+ $filteredPairs = [];
+ foreach ($pairs as $pair) {
+ $filteredPairs[] = match (true) {
+ !is_string($pair[0]) => throw new SyntaxError('the pair key MUST be a string;, `'.gettype($pair[0]).'` given.'),
+ null === $pair[1] => self::vString($pair[0]),
+ default => self::vString($pair[0]).'='.self::vString($pair[1]),
+ };
+ }
+
+ return match ([]) {
+ $filteredPairs => null,
+ default => str_replace($this->fromRfc3986, $this->toEncoding, implode($this->separator, $filteredPairs)),
+ };
+ }
+
+ /**
+ * @param non-empty-string $separator
+ */
+ public function withSeparator(string $separator): self
+ {
+ return match ($this->separator) {
+ $separator => $this,
+ default => new self($separator, $this->fromRfc3986, $this->toEncoding),
+ };
+ }
+
+ /**
+ * Sets the conversion map.
+ *
+ * Each key from the iterable structure represents the RFC3986 encoded characters as string,
+ * while each value represents the expected output encoded characters
+ */
+ public function withEncodingMap(iterable $encodingMap): self
+ {
+ $fromRfc3986 = [];
+ $toEncoding = [];
+ foreach ($encodingMap as $from => $to) {
+ [$fromRfc3986[], $toEncoding[]] = match (true) {
+ !is_string($from) => throw new SyntaxError('The encoding output must be a string; `'.gettype($from).'` given.'),
+ $to instanceof Stringable,
+ is_string($to) => [$from, (string) $to],
+ default => throw new SyntaxError('The encoding output must be a string; `'.gettype($to).'` given.'),
+ };
+ }
+
+ return match (true) {
+ $fromRfc3986 !== $this->fromRfc3986,
+ $toEncoding !== $this->toEncoding => new self($this->separator, $fromRfc3986, $toEncoding),
+ default => $this,
+ };
+ }
+}
diff --git a/vendor/league/uri-interfaces/LICENSE b/vendor/league/uri-interfaces/LICENSE
new file mode 100644
index 000000000..3b52528f2
--- /dev/null
+++ b/vendor/league/uri-interfaces/LICENSE
@@ -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.
diff --git a/vendor/league/uri-interfaces/QueryString.php b/vendor/league/uri-interfaces/QueryString.php
new file mode 100644
index 000000000..b35086736
--- /dev/null
+++ b/vendor/league/uri-interfaces/QueryString.php
@@ -0,0 +1,276 @@
+<?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\Exceptions\SyntaxError;
+use League\Uri\KeyValuePair\Converter;
+use Stringable;
+
+use function array_key_exists;
+use function array_keys;
+use function is_array;
+use function rawurldecode;
+use function strpos;
+use function substr;
+
+use const PHP_QUERY_RFC3986;
+
+/**
+ * A class to parse the URI query string.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-3.4
+ */
+final class QueryString
+{
+ private const PAIR_VALUE_DECODED = 1;
+ private const PAIR_VALUE_PRESERVED = 2;
+
+ /**
+ * @codeCoverageIgnore
+ */
+ private function __construct()
+ {
+ }
+
+ /**
+ * Build a query string from a list of pairs.
+ *
+ * @see QueryString::buildFromPairs()
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
+ *
+ * @param iterable<array{0:string, 1:string|float|int|bool|null}> $pairs
+ * @param non-empty-string $separator
+ *
+ * @throws SyntaxError If the encoding type is invalid
+ * @throws SyntaxError If a pair is invalid
+ */
+ public static function build(iterable $pairs, string $separator = '&', int $encType = PHP_QUERY_RFC3986): ?string
+ {
+ return self::buildFromPairs($pairs, Converter::fromEncodingType($encType)->withSeparator($separator));
+ }
+
+ /**
+ * Build a query string from a list of pairs.
+ *
+ * The method expects the return value from Query::parse to build
+ * a valid query string. This method differs from PHP http_build_query as
+ * it does not modify parameters keys.
+ *
+ * If a reserved character is found in a URI component and
+ * no delimiting role is known for that character, then it must be
+ * interpreted as representing the data octet corresponding to that
+ * character's encoding in US-ASCII.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
+ *
+ * @param iterable<array{0:string, 1:string|float|int|bool|null}> $pairs
+ *
+ * @throws SyntaxError If the encoding type is invalid
+ * @throws SyntaxError If a pair is invalid
+ */
+ public static function buildFromPairs(iterable $pairs, ?Converter $converter = null): ?string
+ {
+ $keyValuePairs = [];
+ foreach ($pairs as $pair) {
+ if (!is_array($pair) || [0, 1] !== array_keys($pair)) {
+ throw new SyntaxError('A pair must be a sequential array starting at `0` and containing two elements.');
+ }
+
+ $keyValuePairs[] = [(string) Encoder::encodeQueryKeyValue($pair[0]), match(null) {
+ $pair[1] => null,
+ default => Encoder::encodeQueryKeyValue($pair[1]),
+ }];
+ }
+
+ return ($converter ?? Converter::fromRFC3986())->toValue($keyValuePairs);
+ }
+
+ /**
+ * Parses the query string like parse_str without mangling the results.
+ *
+ * @see QueryString::extractFromValue()
+ * @see http://php.net/parse_str
+ * @see https://wiki.php.net/rfc/on_demand_name_mangling
+ *
+ * @param non-empty-string $separator
+ *
+ * @throws SyntaxError
+ */
+ public static function extract(Stringable|string|bool|null $query, string $separator = '&', int $encType = PHP_QUERY_RFC3986): array
+ {
+ return self::extractFromValue($query, Converter::fromEncodingType($encType)->withSeparator($separator));
+ }
+
+ /**
+ * Parses the query string like parse_str without mangling the results.
+ *
+ * The result is similar as PHP parse_str when used with its
+ * second argument with the difference that variable names are
+ * not mangled.
+ *
+ * @see http://php.net/parse_str
+ * @see https://wiki.php.net/rfc/on_demand_name_mangling
+ *
+ * @throws SyntaxError
+ */
+ public static function extractFromValue(Stringable|string|bool|null $query, ?Converter $converter = null): array
+ {
+ return self::convert(self::decodePairs(
+ ($converter ?? Converter::fromRFC3986())->toPairs($query),
+ self::PAIR_VALUE_PRESERVED
+ ));
+ }
+
+ /**
+ * Parses a query string into a collection of key/value pairs.
+ *
+ * @param non-empty-string $separator
+ *
+ * @throws SyntaxError
+ *
+ * @return array<int, array{0:string, 1:string|null}>
+ */
+ public static function parse(Stringable|string|bool|null $query, string $separator = '&', int $encType = PHP_QUERY_RFC3986): array
+ {
+ return self::parseFromValue($query, Converter::fromEncodingType($encType)->withSeparator($separator));
+ }
+
+ /**
+ * Parses a query string into a collection of key/value pairs.
+ *
+ * @throws SyntaxError
+ *
+ * @return array<int, array{0:string, 1:string|null}>
+ */
+ public static function parseFromValue(Stringable|string|bool|null $query, ?Converter $converter = null): array
+ {
+ return self::decodePairs(
+ ($converter ?? Converter::fromRFC3986())->toPairs($query),
+ self::PAIR_VALUE_DECODED
+ );
+ }
+
+ /**
+ * @param array<non-empty-list<string|null>> $pairs
+ *
+ * @return array<int, array{0:string, 1:string|null}>
+ */
+ private static function decodePairs(array $pairs, int $pairValueState): array
+ {
+ $decodePair = static function (array $pair, int $pairValueState): array {
+ [$key, $value] = $pair;
+
+ return match ($pairValueState) {
+ self::PAIR_VALUE_PRESERVED => [(string) Encoder::decodeAll($key), $value],
+ default => [(string) Encoder::decodeAll($key), Encoder::decodeAll($value)],
+ };
+ };
+
+ return array_reduce(
+ $pairs,
+ fn (array $carry, array $pair) => [...$carry, $decodePair($pair, $pairValueState)],
+ []
+ );
+ }
+
+ /**
+ * Converts a collection of key/value pairs and returns
+ * the store PHP variables as elements of an array.
+ */
+ public static function convert(iterable $pairs): array
+ {
+ $returnedValue = [];
+ foreach ($pairs as $pair) {
+ $returnedValue = self::extractPhpVariable($returnedValue, $pair);
+ }
+
+ return $returnedValue;
+ }
+
+ /**
+ * Parses a query pair like parse_str without mangling the results array keys.
+ *
+ * <ul>
+ * <li>empty name are not saved</li>
+ * <li>If the value from name is duplicated its corresponding value will be overwritten</li>
+ * <li>if no "[" is detected the value is added to the return array with the name as index</li>
+ * <li>if no "]" is detected after detecting a "[" the value is added to the return array with the name as index</li>
+ * <li>if there's a mismatch in bracket usage the remaining part is dropped</li>
+ * <li>“.” and “ ” are not converted to “_”</li>
+ * <li>If there is no “]”, then the first “[” is not converted to becomes an “_”</li>
+ * <li>no whitespace trimming is done on the key value</li>
+ * </ul>
+ *
+ * @see https://php.net/parse_str
+ * @see https://wiki.php.net/rfc/on_demand_name_mangling
+ * @see https://github.com/php/php-src/blob/master/ext/standard/tests/strings/parse_str_basic1.phpt
+ * @see https://github.com/php/php-src/blob/master/ext/standard/tests/strings/parse_str_basic2.phpt
+ * @see https://github.com/php/php-src/blob/master/ext/standard/tests/strings/parse_str_basic3.phpt
+ * @see https://github.com/php/php-src/blob/master/ext/standard/tests/strings/parse_str_basic4.phpt
+ *
+ * @param array $data the submitted array
+ * @param array|string $name the pair key
+ * @param string $value the pair value
+ */
+ private static function extractPhpVariable(array $data, array|string $name, string $value = ''): array
+ {
+ if (is_array($name)) {
+ [$name, $value] = $name;
+ $value = rawurldecode((string) $value);
+ }
+
+ if ('' === $name) {
+ return $data;
+ }
+
+ $leftBracketPosition = strpos($name, '[');
+ if (false === $leftBracketPosition) {
+ $data[$name] = $value;
+
+ return $data;
+ }
+
+ $rightBracketPosition = strpos($name, ']', $leftBracketPosition);
+ if (false === $rightBracketPosition) {
+ $data[$name] = $value;
+
+ return $data;
+ }
+
+ $key = substr($name, 0, $leftBracketPosition);
+ if ('' === $key) {
+ $key = '0';
+ }
+
+ if (!array_key_exists($key, $data) || !is_array($data[$key])) {
+ $data[$key] = [];
+ }
+
+ $remaining = substr($name, $rightBracketPosition + 1);
+ if (!str_starts_with($remaining, '[') || !str_contains($remaining, ']')) {
+ $remaining = '';
+ }
+
+ $name = substr($name, $leftBracketPosition + 1, $rightBracketPosition - $leftBracketPosition - 1).$remaining;
+ if ('' === $name) {
+ $data[$key][] = $value;
+
+ return $data;
+ }
+
+ $data[$key] = self::extractPhpVariable($data[$key], $name, $value);
+
+ return $data;
+ }
+}
diff --git a/vendor/league/uri-interfaces/UriString.php b/vendor/league/uri-interfaces/UriString.php
new file mode 100644
index 000000000..a79184982
--- /dev/null
+++ b/vendor/league/uri-interfaces/UriString.php
@@ -0,0 +1,513 @@
+<?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\Exceptions\ConversionFailed;
+use League\Uri\Exceptions\MissingFeature;
+use League\Uri\Exceptions\SyntaxError;
+use League\Uri\Idna\Converter;
+use Stringable;
+
+use function array_merge;
+use function explode;
+use function filter_var;
+use function inet_pton;
+use function preg_match;
+use function rawurldecode;
+use function sprintf;
+use function strpos;
+use function substr;
+
+use const FILTER_FLAG_IPV6;
+use const FILTER_VALIDATE_IP;
+
+/**
+ * A class to parse a URI string according to RFC3986.
+ *
+ * @link https://tools.ietf.org/html/rfc3986
+ * @package League\Uri
+ * @author Ignace Nyamagana Butera <nyamsprod@gmail.com>
+ * @since 6.0.0
+ *
+ * @phpstan-type AuthorityMap array{user:?string, pass:?string, host:?string, port:?int}
+ * @phpstan-type ComponentMap array{scheme:?string, user:?string, pass:?string, host:?string, port:?int, path:string, query:?string, fragment:?string}
+ * @phpstan-type InputComponentMap array{scheme? : ?string, user? : ?string, pass? : ?string, host? : ?string, port? : ?int, path? : ?string, query? : ?string, fragment? : ?string}
+ */
+final class UriString
+{
+ /**
+ * Default URI component values.
+ *
+ * @var ComponentMap
+ */
+ private const URI_COMPONENTS = [
+ 'scheme' => null, 'user' => null, 'pass' => null, 'host' => null,
+ 'port' => null, 'path' => '', 'query' => null, 'fragment' => null,
+ ];
+
+ /**
+ * Simple URI which do not need any parsing.
+ *
+ * @var array<string, array<string>>
+ */
+ private const URI_SHORTCUTS = [
+ '' => [],
+ '#' => ['fragment' => ''],
+ '?' => ['query' => ''],
+ '?#' => ['query' => '', 'fragment' => ''],
+ '/' => ['path' => '/'],
+ '//' => ['host' => ''],
+ ];
+
+ /**
+ * Range of invalid characters in URI string.
+ *
+ * @var string
+ */
+ private const REGEXP_INVALID_URI_CHARS = '/[\x00-\x1f\x7f]/';
+
+ /**
+ * RFC3986 regular expression URI splitter.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#appendix-B
+ * @var string
+ */
+ private const REGEXP_URI_PARTS = ',^
+ (?<scheme>(?<scontent>[^:/?\#]+):)? # URI scheme component
+ (?<authority>//(?<acontent>[^/?\#]*))? # URI authority part
+ (?<path>[^?\#]*) # URI path component
+ (?<query>\?(?<qcontent>[^\#]*))? # URI query component
+ (?<fragment>\#(?<fcontent>.*))? # URI fragment component
+ ,x';
+
+ /**
+ * URI scheme regular expression.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.1
+ * @var string
+ */
+ private const REGEXP_URI_SCHEME = '/^([a-z][a-z\d+.-]*)?$/i';
+
+ /**
+ * IPvFuture regular expression.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
+ * @var string
+ */
+ private const REGEXP_IP_FUTURE = '/^
+ v(?<version>[A-F0-9])+\.
+ (?:
+ (?<unreserved>[a-z0-9_~\-\.])|
+ (?<sub_delims>[!$&\'()*+,;=:]) # also include the : character
+ )+
+ $/ix';
+
+ /**
+ * General registered name regular expression.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
+ * @var string
+ */
+ private const REGEXP_REGISTERED_NAME = '/(?(DEFINE)
+ (?<unreserved>[a-z0-9_~\-]) # . is missing as it is used to separate labels
+ (?<sub_delims>[!$&\'()*+,;=])
+ (?<encoded>%[A-F0-9]{2})
+ (?<reg_name>(?:(?&unreserved)|(?&sub_delims)|(?&encoded))*)
+ )
+ ^(?:(?&reg_name)\.)*(?&reg_name)\.?$/ix';
+
+ /**
+ * Invalid characters in host regular expression.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
+ * @var string
+ */
+ private const REGEXP_INVALID_HOST_CHARS = '/
+ [:\/?#\[\]@ ] # gen-delims characters as well as the space character
+ /ix';
+
+ /**
+ * Invalid path for URI without scheme and authority regular expression.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.3
+ * @var string
+ */
+ private const REGEXP_INVALID_PATH = ',^(([^/]*):)(.*)?/,';
+
+ /**
+ * Host and Port splitter regular expression.
+ *
+ * @var string
+ */
+ private const REGEXP_HOST_PORT = ',^(?<host>\[.*\]|[^:]*)(:(?<port>.*))?$,';
+
+ /**
+ * IDN Host detector regular expression.
+ *
+ * @var string
+ */
+ private const REGEXP_IDN_PATTERN = '/[^\x20-\x7f]/';
+
+ /**
+ * Only the address block fe80::/10 can have a Zone ID attach to
+ * let's detect the link local significant 10 bits.
+ *
+ * @var string
+ */
+ private const ZONE_ID_ADDRESS_BLOCK = "\xfe\x80";
+
+ /**
+ * Maximum number of host cached.
+ *
+ * @var int
+ */
+ private const MAXIMUM_HOST_CACHED = 100;
+
+ /**
+ * Generate a URI string representation from its parsed representation
+ * returned by League\UriString::parse() or PHP's parse_url.
+ *
+ * If you supply your own array, you are responsible for providing
+ * valid components without their URI delimiters.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-5.3
+ * @link https://tools.ietf.org/html/rfc3986#section-7.5
+ *
+ * @param InputComponentMap $components
+ */
+ public static function build(array $components): string
+ {
+ return self::buildUri(
+ $components['scheme'] ?? null,
+ self::buildAuthority($components),
+ $components['path'] ?? '',
+ $components['query'] ?? null,
+ $components['fragment'] ?? null,
+ );
+ }
+
+ /**
+ * Generate a URI string representation based on RFC3986 algorithm.
+ *
+ * valid URI component MUST be provided without their URI delimiters
+ * but properly encoded.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-5.3
+ * @link https://tools.ietf.org/html/rfc3986#section-7.5
+ */
+ public static function buildUri(
+ ?string $scheme,
+ ?string $authority,
+ string $path,
+ ?string $query,
+ ?string $fragment,
+ ): string {
+ $uri = '';
+ if (null !== $scheme) {
+ $uri .= $scheme.':';
+ }
+
+ if (null !== $authority) {
+ $uri .= '//'.$authority;
+ }
+
+ $uri .= $path;
+ if (null !== $query) {
+ $uri .= '?'.$query;
+ }
+
+ if (null !== $fragment) {
+ $uri .= '#'.$fragment;
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Generate a URI authority representation from its parsed representation.
+ *
+ * @param InputComponentMap $components
+ */
+ public static function buildAuthority(array $components): ?string
+ {
+ if (!isset($components['host'])) {
+ return null;
+ }
+
+ $authority = $components['host'];
+ if (isset($components['port'])) {
+ $authority .= ':'.$components['port'];
+ }
+
+ if (!isset($components['user'])) {
+ return $authority;
+ }
+
+ $authority = '@'.$authority;
+ if (!isset($components['pass'])) {
+ return $components['user'].$authority;
+ }
+
+ return $components['user'].':'.$components['pass'].$authority;
+ }
+
+ /**
+ * Parse a URI string into its components.
+ *
+ * This method parses a URI and returns an associative array containing any
+ * of the various components of the URI that are present.
+ *
+ * <code>
+ * $components = UriString::parse('http://foo@test.example.com:42?query#');
+ * var_export($components);
+ * //will display
+ * array(
+ * 'scheme' => 'http', // the URI scheme component
+ * 'user' => 'foo', // the URI user component
+ * 'pass' => null, // the URI pass component
+ * 'host' => 'test.example.com', // the URI host component
+ * 'port' => 42, // the URI port component
+ * 'path' => '', // the URI path component
+ * 'query' => 'query', // the URI query component
+ * 'fragment' => '', // the URI fragment component
+ * );
+ * </code>
+ *
+ * The returned array is similar to PHP's parse_url return value with the following
+ * differences:
+ *
+ * <ul>
+ * <li>All components are always present in the returned array</li>
+ * <li>Empty and undefined component are treated differently. And empty component is
+ * set to the empty string while an undefined component is set to the `null` value.</li>
+ * <li>The path component is never undefined</li>
+ * <li>The method parses the URI following the RFC3986 rules, but you are still
+ * required to validate the returned components against its related scheme specific rules.</li>
+ * </ul>
+ *
+ * @link https://tools.ietf.org/html/rfc3986
+ *
+ * @throws SyntaxError if the URI contains invalid characters
+ * @throws SyntaxError if the URI contains an invalid scheme
+ * @throws SyntaxError if the URI contains an invalid path
+ *
+ * @return ComponentMap
+ */
+ public static function parse(Stringable|string|int $uri): array
+ {
+ $uri = (string) $uri;
+ if (isset(self::URI_SHORTCUTS[$uri])) {
+ /** @var ComponentMap $components */
+ $components = array_merge(self::URI_COMPONENTS, self::URI_SHORTCUTS[$uri]);
+
+ return $components;
+ }
+
+ if (1 === preg_match(self::REGEXP_INVALID_URI_CHARS, $uri)) {
+ throw new SyntaxError(sprintf('The uri `%s` contains invalid characters', $uri));
+ }
+
+ //if the first character is a known URI delimiter parsing can be simplified
+ $first_char = $uri[0];
+
+ //The URI is made of the fragment only
+ if ('#' === $first_char) {
+ [, $fragment] = explode('#', $uri, 2);
+ $components = self::URI_COMPONENTS;
+ $components['fragment'] = $fragment;
+
+ return $components;
+ }
+
+ //The URI is made of the query and fragment
+ if ('?' === $first_char) {
+ [, $partial] = explode('?', $uri, 2);
+ [$query, $fragment] = explode('#', $partial, 2) + [1 => null];
+ $components = self::URI_COMPONENTS;
+ $components['query'] = $query;
+ $components['fragment'] = $fragment;
+
+ return $components;
+ }
+
+ //use RFC3986 URI regexp to split the URI
+ preg_match(self::REGEXP_URI_PARTS, $uri, $parts);
+ $parts += ['query' => '', 'fragment' => ''];
+
+ if (':' === ($parts['scheme'] ?? null) || 1 !== preg_match(self::REGEXP_URI_SCHEME, $parts['scontent'] ?? '')) {
+ throw new SyntaxError(sprintf('The uri `%s` contains an invalid scheme', $uri));
+ }
+
+ if ('' === ($parts['scheme'] ?? '').($parts['authority'] ?? '') && 1 === preg_match(self::REGEXP_INVALID_PATH, $parts['path'] ?? '')) {
+ throw new SyntaxError(sprintf('The uri `%s` contains an invalid path.', $uri));
+ }
+
+ /** @var ComponentMap $components */
+ $components = array_merge(
+ self::URI_COMPONENTS,
+ '' === ($parts['authority'] ?? null) ? [] : self::parseAuthority($parts['acontent'] ?? null),
+ [
+ 'path' => $parts['path'] ?? '',
+ 'scheme' => '' === ($parts['scheme'] ?? null) ? null : ($parts['scontent'] ?? null),
+ 'query' => '' === $parts['query'] ? null : ($parts['qcontent'] ?? null),
+ 'fragment' => '' === $parts['fragment'] ? null : ($parts['fcontent'] ?? null),
+ ]
+ );
+
+ return $components;
+ }
+
+ /**
+ * Parses the URI authority part.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.2
+ *
+ * @throws SyntaxError If the port component is invalid
+ *
+ * @return AuthorityMap
+ */
+ public static function parseAuthority(Stringable|string|null $authority): array
+ {
+ $components = ['user' => null, 'pass' => null, 'host' => null, 'port' => null];
+ if (null === $authority) {
+ return $components;
+ }
+
+ $authority = (string) $authority;
+ $components['host'] = '';
+ if ('' === $authority) {
+ return $components;
+ }
+
+ $parts = explode('@', $authority, 2);
+ if (isset($parts[1])) {
+ [$components['user'], $components['pass']] = explode(':', $parts[0], 2) + [1 => null];
+ }
+
+ preg_match(self::REGEXP_HOST_PORT, $parts[1] ?? $parts[0], $matches);
+ $matches += ['port' => ''];
+
+ $components['port'] = self::filterPort($matches['port']);
+ $components['host'] = self::filterHost($matches['host'] ?? '');
+
+ return $components;
+ }
+
+ /**
+ * Filter and format the port component.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
+ *
+ * @throws SyntaxError if the registered name is invalid
+ */
+ private static function filterPort(string $port): ?int
+ {
+ return match (true) {
+ '' === $port => null,
+ 1 === preg_match('/^\d*$/', $port) => (int) $port,
+ default => throw new SyntaxError(sprintf('The port `%s` is invalid', $port)),
+ };
+ }
+
+ /**
+ * Returns whether a hostname is valid.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
+ *
+ * @throws SyntaxError if the registered name is invalid
+ */
+ private static function filterHost(string $host): string
+ {
+ if ('' === $host) {
+ return $host;
+ }
+
+ /** @var array<string, 1> $hostCache */
+ static $hostCache = [];
+ if (isset($hostCache[$host])) {
+ return $host;
+ }
+
+ if (self::MAXIMUM_HOST_CACHED < count($hostCache)) {
+ array_shift($hostCache);
+ }
+
+ if ('[' !== $host[0] || !str_ends_with($host, ']')) {
+ self::filterRegisteredName($host);
+ $hostCache[$host] = 1;
+
+ return $host;
+ }
+
+ if (self::isIpHost(substr($host, 1, -1))) {
+ $hostCache[$host] = 1;
+
+ return $host;
+ }
+
+ throw new SyntaxError(sprintf('Host `%s` is invalid : the IP host is malformed', $host));
+ }
+
+ /**
+ * Throws if the host is not a registered name and not a valid IDN host.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
+ *
+ * @throws SyntaxError if the registered name is invalid
+ * @throws MissingFeature if IDN support or ICU requirement are not available or met.
+ * @throws ConversionFailed if the submitted IDN host cannot be converted to a valid ascii form
+ */
+ private static function filterRegisteredName(string $host): void
+ {
+ $formattedHost = rawurldecode($host);
+ if (1 === preg_match(self::REGEXP_REGISTERED_NAME, $formattedHost)) {
+ return;
+ }
+
+ //to test IDN host non-ascii characters must be present in the host
+ if (1 !== preg_match(self::REGEXP_IDN_PATTERN, $formattedHost)) {
+ throw new SyntaxError(sprintf('Host `%s` is invalid: the host is not a valid registered name', $host));
+ }
+
+ Converter::toAsciiOrFail($host);
+ }
+
+ /**
+ * Validates a IPv6/IPfuture host.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
+ * @link https://tools.ietf.org/html/rfc6874#section-2
+ * @link https://tools.ietf.org/html/rfc6874#section-4
+ */
+ private static function isIpHost(string $ipHost): bool
+ {
+ if (false !== filter_var($ipHost, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ return true;
+ }
+
+ if (1 === preg_match(self::REGEXP_IP_FUTURE, $ipHost, $matches)) {
+ return !in_array($matches['version'], ['4', '6'], true);
+ }
+
+ $pos = strpos($ipHost, '%');
+ if (false === $pos || 1 === preg_match(self::REGEXP_INVALID_HOST_CHARS, rawurldecode(substr($ipHost, $pos)))) {
+ return false;
+ }
+
+ $ipHost = substr($ipHost, 0, $pos);
+
+ return false !== filter_var($ipHost, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
+ && str_starts_with((string)inet_pton($ipHost), self::ZONE_ID_ADDRESS_BLOCK);
+ }
+}
diff --git a/vendor/league/uri-interfaces/composer.json b/vendor/league/uri-interfaces/composer.json
new file mode 100644
index 000000000..1e36a98a2
--- /dev/null
+++ b/vendor/league/uri-interfaces/composer.json
@@ -0,0 +1,70 @@
+{
+ "name": "league/uri-interfaces",
+ "type": "library",
+ "description" : "Common interfaces and classes for URI representation and interaction",
+ "keywords": [
+ "url",
+ "uri",
+ "rfc3986",
+ "rfc3987",
+ "rfc6570",
+ "psr-7",
+ "parse_url",
+ "http",
+ "https",
+ "ws",
+ "ftp",
+ "data-uri",
+ "file-uri",
+ "parse_str",
+ "query-string",
+ "querystring",
+ "hostname"
+ ],
+ "license": "MIT",
+ "homepage": "https://uri.thephpleague.com",
+ "authors": [
+ {
+ "name" : "Ignace Nyamagana Butera",
+ "email" : "nyamsprod@gmail.com",
+ "homepage" : "https://nyamsprod.com"
+ }
+ ],
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nyamsprod"
+ }
+ ],
+ "require": {
+ "php" : "^8.1",
+ "ext-filter": "*",
+ "psr/http-message": "^1.1 || ^2.0",
+ "psr/http-factory": "^1"
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Uri\\": ""
+ }
+ },
+ "suggest": {
+ "ext-bcmath": "to improve IPV4 host parsing",
+ "ext-gmp": "to improve IPV4 host parsing",
+ "ext-intl": "to handle IDN host with the best performance",
+ "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"
+ }
+ },
+ "support": {
+ "forum": "https://thephpleague.slack.com",
+ "docs": "https://uri.thephpleague.com",
+ "issues": "https://github.com/thephpleague/uri-src/issues"
+ },
+ "config": {
+ "sort-packages": true
+ }
+}
diff --git a/vendor/league/uri/BaseUri.php b/vendor/league/uri/BaseUri.php
new file mode 100644
index 000000000..c29dc2799
--- /dev/null
+++ b/vendor/league/uri/BaseUri.php
@@ -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;
+ }
+}
diff --git a/vendor/league/uri/Http.php b/vendor/league/uri/Http.php
new file mode 100644
index 000000000..0293a9864
--- /dev/null
+++ b/vendor/league/uri/Http.php
@@ -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);
+ }
+}
diff --git a/vendor/league/uri/HttpFactory.php b/vendor/league/uri/HttpFactory.php
new file mode 100644
index 000000000..3508e67cc
--- /dev/null
+++ b/vendor/league/uri/HttpFactory.php
@@ -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);
+ }
+}
diff --git a/vendor/league/uri/LICENSE b/vendor/league/uri/LICENSE
new file mode 100644
index 000000000..3b52528f2
--- /dev/null
+++ b/vendor/league/uri/LICENSE
@@ -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.
diff --git a/vendor/league/uri/Uri.php b/vendor/league/uri/Uri.php
new file mode 100644
index 000000000..00383428d
--- /dev/null
+++ b/vendor/league/uri/Uri.php
@@ -0,0 +1,1328 @@
+<?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 finfo;
+use League\Uri\Contracts\UriComponentInterface;
+use League\Uri\Contracts\UriException;
+use League\Uri\Contracts\UriInterface;
+use League\Uri\Exceptions\ConversionFailed;
+use League\Uri\Exceptions\MissingFeature;
+use League\Uri\Exceptions\SyntaxError;
+use League\Uri\Idna\Converter as IdnConverter;
+use League\Uri\UriTemplate\TemplateCanNotBeExpanded;
+use Psr\Http\Message\UriInterface as Psr7UriInterface;
+use SensitiveParameter;
+use Stringable;
+
+use function array_filter;
+use function array_map;
+use function base64_decode;
+use function base64_encode;
+use function count;
+use function explode;
+use function file_get_contents;
+use function filter_var;
+use function implode;
+use function in_array;
+use function inet_pton;
+use function ltrim;
+use function preg_match;
+use function preg_replace_callback;
+use function rawurlencode;
+use function str_contains;
+use function str_replace;
+use function strlen;
+use function strpos;
+use function strspn;
+use function strtolower;
+use function substr;
+
+use const FILEINFO_MIME;
+use const FILTER_FLAG_IPV4;
+use const FILTER_FLAG_IPV6;
+use const FILTER_NULL_ON_FAILURE;
+use const FILTER_VALIDATE_BOOLEAN;
+use const FILTER_VALIDATE_IP;
+
+/**
+ * @phpstan-import-type ComponentMap from UriString
+ * @phpstan-import-type InputComponentMap from UriString
+ */
+final class Uri implements UriInterface
+{
+ /**
+ * RFC3986 invalid characters.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-2.2
+ *
+ * @var string
+ */
+ private const REGEXP_INVALID_CHARS = '/[\x00-\x1f\x7f]/';
+
+ /**
+ * RFC3986 schema regular expression pattern.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.1
+ *
+ * @var string
+ */
+ private const REGEXP_SCHEME = ',^[a-z]([-a-z\d+.]+)?$,i';
+
+ /**
+ * RFC3986 host identified by a registered name regular expression pattern.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
+ *
+ * @var string
+ */
+ private const REGEXP_HOST_REGNAME = '/^(
+ (?<unreserved>[a-z\d_~\-\.])|
+ (?<sub_delims>[!$&\'()*+,;=])|
+ (?<encoded>%[A-F\d]{2})
+ )+$/x';
+
+ /**
+ * RFC3986 delimiters of the generic URI components regular expression pattern.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-2.2
+ *
+ * @var string
+ */
+ private const REGEXP_HOST_GEN_DELIMS = '/[:\/?#\[\]@ ]/'; // Also includes space.
+
+ /**
+ * RFC3986 IPvFuture regular expression pattern.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3.2.2
+ *
+ * @var string
+ */
+ private const REGEXP_HOST_IP_FUTURE = '/^
+ v(?<version>[A-F\d])+\.
+ (?:
+ (?<unreserved>[a-z\d_~\-\.])|
+ (?<sub_delims>[!$&\'()*+,;=:]) # also include the : character
+ )+
+ $/ix';
+
+ /**
+ * RFC3986 IPvFuture host and port component.
+ *
+ * @var string
+ */
+ private const REGEXP_HOST_PORT = ',^(?<host>(\[.*]|[^:])*)(:(?<port>[^/?#]*))?$,x';
+
+ /**
+ * Significant 10 bits of IP to detect Zone ID regular expression pattern.
+ *
+ * @var string
+ */
+ private const HOST_ADDRESS_BLOCK = "\xfe\x80";
+
+ /**
+ * Regular expression pattern to for file URI.
+ * <volume> contains the volume but not the volume separator.
+ * The volume separator may be URL-encoded (`|` as `%7C`) by ::formatPath(),
+ * so we account for that here.
+ *
+ * @var string
+ */
+ private const REGEXP_FILE_PATH = ',^(?<delim>/)?(?<volume>[a-zA-Z])(?:[:|\|]|%7C)(?<rest>.*)?,';
+
+ /**
+ * Mimetype regular expression pattern.
+ *
+ * @link https://tools.ietf.org/html/rfc2397
+ *
+ * @var string
+ */
+ private const REGEXP_MIMETYPE = ',^\w+/[-.\w]+(?:\+[-.\w]+)?$,';
+
+ /**
+ * Base64 content regular expression pattern.
+ *
+ * @link https://tools.ietf.org/html/rfc2397
+ *
+ * @var string
+ */
+ private const REGEXP_BINARY = ',(;|^)base64$,';
+
+ /**
+ * Windows file path string regular expression pattern.
+ * <root> contains both the volume and volume separator.
+ *
+ * @var string
+ */
+ private const REGEXP_WINDOW_PATH = ',^(?<root>[a-zA-Z][:|\|]),';
+
+ /**
+ * Supported schemes and corresponding default port.
+ *
+ * @var array<string, int|null>
+ */
+ private const SCHEME_DEFAULT_PORT = [
+ 'data' => null,
+ 'file' => null,
+ 'ftp' => 21,
+ 'gopher' => 70,
+ 'http' => 80,
+ 'https' => 443,
+ 'ws' => 80,
+ 'wss' => 443,
+ ];
+
+ /**
+ * Maximum number of cached items.
+ *
+ * @var int
+ */
+ private const MAXIMUM_CACHED_ITEMS = 100;
+
+ /**
+ * All ASCII letters sorted by typical frequency of occurrence.
+ *
+ * @var string
+ */
+ private const ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
+
+ private readonly ?string $scheme;
+ private readonly ?string $user;
+ private readonly ?string $pass;
+ private readonly ?string $userInfo;
+ private readonly ?string $host;
+ private readonly ?int $port;
+ private readonly ?string $authority;
+ private readonly string $path;
+ private readonly ?string $query;
+ private readonly ?string $fragment;
+ private readonly string $uri;
+
+ private function __construct(
+ ?string $scheme,
+ ?string $user,
+ #[SensitiveParameter] ?string $pass,
+ ?string $host,
+ ?int $port,
+ string $path,
+ ?string $query,
+ ?string $fragment
+ ) {
+ $this->scheme = $this->formatScheme($scheme);
+ $this->user = Encoder::encodeUser($user);
+ $this->pass = Encoder::encodePassword($pass);
+ $this->host = $this->formatHost($host);
+ $this->port = $this->formatPort($port);
+ $this->path = $this->formatPath($path);
+ $this->query = Encoder::encodeQueryOrFragment($query);
+ $this->fragment = Encoder::encodeQueryOrFragment($fragment);
+ $this->userInfo = $this->formatUserInfo($this->user, $this->pass);
+ $this->authority = UriString::buildAuthority($this->toComponents());
+ $this->uri = UriString::buildUri($this->scheme, $this->authority, $this->path, $this->query, $this->fragment);
+
+ $this->assertValidState();
+ }
+
+ /**
+ * Format the Scheme and Host component.
+ *
+ * @throws SyntaxError if the scheme is invalid
+ */
+ private function formatScheme(?string $scheme): ?string
+ {
+ if (null === $scheme) {
+ return null;
+ }
+
+ $formattedScheme = strtolower($scheme);
+ static $cache = [];
+ if (isset($cache[$formattedScheme])) {
+ return $formattedScheme;
+ }
+
+ if (
+ !array_key_exists($formattedScheme, self::SCHEME_DEFAULT_PORT)
+ && 1 !== preg_match(self::REGEXP_SCHEME, $formattedScheme)
+ ) {
+ throw new SyntaxError('The scheme `'.$scheme.'` is invalid.');
+ }
+
+ $cache[$formattedScheme] = 1;
+ if (self::MAXIMUM_CACHED_ITEMS < count($cache)) {
+ array_shift($cache);
+ }
+
+ return $formattedScheme;
+ }
+
+ /**
+ * Set the UserInfo component.
+ */
+ private function formatUserInfo(
+ ?string $user,
+ #[SensitiveParameter] ?string $password
+ ): ?string {
+ return match (null) {
+ $password => $user,
+ default => $user.':'.$password,
+ };
+ }
+
+ /**
+ * Validate and Format the Host component.
+ */
+ private function formatHost(?string $host): ?string
+ {
+ if (null === $host || '' === $host) {
+ return $host;
+ }
+
+ static $cache = [];
+ if (isset($cache[$host])) {
+ return $cache[$host];
+ }
+
+ $formattedHost = '[' === $host[0] ? $this->formatIp($host) : $this->formatRegisteredName($host);
+ $cache[$host] = $formattedHost;
+ if (self::MAXIMUM_CACHED_ITEMS < count($cache)) {
+ array_shift($cache);
+ }
+
+ return $formattedHost;
+ }
+
+ /**
+ * Validate and format a registered name.
+ *
+ * The host is converted to its ascii representation if needed
+ *
+ * @throws MissingFeature if the submitted host required missing or misconfigured IDN support
+ * @throws SyntaxError if the submitted host is not a valid registered name
+ * @throws ConversionFailed if the submitted IDN host cannot be converted to a valid ascii form
+ */
+ private function formatRegisteredName(string $host): string
+ {
+ $formattedHost = rawurldecode($host);
+
+ return match (1) {
+ preg_match(self::REGEXP_HOST_REGNAME, $formattedHost) => $formattedHost,
+ preg_match(self::REGEXP_HOST_GEN_DELIMS, $formattedHost) => throw new SyntaxError('The host `'.$host.'` is invalid : a registered name cannot contain URI delimiters or spaces.'),
+ default => IdnConverter::toAsciiOrFail($host),
+ };
+ }
+
+ /**
+ * Validate and Format the IPv6/IPvfuture host.
+ *
+ * @throws SyntaxError if the submitted host is not a valid IP host
+ */
+ private function formatIp(string $host): string
+ {
+ $ip = substr($host, 1, -1);
+ if (false !== filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ return $host;
+ }
+
+ if (1 === preg_match(self::REGEXP_HOST_IP_FUTURE, $ip, $matches) && !in_array($matches['version'], ['4', '6'], true)) {
+ return $host;
+ }
+
+ $pos = strpos($ip, '%');
+ if (false === $pos) {
+ throw new SyntaxError('The host `'.$host.'` is invalid : the IP host is malformed.');
+ }
+
+ if (1 === preg_match(self::REGEXP_HOST_GEN_DELIMS, rawurldecode(substr($ip, $pos)))) {
+ throw new SyntaxError('The host `'.$host.'` is invalid : the IP host is malformed.');
+ }
+
+ $ip = substr($ip, 0, $pos);
+ if (false === filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ throw new SyntaxError('The host `'.$host.'` is invalid : the IP host is malformed.');
+ }
+
+ //Only the address block fe80::/10 can have a Zone ID attach to
+ //let's detect the link local significant 10 bits
+ if (str_starts_with((string)inet_pton($ip), self::HOST_ADDRESS_BLOCK)) {
+ return $host;
+ }
+
+ throw new SyntaxError('The host `'.$host.'` is invalid : the IP host is malformed.');
+ }
+
+ /**
+ * Format the Port component.
+ *
+ * @throws SyntaxError
+ */
+ private function formatPort(?int $port = null): ?int
+ {
+ $defaultPort = self::SCHEME_DEFAULT_PORT[$this->scheme] ?? null;
+
+ return match (true) {
+ null === $port, $defaultPort === $port => null,
+ 0 > $port => throw new SyntaxError('The port `'.$port.'` is invalid.'),
+ default => $port,
+ };
+ }
+
+ /**
+ * Create a new instance from a string.
+ */
+ public static function new(Stringable|string $uri = ''): self
+ {
+ $components = match (true) {
+ $uri instanceof UriInterface => $uri->toComponents(),
+ default => UriString::parse($uri),
+ };
+
+ return new self(
+ $components['scheme'],
+ $components['user'],
+ $components['pass'],
+ $components['host'],
+ $components['port'],
+ $components['path'],
+ $components['query'],
+ $components['fragment']
+ );
+ }
+
+ /**
+ * Creates 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 {
+ $uri = self::new($uri);
+ $baseUri = BaseUri::from($baseUri ?? $uri);
+
+ /** @var self $uri */
+ $uri = match (true) {
+ $baseUri->isAbsolute() => $baseUri->resolve($uri)->getUri(),
+ default => throw new SyntaxError('the URI `'.$baseUri.'` must be absolute.'),
+ };
+
+ return $uri;
+ }
+
+ /**
+ * Creates a new instance from a template.
+ *
+ * @throws TemplateCanNotBeExpanded if the variables are invalid or missing
+ * @throws UriException if the resulting expansion cannot be converted to a UriInterface instance
+ */
+ public static function fromTemplate(UriTemplate|Stringable|string $template, iterable $variables = []): self
+ {
+ return match (true) {
+ $template instanceof UriTemplate => self::fromComponents($template->expand($variables)->toComponents()),
+ $template instanceof UriTemplate\Template => self::new($template->expand($variables)),
+ default => self::new(UriTemplate\Template::new($template)->expand($variables)),
+ };
+ }
+
+ /**
+ * Create a new instance from a hash representation of the URI similar
+ * to PHP parse_url function result.
+ *
+ * @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 (null === $components['path']) {
+ $components['path'] = '';
+ }
+
+ return new self(
+ $components['scheme'],
+ $components['user'],
+ $components['pass'],
+ $components['host'],
+ $components['port'],
+ $components['path'],
+ $components['query'],
+ $components['fragment']
+ );
+ }
+
+ /**
+ * Create a new instance from a data file path.
+ *
+ * @param resource|null $context
+ *
+ * @throws MissingFeature If ext/fileinfo is not installed
+ * @throws SyntaxError If the file does not exist or is not readable
+ */
+ public static function fromFileContents(Stringable|string $path, $context = null): self
+ {
+ FeatureDetection::supportsFileDetection();
+
+ $path = (string) $path;
+ $fileArguments = [$path, false];
+ $mimeArguments = [$path, FILEINFO_MIME];
+ if (null !== $context) {
+ $fileArguments[] = $context;
+ $mimeArguments[] = $context;
+ }
+
+ set_error_handler(fn (int $errno, string $errstr, string $errfile, int $errline) => true);
+ $raw = file_get_contents(...$fileArguments);
+ restore_error_handler();
+
+ if (false === $raw) {
+ throw new SyntaxError('The file `'.$path.'` does not exist or is not readable.');
+ }
+
+ $mimetype = (string) (new finfo(FILEINFO_MIME))->file(...$mimeArguments);
+
+ return Uri::fromComponents([
+ 'scheme' => 'data',
+ 'path' => str_replace(' ', '', $mimetype.';base64,'.base64_encode($raw)),
+ ]);
+ }
+
+ /**
+ * Create a new instance from a data URI string.
+ *
+ * @throws SyntaxError If the parameter syntax is invalid
+ */
+ public static function fromData(string $data, string $mimetype = '', string $parameters = ''): self
+ {
+ static $regexpMimetype = ',^\w+/[-.\w]+(?:\+[-.\w]+)?$,';
+
+ $mimetype = match (true) {
+ '' === $mimetype => 'text/plain',
+ 1 === preg_match($regexpMimetype, $mimetype) => $mimetype,
+ default => throw new SyntaxError('Invalid mimeType, `'.$mimetype.'`.'),
+ };
+
+ if ('' === $parameters) {
+ return self::fromComponents([
+ 'scheme' => 'data',
+ 'path' => self::formatDataPath($mimetype.','.rawurlencode($data)),
+ ]);
+ }
+
+ $isInvalidParameter = static function (string $parameter): bool {
+ $properties = explode('=', $parameter);
+
+ return 2 !== count($properties) || 'base64' === strtolower($properties[0]);
+ };
+
+ if (str_starts_with($parameters, ';')) {
+ $parameters = substr($parameters, 1);
+ }
+
+ return match ([]) {
+ array_filter(explode(';', $parameters), $isInvalidParameter) => self::fromComponents([
+ 'scheme' => 'data',
+ 'path' => self::formatDataPath($mimetype.';'.$parameters.','.rawurlencode($data)),
+ ]),
+ default => throw new SyntaxError(sprintf('Invalid mediatype parameters, `%s`.', $parameters))
+ };
+ }
+
+ /**
+ * Create a new instance from a Unix path string.
+ */
+ public static function fromUnixPath(Stringable|string $path): self
+ {
+ $path = implode('/', array_map(rawurlencode(...), explode('/', (string) $path)));
+
+ return Uri::fromComponents(match (true) {
+ '/' !== ($path[0] ?? '') => ['path' => $path],
+ default => ['path' => $path, 'scheme' => 'file', 'host' => ''],
+ });
+ }
+
+ /**
+ * Create a new instance from a local Windows path string.
+ */
+ public static function fromWindowsPath(Stringable|string $path): self
+ {
+ $path = (string) $path;
+ $root = '';
+ if (1 === preg_match(self::REGEXP_WINDOW_PATH, $path, $matches)) {
+ $root = substr($matches['root'], 0, -1).':';
+ $path = substr($path, strlen($root));
+ }
+ $path = str_replace('\\', '/', $path);
+ $path = implode('/', array_map(rawurlencode(...), explode('/', $path)));
+
+ //Local Windows absolute path
+ if ('' !== $root) {
+ return Uri::fromComponents(['path' => '/'.$root.$path, 'scheme' => 'file', 'host' => '']);
+ }
+
+ //UNC Windows Path
+ if (!str_starts_with($path, '//')) {
+ return Uri::fromComponents(['path' => $path]);
+ }
+
+ [$host, $path] = explode('/', substr($path, 2), 2) + [1 => ''];
+
+ return Uri::fromComponents(['host' => $host, 'path' => '/'.$path, 'scheme' => 'file']);
+ }
+
+ /**
+ * Creates a new instance from a RFC8089 compatible URI.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc8089
+ */
+ public static function fromRfc8089(Stringable|string $uri): UriInterface
+ {
+ $fileUri = self::new((string) preg_replace(',^(file:/)([^/].*)$,i', 'file:///$2', (string) $uri));
+ $scheme = $fileUri->getScheme();
+
+ return match (true) {
+ 'file' !== $scheme => throw new SyntaxError('As per RFC8089, the URI scheme must be `file`.'),
+ 'localhost' === $fileUri->getAuthority() => $fileUri->withHost(''),
+ default => $fileUri,
+ };
+ }
+
+ /**
+ * Create a new instance from the environment.
+ */
+ public static function fromServer(array $server): self
+ {
+ $components = ['scheme' => self::fetchScheme($server)];
+ [$components['user'], $components['pass']] = self::fetchUserInfo($server);
+ [$components['host'], $components['port']] = self::fetchHostname($server);
+ [$components['path'], $components['query']] = self::fetchRequestUri($server);
+
+ return Uri::fromComponents($components);
+ }
+
+ /**
+ * Returns the environment scheme.
+ */
+ private static function fetchScheme(array $server): string
+ {
+ $server += ['HTTPS' => ''];
+
+ return match (true) {
+ false !== filter_var($server['HTTPS'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) => 'https',
+ default => 'http',
+ };
+ }
+
+ /**
+ * Returns the environment user info.
+ *
+ * @return non-empty-array{0: ?string, 1: ?string}
+ */
+ private static function fetchUserInfo(array $server): array
+ {
+ $server += ['PHP_AUTH_USER' => null, 'PHP_AUTH_PW' => null, 'HTTP_AUTHORIZATION' => ''];
+ $user = $server['PHP_AUTH_USER'];
+ $pass = $server['PHP_AUTH_PW'];
+ if (str_starts_with(strtolower($server['HTTP_AUTHORIZATION']), 'basic')) {
+ $userinfo = base64_decode(substr($server['HTTP_AUTHORIZATION'], 6), true);
+ if (false === $userinfo) {
+ throw new SyntaxError('The user info could not be detected');
+ }
+ [$user, $pass] = explode(':', $userinfo, 2) + [1 => null];
+ }
+
+ if (null !== $user) {
+ $user = rawurlencode($user);
+ }
+
+ if (null !== $pass) {
+ $pass = rawurlencode($pass);
+ }
+
+ return [$user, $pass];
+ }
+
+ /**
+ * Returns the environment host.
+ *
+ * @throws SyntaxError If the host cannot be detected
+ *
+ * @return array{0:string|null, 1:int|null}
+ */
+ private static function fetchHostname(array $server): array
+ {
+ $server += ['SERVER_PORT' => null];
+ if (null !== $server['SERVER_PORT']) {
+ $server['SERVER_PORT'] = (int) $server['SERVER_PORT'];
+ }
+
+ if (isset($server['HTTP_HOST']) && 1 === preg_match(self::REGEXP_HOST_PORT, $server['HTTP_HOST'], $matches)) {
+ $matches += ['host' => null, 'port' => null];
+ if (null !== $matches['port']) {
+ $matches['port'] = (int) $matches['port'];
+ }
+
+ return [$matches['host'], $matches['port'] ?? $server['SERVER_PORT']];
+ }
+
+ if (!isset($server['SERVER_ADDR'])) {
+ throw new SyntaxError('The host could not be detected');
+ }
+
+ if (false === filter_var($server['SERVER_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+ return ['['.$server['SERVER_ADDR'].']', $server['SERVER_PORT']];
+ }
+
+ return [$server['SERVER_ADDR'], $server['SERVER_PORT']];
+ }
+
+ /**
+ * Returns the environment path.
+ *
+ * @return list<?string>
+ */
+ private static function fetchRequestUri(array $server): array
+ {
+ $server += ['IIS_WasUrlRewritten' => null, 'UNENCODED_URL' => '', 'PHP_SELF' => '', 'QUERY_STRING' => null];
+ if ('1' === $server['IIS_WasUrlRewritten'] && '' !== $server['UNENCODED_URL']) {
+ return explode('?', $server['UNENCODED_URL'], 2) + [1 => null];
+ }
+
+ if (isset($server['REQUEST_URI'])) {
+ [$path] = explode('?', $server['REQUEST_URI'], 2);
+ $query = ('' !== $server['QUERY_STRING']) ? $server['QUERY_STRING'] : null;
+
+ return [$path, $query];
+ }
+
+ return [$server['PHP_SELF'], $server['QUERY_STRING']];
+ }
+
+ /**
+ * Format the Path component.
+ */
+ private function formatPath(string $path): string
+ {
+ return match ($this->scheme) {
+ 'data' => Encoder::encodePath(self::formatDataPath($path)),
+ 'file' => $this->formatFilePath(Encoder::encodePath($path)),
+ default => Encoder::encodePath($path),
+ };
+ }
+
+ /**
+ * Filter the Path component.
+ *
+ * @link https://tools.ietf.org/html/rfc2397
+ *
+ * @throws SyntaxError If the path is not compliant with RFC2397
+ */
+ private static function formatDataPath(string $path): string
+ {
+ if ('' == $path) {
+ return 'text/plain;charset=us-ascii,';
+ }
+
+ if (strlen($path) !== strspn($path, self::ASCII) || !str_contains($path, ',')) {
+ throw new SyntaxError('The path `'.$path.'` is invalid according to RFC2937.');
+ }
+
+ $parts = explode(',', $path, 2) + [1 => null];
+ $mediatype = explode(';', (string) $parts[0], 2) + [1 => null];
+ $data = (string) $parts[1];
+ $mimetype = $mediatype[0];
+ if (null === $mimetype || '' === $mimetype) {
+ $mimetype = 'text/plain';
+ }
+
+ $parameters = $mediatype[1];
+ if (null === $parameters || '' === $parameters) {
+ $parameters = 'charset=us-ascii';
+ }
+
+ self::assertValidPath($mimetype, $parameters, $data);
+
+ return $mimetype.';'.$parameters.','.$data;
+ }
+
+ /**
+ * Assert the path is a compliant with RFC2397.
+ *
+ * @link https://tools.ietf.org/html/rfc2397
+ *
+ * @throws SyntaxError If the mediatype or the data are not compliant with the RFC2397
+ */
+ private static function assertValidPath(string $mimetype, string $parameters, string $data): void
+ {
+ if (1 !== preg_match(self::REGEXP_MIMETYPE, $mimetype)) {
+ throw new SyntaxError('The path mimetype `'.$mimetype.'` is invalid.');
+ }
+
+ $isBinary = 1 === preg_match(self::REGEXP_BINARY, $parameters, $matches);
+ if ($isBinary) {
+ $parameters = substr($parameters, 0, - strlen($matches[0]));
+ }
+
+ $res = array_filter(array_filter(explode(';', $parameters), self::validateParameter(...)));
+ if ([] !== $res) {
+ throw new SyntaxError('The path parameters `'.$parameters.'` is invalid.');
+ }
+
+ if (!$isBinary) {
+ return;
+ }
+
+ $res = base64_decode($data, true);
+ if (false === $res || $data !== base64_encode($res)) {
+ throw new SyntaxError('The path data `'.$data.'` is invalid.');
+ }
+ }
+
+ /**
+ * Validate mediatype parameter.
+ */
+ private static function validateParameter(string $parameter): bool
+ {
+ $properties = explode('=', $parameter);
+
+ return 2 != count($properties) || 'base64' === strtolower($properties[0]);
+ }
+
+ /**
+ * Format path component for file scheme.
+ */
+ private function formatFilePath(string $path): string
+ {
+ return (string) preg_replace_callback(
+ self::REGEXP_FILE_PATH,
+ static fn (array $matches): string => $matches['delim'].$matches['volume'].(isset($matches['rest']) ? ':'.$matches['rest'] : ''),
+ $path
+ );
+ }
+
+ /**
+ * assert the URI internal state is valid.
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-3
+ * @link https://tools.ietf.org/html/rfc3986#section-3.3
+ *
+ * @throws SyntaxError if the URI is in an invalid state according to RFC3986
+ * @throws SyntaxError if the URI is in an invalid state according to scheme specific rules
+ */
+ private function assertValidState(): void
+ {
+ if (null !== $this->authority && ('' !== $this->path && '/' !== $this->path[0])) {
+ throw new SyntaxError('If an authority is present the path must be empty or start with a `/`.');
+ }
+
+ if (null === $this->authority && str_starts_with($this->path, '//')) {
+ throw new SyntaxError('If there is no authority the path `'.$this->path.'` cannot start with a `//`.');
+ }
+
+ $pos = strpos($this->path, ':');
+ if (null === $this->authority
+ && null === $this->scheme
+ && false !== $pos
+ && !str_contains(substr($this->path, 0, $pos), '/')
+ ) {
+ throw new SyntaxError('In absence of a scheme and an authority the first path segment cannot contain a colon (":") character.');
+ }
+
+ if (! match ($this->scheme) {
+ 'data' => $this->isUriWithSchemeAndPathOnly(),
+ 'file' => $this->isUriWithSchemeHostAndPathOnly(),
+ 'ftp', 'gopher' => $this->isNonEmptyHostUriWithoutFragmentAndQuery(),
+ 'http', 'https' => $this->isNonEmptyHostUri(),
+ 'ws', 'wss' => $this->isNonEmptyHostUriWithoutFragment(),
+ default => true,
+ }) {
+ throw new SyntaxError('The uri `'.$this->uri.'` is invalid for the `'.$this->scheme.'` scheme.');
+ }
+ }
+
+ /**
+ * URI validation for URI schemes which allows only scheme and path components.
+ */
+ private function isUriWithSchemeAndPathOnly(): bool
+ {
+ return null === $this->authority
+ && null === $this->query
+ && null === $this->fragment;
+ }
+
+ /**
+ * URI validation for URI schemes which allows only scheme, host and path components.
+ */
+ private function isUriWithSchemeHostAndPathOnly(): bool
+ {
+ return null === $this->userInfo
+ && null === $this->port
+ && null === $this->query
+ && null === $this->fragment
+ && !('' != $this->scheme && null === $this->host);
+ }
+
+ /**
+ * URI validation for URI schemes which disallow the empty '' host.
+ */
+ private function isNonEmptyHostUri(): bool
+ {
+ return '' !== $this->host
+ && !(null !== $this->scheme && null === $this->host);
+ }
+
+ /**
+ * URI validation for URIs schemes which disallow the empty '' host
+ * and forbids the fragment component.
+ */
+ private function isNonEmptyHostUriWithoutFragment(): bool
+ {
+ return $this->isNonEmptyHostUri() && null === $this->fragment;
+ }
+
+ /**
+ * URI validation for URIs schemes which disallow the empty '' host
+ * and forbids fragment and query components.
+ */
+ private function isNonEmptyHostUriWithoutFragmentAndQuery(): bool
+ {
+ return $this->isNonEmptyHostUri() && null === $this->fragment && null === $this->query;
+ }
+
+ public function toString(): string
+ {
+ return $this->uri;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function __toString(): string
+ {
+ return $this->toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function jsonSerialize(): string
+ {
+ return $this->toString();
+ }
+
+ /**
+ * @return ComponentMap
+ */
+ public function toComponents(): array
+ {
+ return [
+ 'scheme' => $this->scheme,
+ 'user' => $this->user,
+ 'pass' => $this->pass,
+ 'host' => $this->host,
+ 'port' => $this->port,
+ 'path' => $this->path,
+ 'query' => $this->query,
+ 'fragment' => $this->fragment,
+ ];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getScheme(): ?string
+ {
+ return $this->scheme;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getAuthority(): ?string
+ {
+ return $this->authority;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getUsername(): ?string
+ {
+ return $this->user;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getPassword(): ?string
+ {
+ return $this->pass;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getUserInfo(): ?string
+ {
+ return $this->userInfo;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getHost(): ?string
+ {
+ return $this->host;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getPort(): ?int
+ {
+ return $this->port;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getPath(): string
+ {
+ return match (true) {
+ str_starts_with($this->path, '//') => '/'.ltrim($this->path, '/'),
+ default => $this->path,
+ };
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getQuery(): ?string
+ {
+ return $this->query;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getFragment(): ?string
+ {
+ return $this->fragment;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function withScheme(Stringable|string|null $scheme): UriInterface
+ {
+ $scheme = $this->formatScheme($this->filterString($scheme));
+
+ return match ($scheme) {
+ $this->scheme => $this,
+ default => new self(
+ $scheme,
+ $this->user,
+ $this->pass,
+ $this->host,
+ $this->port,
+ $this->path,
+ $this->query,
+ $this->fragment,
+ ),
+ };
+ }
+
+ /**
+ * Filter a string.
+ *
+ * @throws SyntaxError if the submitted data cannot be converted to string
+ */
+ private function filterString(Stringable|string|null $str): ?string
+ {
+ $str = match (true) {
+ $str instanceof UriComponentInterface => $str->value(),
+ null === $str => null,
+ default => (string) $str,
+ };
+
+ return match (true) {
+ null === $str => null,
+ 1 === preg_match(self::REGEXP_INVALID_CHARS, $str) => throw new SyntaxError('The component `'.$str.'` contains invalid characters.'),
+ default => $str,
+ };
+ }
+
+ public function withUserInfo(
+ Stringable|string|null $user,
+ #[SensitiveParameter] Stringable|string|null $password = null
+ ): UriInterface {
+ $user = Encoder::encodeUser($this->filterString($user));
+ $pass = Encoder::encodePassword($this->filterString($password));
+ $userInfo = ('' !== $user) ? $this->formatUserInfo($user, $pass) : null;
+
+ return match ($userInfo) {
+ $this->userInfo => $this,
+ default => new self(
+ $this->scheme,
+ $user,
+ $pass,
+ $this->host,
+ $this->port,
+ $this->path,
+ $this->query,
+ $this->fragment,
+ ),
+ };
+ }
+
+ public function withHost(Stringable|string|null $host): UriInterface
+ {
+ $host = $this->formatHost($this->filterString($host));
+
+ return match ($host) {
+ $this->host => $this,
+ default => new self(
+ $this->scheme,
+ $this->user,
+ $this->pass,
+ $host,
+ $this->port,
+ $this->path,
+ $this->query,
+ $this->fragment,
+ ),
+ };
+ }
+
+ public function withPort(int|null $port): UriInterface
+ {
+ $port = $this->formatPort($port);
+
+ return match ($port) {
+ $this->port => $this,
+ default => new self(
+ $this->scheme,
+ $this->user,
+ $this->pass,
+ $this->host,
+ $port,
+ $this->path,
+ $this->query,
+ $this->fragment,
+ ),
+ };
+ }
+
+ public function withPath(Stringable|string $path): UriInterface
+ {
+ $path = $this->formatPath(
+ $this->filterString($path) ?? throw new SyntaxError('The path component cannot be null.')
+ );
+
+ return match ($path) {
+ $this->path => $this,
+ default => new self(
+ $this->scheme,
+ $this->user,
+ $this->pass,
+ $this->host,
+ $this->port,
+ $path,
+ $this->query,
+ $this->fragment,
+ ),
+ };
+ }
+
+ public function withQuery(Stringable|string|null $query): UriInterface
+ {
+ $query = Encoder::encodeQueryOrFragment($this->filterString($query));
+
+ return match ($query) {
+ $this->query => $this,
+ default => new self(
+ $this->scheme,
+ $this->user,
+ $this->pass,
+ $this->host,
+ $this->port,
+ $this->path,
+ $query,
+ $this->fragment,
+ ),
+ };
+ }
+
+ public function withFragment(Stringable|string|null $fragment): UriInterface
+ {
+ $fragment = Encoder::encodeQueryOrFragment($this->filterString($fragment));
+
+ return match ($fragment) {
+ $this->fragment => $this,
+ default => new self(
+ $this->scheme,
+ $this->user,
+ $this->pass,
+ $this->host,
+ $this->port,
+ $this->path,
+ $this->query,
+ $fragment,
+ ),
+ };
+ }
+
+ /**
+ * DEPRECATION WARNING! This method will be removed in the next major point release.
+ *
+ * @deprecated Since version 7.5.0
+ * @codeCoverageIgnore
+ * @see Uri::toComponents()
+ *
+ * @return ComponentMap
+ */
+ #[Deprecated(message:'use League\Uri\Uri::toComponents() instead', since:'league/uri:7.5.0')]
+ public function getComponents(): array
+ {
+ return $this->toComponents();
+ }
+
+ /**
+ * DEPRECATION WARNING! This method will be removed in the next major point release.
+ *
+ * @deprecated Since version 7.0.0
+ * @codeCoverageIgnore
+ * @see Uri::new()
+ */
+ #[Deprecated(message:'use League\Uri\Uri::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 Uri::fromComponents()
+ *
+ * @param InputComponentMap $components a hash representation of the URI similar to PHP parse_url function result
+ */
+ #[Deprecated(message:'use League\Uri\Uri::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.
+ *
+ * @param resource|null $context
+ *
+ * @throws MissingFeature If ext/fileinfo is not installed
+ * @throws SyntaxError If the file does not exist or is not readable
+ * @see Uri::fromFileContents()
+ *
+ * @deprecated Since version 7.0.0
+ * @codeCoverageIgnore
+ */
+ #[Deprecated(message:'use League\Uri\Uri::fromDataPath() instead', since:'league/uri:7.0.0')]
+ public static function createFromDataPath(string $path, $context = null): self
+ {
+ return self::fromFileContents($path, $context);
+ }
+
+ /**
+ * DEPRECATION WARNING! This method will be removed in the next major point release.
+ *
+ * @deprecated Since version 7.0.0
+ * @codeCoverageIgnore
+ * @see Uri::fromBaseUri()
+ *
+ * Creates a new instance from a URI and a Base URI.
+ *
+ * The returned URI must be absolute.
+ */
+ #[Deprecated(message:'use League\Uri\Uri::fromBaseUri() instead', since:'league/uri:7.0.0')]
+ public static function createFromBaseUri(
+ Stringable|UriInterface|String $uri,
+ Stringable|UriInterface|String|null $baseUri = null
+ ): UriInterface {
+ return self::fromBaseUri($uri, $baseUri);
+ }
+
+ /**
+ * DEPRECATION WARNING! This method will be removed in the next major point release.
+ *
+ * @deprecated Since version 7.0.0
+ * @codeCoverageIgnore
+ * @see Uri::fromUnixPath()
+ *
+ * Create a new instance from a Unix path string.
+ */
+ #[Deprecated(message:'use League\Uri\Uri::fromUnixPath() instead', since:'league/uri:7.0.0')]
+ public static function createFromUnixPath(string $uri = ''): self
+ {
+ return self::fromUnixPath($uri);
+ }
+
+ /**
+ * DEPRECATION WARNING! This method will be removed in the next major point release.
+ *
+ * @deprecated Since version 7.0.0
+ * @codeCoverageIgnore
+ * @see Uri::fromWindowsPath()
+ *
+ * Create a new instance from a local Windows path string.
+ */
+ #[Deprecated(message:'use League\Uri\Uri::fromWindowsPath() instead', since:'league/uri:7.0.0')]
+ public static function createFromWindowsPath(string $uri = ''): self
+ {
+ return self::fromWindowsPath($uri);
+ }
+
+ /**
+ * DEPRECATION WARNING! This method will be removed in the next major point release.
+ *
+ * @deprecated Since version 7.0.0
+ * @codeCoverageIgnore
+ * @see Uri::new()
+ *
+ * Create a new instance from a URI object.
+ */
+ #[Deprecated(message:'use League\Uri\Uri::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 Uri::fromServer()
+ *
+ * Create a new instance from the environment.
+ */
+ #[Deprecated(message:'use League\Uri\Uri::fromServer() instead', since:'league/uri:7.0.0')]
+ public static function createFromServer(array $server): self
+ {
+ return self::fromServer($server);
+ }
+}
diff --git a/vendor/league/uri/UriInfo.php b/vendor/league/uri/UriInfo.php
new file mode 100644
index 000000000..c782926b7
--- /dev/null
+++ b/vendor/league/uri/UriInfo.php
@@ -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);
+ }
+}
diff --git a/vendor/league/uri/UriResolver.php b/vendor/league/uri/UriResolver.php
new file mode 100644
index 000000000..a50966ad8
--- /dev/null
+++ b/vendor/league/uri/UriResolver.php
@@ -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();
+ }
+}
diff --git a/vendor/league/uri/UriTemplate.php b/vendor/league/uri/UriTemplate.php
new file mode 100644
index 000000000..882a7b51a
--- /dev/null
+++ b/vendor/league/uri/UriTemplate.php
@@ -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)
+ ));
+ }
+}
diff --git a/vendor/league/uri/UriTemplate/Expression.php b/vendor/league/uri/UriTemplate/Expression.php
new file mode 100644
index 000000000..d9d5b9054
--- /dev/null
+++ b/vendor/league/uri/UriTemplate/Expression.php
@@ -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,
+ };
+ }
+}
diff --git a/vendor/league/uri/UriTemplate/Operator.php b/vendor/league/uri/UriTemplate/Operator.php
new file mode 100644
index 000000000..0316b80d8
--- /dev/null
+++ b/vendor/league/uri/UriTemplate/Operator.php
@@ -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];
+ }
+}
diff --git a/vendor/league/uri/UriTemplate/Template.php b/vendor/league/uri/UriTemplate/Template.php
new file mode 100644
index 000000000..2727c9480
--- /dev/null
+++ b/vendor/league/uri/UriTemplate/Template.php
@@ -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);
+ }
+}
diff --git a/vendor/league/uri/UriTemplate/TemplateCanNotBeExpanded.php b/vendor/league/uri/UriTemplate/TemplateCanNotBeExpanded.php
new file mode 100644
index 000000000..70e1601d1
--- /dev/null
+++ b/vendor/league/uri/UriTemplate/TemplateCanNotBeExpanded.php
@@ -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);
+ }
+}
diff --git a/vendor/league/uri/UriTemplate/VarSpecifier.php b/vendor/league/uri/UriTemplate/VarSpecifier.php
new file mode 100644
index 000000000..1730d3ca1
--- /dev/null
+++ b/vendor/league/uri/UriTemplate/VarSpecifier.php
@@ -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 => '',
+ };
+ }
+}
diff --git a/vendor/league/uri/UriTemplate/VariableBag.php b/vendor/league/uri/UriTemplate/VariableBag.php
new file mode 100644
index 000000000..cf6d08f30
--- /dev/null
+++ b/vendor/league/uri/UriTemplate/VariableBag.php
@@ -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));
+ }
+}
diff --git a/vendor/league/uri/composer.json b/vendor/league/uri/composer.json
new file mode 100644
index 000000000..942c73e70
--- /dev/null
+++ b/vendor/league/uri/composer.json
@@ -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
+ }
+}