Files
itflow/libs/vendor/guzzlehttp/psr7/docs/uri-helpers.md
2026-08-02 01:26:40 -04:00

20 KiB
Raw Blame History

URI Helpers

This page covers this package's Psr\Http\Message\UriInterface implementation and URI helper classes for classifying, composing, resolving, normalizing, comparing, and safely modifying URIs.

Aside from the standard Psr\Http\Message\UriInterface implementation provided by the GuzzleHttp\Psr7\Uri class, this library also provides additional static methods for working with URIs.

URI Types

An instance of Psr\Http\Message\UriInterface can either be an absolute URI or a relative reference. An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI, the base URI. Relative references can be divided into several forms according to RFC 3986 Section 4.2:

  • network-path references, e.g. //example.com/path
  • absolute-path references, e.g. /path
  • relative-path references, e.g. subpath

The following methods can be used to identify the type of the URI.

GuzzleHttp\Psr7\Uri::isAbsolute

public static function isAbsolute(UriInterface $uri): bool

Whether the URI is absolute, i.e. it has a scheme.

GuzzleHttp\Psr7\Uri::isNetworkPathReference

public static function isNetworkPathReference(UriInterface $uri): bool

Whether the URI is a network-path reference. A relative reference that begins with two slash characters is termed a network-path reference.

GuzzleHttp\Psr7\Uri::isAbsolutePathReference

public static function isAbsolutePathReference(UriInterface $uri): bool

Whether the URI is an absolute-path reference. A relative reference that begins with a single slash character is termed an absolute-path reference.

GuzzleHttp\Psr7\Uri::isRelativePathReference

public static function isRelativePathReference(UriInterface $uri): bool

Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is termed a relative-path reference.

GuzzleHttp\Psr7\Uri::isSameDocumentReference

public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool

Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its fragment component, identical to the base URI. When no base URI is given, only an empty URI reference (apart from its fragment) is considered a same-document reference.

URI Syntax Validation

GuzzleHttp\Psr7\Rfc3986 provides static methods for validating and canonicalizing individual URI components against the grammar defined by RFC 3986. They operate on raw component strings rather than on Psr\Http\Message\UriInterface instances.

GuzzleHttp\Psr7\Rfc3986::isValidScheme

public static function isValidScheme(string $scheme): bool

Whether the string is a valid URI scheme. Per RFC 3986 Section 3.1, a scheme must start with a letter, followed by any number of letters, digits, +, -, or .. The empty string is also accepted, since a URI reference may omit the scheme.

GuzzleHttp\Psr7\Rfc3986::isValidHost

public static function isValidHost(string $host): bool

Whether the string is a valid URI host. Per RFC 3986 Section 3.2.2, the host is an IP-literal, IPv4 address, or registered name. An empty host is accepted, since the authority, and thus the host, may be empty. Bracketed values are validated as IPv6 or IPvFuture literals; any other value is rejected if it contains control characters, whitespace, an authority or path delimiter (/, ?, #, @, \), or an embedded colon denoting a port. Percent-encoding is validated the same way: malformed sequences (a % not followed by two hex digits) and percent-encoded octets that decode to one of the rejected bytes, to a bracket ([, ]), or to % itself are invalid, while all other percent-encoded octets are accepted. Rejecting these percent-encoded octets is a deliberate guzzle host policy, stricter than the RFC 3986 reg-name grammar, which permits any well-formed pct-encoded octet; it matches the stricter policy used throughout the library. RFC 6874 IPv6 zone identifiers (for example [fe80::1%25eth0]) are not supported.

Registered names are otherwise intentionally permissive: single-label hosts such as localhost, underscores, sub-delims, and raw or percent-encoded non-ASCII (IDN) data are accepted and preserved as given, with no punycode conversion. IDNA is treated as a client concern. Consumers that need DNS IDNs must perform the conversion themselves, for example via Guzzle's idn_conversion request option.

GuzzleHttp\Psr7\Rfc3986::isValidPort

public static function isValidPort(string $port): bool

Whether the string is a valid port number. RFC 3986 defines the port as *DIGIT, which also permits an empty port and has no upper bound; this applies the stricter policy used throughout the library instead, accepting a non-empty run of digits (leading zeros are accepted and normalized) that resolves to a value in the range 0-65535.

GuzzleHttp\Psr7\Rfc3986::canonicalizeIpv6

public static function canonicalizeIpv6(string $address): string

Returns the RFC 5952 canonical form of a valid IPv6 address. The address must be a valid textual IPv6 address without brackets and without a zone identifier, such as the inside of an IP-literal accepted by isValidHost(). Canonicalization lowercases the hexadecimal fields, suppresses leading zeros, and collapses the longest run of two or more zero fields (the leftmost on a tie) with ::. Embedded dotted-decimal notation follows the rendering policy of BIND-derived inet_ntop() implementations and curl: exactly the IPv4-mapped (::ffff:0:0/96) and deprecated IPv4-compatible (::/96) layouts use it, while other embedded-IPv4 forms, including translated (NAT64) well-known prefixes such as 64:ff9b::/96 (RFC 6052), serialize in pure hexadecimal fields. An \InvalidArgumentException is thrown if the address cannot be parsed.

Validation is strict and platform-independent: the address is checked against the RFC 3986 IPv6address grammar with PHP's FILTER_VALIDATE_IP filter and parsed in pure PHP, so spellings that only some platform parsers accept, such as the zero-padded dotted octets in ::ffff:192.168.001.001, are rejected everywhere.

Emitting dotted-decimal notation for these two selected layouts is the BIND-derived inet_ntop() and curl compatibility policy used here. RFC 5952 Section 5 permits mixed notation for recognizable embedded-IPv4 prefixes but does not limit that category to these layouts; RFC 6052, for example, defines 64:ff9b::/96 as a Well-Known Prefix, which this implementation renders in pure hexadecimal. The WHATWG URL Standard always emits pure hexadecimal fields.

URI Components

Additional methods to work with URI components.

GuzzleHttp\Psr7\Uri::isDefaultPort

public static function isDefaultPort(UriInterface $uri): bool

Whether the URI has the default port of the current scheme. Psr\Http\Message\UriInterface::getPort may return null or the standard port. This method can be used independently of the implementation.

GuzzleHttp\Psr7\Uri::composeComponents

public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment): string

Composes a URI reference string from its various components according to RFC 3986 Section 5.3. Usually this method does not need to be called manually but instead is used indirectly via Psr\Http\Message\UriInterface::__toString.

PSR-7 UriInterface treats an empty component the same as a missing component as getQuery(), getFragment() etc. always return a string. This explains the slight difference to RFC 3986 Section 5.3.

Another adjustment is that the authority separator is added even when the authority is missing/empty for the "file" scheme. This is because PHP stream functions like file_get_contents only work with file:///myfile but not with file:/myfile although they are equivalent according to RFC 3986. But file:/// is the more common syntax for the file scheme anyway (Chrome for example redirects to that format). The separator is omitted when such a URI has a rootless or empty path: adding it would turn the first path segment into the authority of the composed URI, or compose the string file://, which cannot be parsed back into a URI.

GuzzleHttp\Psr7\Uri::fromParts

public static function fromParts(array $parts): UriInterface

Creates a URI from a hash of parse_url components.

GuzzleHttp\Psr7\Uri::withQueryValue

public static function withQueryValue(UriInterface $uri, string $key, ?string $value): UriInterface

Creates a new URI with a specific query string value. Any existing query string values that exactly match the provided key are removed and replaced with the given key value pair. A value of null will set the query string key without a value, e.g. "key" instead of "key=value".

GuzzleHttp\Psr7\Uri::withQueryValues

public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface

Creates a new URI with multiple query string values. It has the same behavior as withQueryValue() but for an associative array of key => value.

GuzzleHttp\Psr7\Uri::withoutQueryValue

public static function withoutQueryValue(UriInterface $uri, string $key): UriInterface

Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the provided key are removed.

Cross-Origin Detection

GuzzleHttp\Psr7\UriComparator provides methods to determine if a modified URI should be considered cross-origin.

GuzzleHttp\Psr7\UriComparator::isCrossOrigin

public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool

Determines if a modified URI should be considered cross-origin with respect to an original URI.

Two URIs are cross-origin when their scheme, host, or effective port differ. Host comparison is case-insensitive, and bracketed IPv6 literals are canonicalized to their RFC 5952 form from any PSR-7 implementation before comparison, so equivalent spellings of the same address are same-origin. IPvFuture literals and bracketed values that cannot be parsed as an IPv6 address, such as those carrying zone identifiers, still compare as case-insensitive text. Missing ports use the default port for http, https, ws, or wss. Other schemes do not receive implicit default ports.

This helper only compares URI origins. It does not implement redirect handling or credential policy.

Reference Resolution

GuzzleHttp\Psr7\UriResolver provides methods to resolve a URI reference in the context of a base URI according to RFC 3986 Section 5. This is also what web browsers do when resolving a link in a document based on the current request URI.

GuzzleHttp\Psr7\UriResolver::resolve

public static function resolve(UriInterface $base, UriInterface $rel): UriInterface

Converts the relative URI into a new URI that is resolved against the base URI.

GuzzleHttp\Psr7\UriResolver::removeDotSegments

public static function removeDotSegments(string $path): string

Removes dot segments from a path and returns the new path according to RFC 3986 Section 5.2.4.

Excess .. segments above the root of an absolute path are dropped without consuming the root, so the result can begin with // (e.g. /..//a becomes //a). Such a path is not valid for a URI without an authority (RFC 3986 Section 3.3); resolve() and UriNormalizer::normalize() serialize it with a /. prefix in that case, like the WHATWG URL Standard.

GuzzleHttp\Psr7\UriResolver::relativize

public static function relativize(UriInterface $base, UriInterface $target): UriInterface

Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():

(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))

One use case is to use the current request URI as the base URI and then generate relative links in your documents to reduce the document size or offer self-contained downloadable document archives.

$base = new Uri('http://example.com/a/b/');
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.
echo UriResolver::relativize($base, new Uri('http://example.com'));         // prints '//example.com'.

This method also accepts a target that is already relative and will try to relativize it further. Only a relative-path reference will be returned as-is.

echo UriResolver::relativize($base, new Uri('/a/b/c'));  // prints 'c' as well

Normalization and Comparison

GuzzleHttp\Psr7\UriNormalizer provides methods to normalize and compare URIs according to RFC 3986 Section 6.

GuzzleHttp\Psr7\UriNormalizer::normalize

public static function normalize(UriInterface $uri, int $flags = self::PRESERVING_NORMALIZATIONS): UriInterface

Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface. This method adds additional normalizations that can be configured with the $flags parameter, which is a bitmask of normalizations to apply.

PSR-7 UriInterface cannot distinguish between an empty component and a missing component as getQuery(), getFragment() etc. always return a string. This means the URIs /?# and / are treated equivalent which is not necessarily true according to RFC 3986. But that difference is highly uncommon in reality. So this potential normalization is implied in PSR-7 as well.

The following normalizations are available:

  • UriNormalizer::PRESERVING_NORMALIZATIONS

    Default normalizations which only include the ones that preserve semantics.

  • UriNormalizer::CAPITALIZE_PERCENT_ENCODING

    All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized. This applies to the userinfo, host, path, query, and fragment components. Bracketed IP-literal hosts are skipped as a legacy tolerance for nonstandard values other implementations may carry; zone-identifier text was briefly valid URI syntax under RFC 6874, which RFC 9844 obsoleted and reverted. The userinfo and host are only rewritten when the value returned by the implementation matches the normalized form, and a userinfo with an empty user segment is never rewritten. No percent-encoding normalization is applied to a component that contains malformed percent syntax, such as a % not followed by two hexadecimal digits.

    Example: http://example.org/a%c2%b1bhttp://example.org/a%C2%B1b

  • UriNormalizer::DECODE_UNRESERVED_CHARACTERS

    Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of ALPHA (%41%5A and %61%7A), DIGIT (%30%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved characters by URI normalizers. This applies to the userinfo, host, path, query, and fragment components. Since the host is case-insensitive and PSR-7 requires it to be lowercase, octets decoded in the host are lowercased (e.g., "%41" becomes "a"). Bracketed IP-literal hosts are skipped as a legacy tolerance for nonstandard values other implementations may carry; zone-identifier text was briefly valid URI syntax under RFC 6874, which RFC 9844 obsoleted and reverted. The userinfo and host are only rewritten when the value returned by the implementation matches the normalized form, and a userinfo with an empty user segment is never rewritten. No percent-encoding normalization is applied to a component that contains malformed percent syntax, such as a % not followed by two hexadecimal digits.

    Example: http://example.org/%7Eusern%61me/http://example.org/~username/

  • UriNormalizer::CONVERT_EMPTY_PATH

    Converts the empty path to "/" for http and https URIs.

    Example: http://example.orghttp://example.org/

  • UriNormalizer::REMOVE_DEFAULT_HOST

    Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host "localhost". All of file:/myfile, file:///myfile, and file://localhost/myfile are equivalent according to RFC 3986.

    Example: file://localhost/myfilefile:///myfile

  • UriNormalizer::REMOVE_DEFAULT_PORT

    Removes the default port of the given URI scheme from the URI.

    Example: http://example.org:80/http://example.org/

  • UriNormalizer::REMOVE_DOT_SEGMENTS

    Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would change the semantics of the URI reference.

    Example: http://example.org/../a/b/../c/./d.htmlhttp://example.org/a/c/d.html

  • UriNormalizer::REMOVE_DUPLICATE_SLASHES

    Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization may change the semantics. Encoded slashes (%2F) are not removed.

    Example: http://example.org//foo///bar.htmlhttp://example.org/foo/bar.html

  • UriNormalizer::SORT_QUERY_PARAMETERS

    Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be significant (this is not defined by the standard). So this normalization is not safe and may change the semantics of the URI.

    Example: ?lang=en&article=fred?article=fred&lang=en

  • UriNormalizer::CANONICALIZE_IPV6_HOST

    Canonicalizes IPv6 hosts to their RFC 5952 form. IPv6 addresses allow leading zeros and multiple placements of the :: elision, so the same address has many textual spellings. The canonical form is required for IPv6 literals in URIs by RFC 5952 Section 6 and never changes what the URI refers to. Native Uri instances already guarantee canonical output; for other implementations, the canonical host is requested through withHost() and the result is kept only when the returned getHost() exactly matches the requested spelling, otherwise this step leaves the URI unchanged while other selected normalizations still apply, and setter exceptions propagate.

    Example: http://[::0:0a]/http://[::a]/

GuzzleHttp\Psr7\UriNormalizer::isEquivalent

public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, int $normalizations = self::PRESERVING_NORMALIZATIONS): bool

Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also accepts relative URI references and returns true when they are equivalent. This of course assumes they will be resolved against the same base URI. If this is not the case, determination of equivalence or difference of relative references does not mean anything.