Update imapengine dependancies too

This commit is contained in:
johnnyq
2026-08-02 01:26:40 -04:00
parent cf4446f405
commit 3e532fc792
185 changed files with 9018 additions and 4442 deletions

View File

@@ -0,0 +1,30 @@
# Diagnostic Values
## `GuzzleHttp\Psr7\DiagnosticValue::escape`
`public static function escape(string $value): string`
Escapes C0, DEL, and C1 controls as uppercase `\xNN` sequences.
ASCII bytes from 0x20 through 0x7E and valid UTF-8 characters outside those
control ranges remain unchanged. If the input is malformed UTF-8 or PCRE cannot
process it, every byte outside printable ASCII is escaped. Valid C1 characters
are rendered as `\xNN` using their Unicode code points. During bytewise
fallback, each original byte outside printable ASCII is rendered in the same
form. The result is diagnostic text, not a reversible encoding.
This does not encode values for HTML, JSON, shells, terminals, URLs, or protocol
fields.
Use this helper when including an untrusted value in diagnostic text:
```php
use GuzzleHttp\Psr7\DiagnosticValue;
$value = "bad\nvalue";
$message = sprintf('Invalid value: %s', DiagnosticValue::escape($value));
// Invalid value: bad\x0Avalue
```
Applications must still encode the completed diagnostic for its final output
context, such as HTML or JSON.

View File

@@ -0,0 +1,107 @@
# Header and Query Helpers
This page covers helper methods for parsing structured header values, splitting
list headers, and parsing or building query strings. For basic message header
behavior, see [PSR-7 Messages](psr-7-messages.md).
## `GuzzleHttp\Psr7\Header::parse`
`public static function parse(string|array $header): array`
Parses semicolon-separated header parameters into associative arrays, one per
comma-separated header value. Parameters without a value are appended as values
under integer keys.
## `GuzzleHttp\Psr7\Header::splitList`
`public static function splitList(string|string[] $header): string[]`
Splits an HTTP header defined to contain a comma-separated list into each
individual value. Empty values are removed:
```php
$knownEtags = Header::splitList($request->getHeader('if-none-match'));
```
Example headers include `accept`, `cache-control`, and `if-none-match`.
This method must not be used to parse headers that are not defined as a list,
such as `user-agent` or `set-cookie`.
## `GuzzleHttp\Psr7\Query::parse`
`public static function parse(string $str, int|bool $urlEncoding = true): array`
Parse a query string into an associative array.
If multiple values are found for the same key, the value of that key-value pair
becomes an array. This function does not parse nested PHP style arrays into an
associative array. For example, `foo[a]=1&foo[b]=2` will be parsed into
`['foo[a]' => '1', 'foo[b]' => '2']`.
## `GuzzleHttp\Psr7\Query::build`
`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string`
Build a query string from an array of key-value pairs.
This function can use the return value of `parse()` to build a query string.
This function does not modify the provided keys when an array is encountered,
unlike `http_build_query()`.
## `GuzzleHttp\Psr7\Utils::asciiToLower`
`public static function asciiToLower(string $string): string`
Converts ASCII uppercase letters in a string to lowercase.
Unlike `strtolower()`, which honors `LC_CTYPE` before PHP 8.2, the conversion is
locale-independent and leaves every non-ASCII byte unchanged, as HTTP protocol
elements require.
## `GuzzleHttp\Psr7\Utils::asciiToUpper`
`public static function asciiToUpper(string $string): string`
Converts ASCII lowercase letters in a string to uppercase.
Unlike `strtoupper()`, which honors `LC_CTYPE` before PHP 8.2, the conversion is
locale-independent and leaves every non-ASCII byte unchanged, as HTTP protocol
elements require.
## `GuzzleHttp\Psr7\Utils::asciiUcFirst`
`public static function asciiUcFirst(string $string): string`
Converts the first character of a string to uppercase when it is an ASCII
lowercase letter.
Unlike `ucfirst()`, which honors `LC_CTYPE` before PHP 8.2, the conversion is
locale-independent and leaves every non-ASCII byte unchanged, as HTTP protocol
elements require.
## `GuzzleHttp\Psr7\Utils::caselessContains`
`public static function caselessContains(string $haystack, string $needle): bool`
Checks whether the haystack contains the needle, comparing ASCII letters
case-insensitively and without locale sensitivity.
## `GuzzleHttp\Psr7\Utils::caselessEquals`
`public static function caselessEquals(string $left, string $right): bool`
Checks whether two strings are equal, comparing ASCII letters case-insensitively
and without locale sensitivity.
## `GuzzleHttp\Psr7\Utils::caselessRemove`
`public static function caselessRemove(array $keys, array $data): array`
Remove the items given by the keys from the data, case-insensitively.
## Related
- [PSR-7 Messages](psr-7-messages.md)
- [Message Helpers](message-helpers.md)
- [URI Helpers](uri-helpers.md)

View File

@@ -0,0 +1,112 @@
# Message Helpers
This page covers static helper methods for converting, parsing, summarizing,
rewinding, and cloning PSR-7 messages. For conceptual request and response
behavior, start with [PSR-7 Messages](psr-7-messages.md).
## `GuzzleHttp\Psr7\Message::toString`
`public static function toString(MessageInterface $message): string`
Returns the string representation of an HTTP message.
```php
$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
echo GuzzleHttp\Psr7\Message::toString($request);
```
## `GuzzleHttp\Psr7\Message::bodySummary`
`public static function bodySummary(MessageInterface $message, ?int $truncateAt = null): string|null`
Get a short summary of the message body.
Will return `null` if the response is not printable.
Reads seekable bodies from the beginning and restores the original cursor
position before returning. Pass `null` for `$truncateAt` to use the default
summary length.
## `GuzzleHttp\Psr7\Message::rewindBody`
`public static function rewindBody(MessageInterface $message): void`
Attempts to rewind a message body and throws an exception on failure.
The body of the message will only be rewound if a call to `tell()` returns a
value other than `0`.
## `GuzzleHttp\Psr7\Message::parseMessage`
`public static function parseMessage(string $message): array`
Parses an HTTP message into an associative array.
The array contains the `start-line` key containing the start line of the
message, `headers` key containing an associative array of header array values,
and a `body` key containing the body of the message.
## `GuzzleHttp\Psr7\Message::parseRequestUri`
`public static function parseRequestUri(string $path, array $headers): string`
Constructs a URI for an HTTP request message.
The URI is composed from the start-line path and the `Host` header, using
`https` when the host's port is `443` and `http` otherwise. Without a `Host`
header, only the path is returned, with extra leading slashes collapsed so an
origin-form target cannot be parsed as a network-path reference with its own
authority. An `InvalidArgumentException` is thrown when the `Host` header is
invalid.
## `GuzzleHttp\Psr7\Message::parseRequest`
`public static function parseRequest(string $message): RequestInterface`
Parses a request message string into a request object.
The request-target must be in origin form, absolute form (without a userinfo
component), authority form (`CONNECT`), or asterisk form (`OPTIONS`), and any
`Host` header must be a single valid value; otherwise an
`InvalidArgumentException` is thrown. Non-origin-form targets are preserved on
the returned request via `withRequestTarget()`.
## `GuzzleHttp\Psr7\Message::parseResponse`
`public static function parseResponse(string $message): ResponseInterface`
Parses a response message string into a response object.
## `GuzzleHttp\Psr7\Utils::modifyRequest`
`public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface`
Clone and modify a request with the given changes.
This method is useful for reducing the number of clones needed to mutate a
message.
The changes can be one of:
- method: (string) Changes the HTTP method.
- set_headers: (array) Sets the given headers. Values must be strings or
non-empty arrays of strings.
- remove_headers: (array) Remove the given headers. Values may be strings or
integers.
- body: (mixed) Sets the given body. Present non-null values are converted with
`GuzzleHttp\Psr7\Utils::streamFor()`, including resources, streams, iterators,
callable arrays, closures, invokable objects, and stringable objects. String
inputs remain literal bodies.
- uri: (UriInterface) Set the URI. When the URI contains a host, the Host header
is updated from it, and combining this with an explicit Host entry in
set_headers throws an InvalidArgumentException. Apply an intentional Host
override separately with withHeader() afterwards.
- query: (string) Set the query string value of the URI.
- version: (string) Set the protocol version.
## Related
- [PSR-7 Messages](psr-7-messages.md)
- [Header and Query Helpers](header-and-query-helpers.md)
- [Stream Helpers](stream-helpers.md)
- [URI and MIME Helpers](uri-and-mime-helpers.md)

View File

@@ -0,0 +1,33 @@
# PSR-17 Factories
This page explains `GuzzleHttp\Psr7\HttpFactory`, the PSR-17 factory implementation provided by this package. Use it when code expects PSR-17 factory interfaces and you want factories that create Guzzle PSR-7 messages, streams, uploaded files, and URIs.
## `GuzzleHttp\Psr7\HttpFactory`
`GuzzleHttp\Psr7\HttpFactory` implements all PSR-17 factory interfaces: `RequestFactoryInterface`, `ResponseFactoryInterface`, `ServerRequestFactoryInterface`, `StreamFactoryInterface`, `UploadedFileFactoryInterface`, and `UriFactoryInterface`.
```php
use GuzzleHttp\Psr7\HttpFactory;
$factory = new HttpFactory();
$request = $factory->createRequest('GET', 'https://example.com');
$response = $factory->createResponse(200);
$serverRequest = $factory->createServerRequest('POST', '/submit', ['REMOTE_ADDR' => '192.0.2.1']);
$stream = $factory->createStream('body');
$uri = $factory->createUri('https://example.com/path');
```
It also creates streams from files and resources, and uploaded files from streams.
```php
$stream = $factory->createStreamFromFile('/path/to/file.txt', 'r');
$upload = $factory->createUploadedFile($stream, $stream->getSize(), UPLOAD_ERR_OK, 'file.txt', 'text/plain');
```
## Related
- [PSR-7 Messages](psr-7-messages.md)
- [Streams and Decorators](streams-and-decorators.md)
- [URI Helpers](uri-helpers.md)
- [Message Helpers](message-helpers.md)

View File

@@ -0,0 +1,402 @@
# PSR-7 Messages
This page covers the PSR-7 message objects provided by this package: requests, responses, server requests, uploaded files, and the message-specific header, URI, and body APIs. Use these objects when you need HTTP messages that can move between Guzzle, PSR-18 clients, PSR-15 middleware, and other PSR-7 compatible libraries.
HTTP requests and responses are both messages. A message has a start line, headers, and an optional body stream. Message and URI objects are immutable; methods named `with*()` return changed copies. Body streams are mutable handles, so reads and writes can change their cursor or contents. For body details, see [Streams and Decorators](streams-and-decorators.md). For URI helpers, see [URI Helpers](uri-helpers.md).
## Creating Requests
You can create a request with `GuzzleHttp\Psr7\Request`.
```php
use GuzzleHttp\Psr7\Request;
$request = new Request('GET', 'https://example.com/users/123');
// You can provide optional headers and a body.
$headers = ['Accept' => 'application/json'];
$body = 'request body';
$request = new Request('PUT', 'https://example.com/users/123', $headers, $body);
```
## Creating Responses
You can create a response with `GuzzleHttp\Psr7\Response`.
```php
use GuzzleHttp\Psr7\Response;
// The constructor requires no arguments.
$response = new Response();
echo $response->getStatusCode();
// 200
echo $response->getProtocolVersion();
// 1.1
// You can provide a status, headers, body, and protocol version.
$response = new Response(200, ['Content-Type' => 'application/json'], '{"ok":true}', '1.1');
```
## Creating Server Requests
Server requests represent incoming HTTP requests on the server side. They include the normal request method, URI, headers, and body, plus server parameters, cookies, query parameters, parsed body data, attributes, and uploaded files.
```php
use GuzzleHttp\Psr7\ServerRequest;
$request = new ServerRequest('POST', 'https://example.com/form', [], 'name=Guzzle', '1.1', [
'REMOTE_ADDR' => '192.0.2.1',
]);
$request = $request
->withCookieParams(['session' => 'abc'])
->withQueryParams(['page' => '1'])
->withParsedBody(['name' => 'Guzzle'])
->withAttribute('route', 'profile');
echo $request->getServerParams()['REMOTE_ADDR'];
echo $request->getCookieParams()['session'];
echo $request->getQueryParams()['page'];
echo $request->getParsedBody()['name'];
echo $request->getAttribute('route');
```
Use `ServerRequest::fromGlobals()` to create a server request from PHP superglobals. It reads `$_SERVER`, `$_GET`, `$_POST`, `$_COOKIE`, and `$_FILES`, and attempts to include request headers when available.
```php
use GuzzleHttp\Psr7\ServerRequest;
$request = ServerRequest::fromGlobals();
```
Use `ServerRequest::getUriFromGlobals()` when you only need the URI derived from `$_SERVER`.
```php
use GuzzleHttp\Psr7\ServerRequest;
$uri = ServerRequest::getUriFromGlobals();
```
For URI construction and normalization helpers, see [URI Helpers](uri-helpers.md).
## Requests
```php
use GuzzleHttp\Psr7\Request;
$request = new Request('GET', 'https://example.com/users/123', [
'Accept' => 'application/json',
]);
echo $request->getMethod();
echo $request->getUri();
```
PSR-7 messages are immutable. Methods such as `withHeader()` and `withUri()` return a modified copy.
```php
$jsonRequest = $request->withHeader('Accept', 'application/json');
```
## Responses
```php
use GuzzleHttp\Psr7\Response;
$response = new Response(200, ['Content-Type' => 'application/json'], '{"ok":true}');
echo $response->getStatusCode();
echo $response->getHeaderLine('Content-Type');
echo $response->getBody();
```
## URIs
```php
use GuzzleHttp\Psr7\Uri;
$uri = new Uri('https://example.com/users?active=1');
echo $uri->getHost();
echo $uri->getQuery();
```
For URI-specific helper methods, see [URI Helpers](uri-helpers.md).
## Headers
Both request and response messages contain HTTP headers.
### Accessing Headers
You can check if a request or response has a specific header using `hasHeader()`.
```php
use GuzzleHttp\Psr7\Request;
$request = new Request('GET', '/', ['X-Foo' => 'bar']);
if ($request->hasHeader('X-Foo')) {
echo 'It is there';
}
```
Retrieve all header values as an array of strings with `getHeader()`.
```php
$request->getHeader('X-Foo');
// ['bar']
// Missing headers return an empty array.
$request->getHeader('X-Bar');
// []
```
Iterate over the headers of a message with `getHeaders()`.
```php
foreach ($request->getHeaders() as $name => $values) {
echo $name . ': ' . implode(', ', $values) . "\r\n";
}
```
### Complex Headers
Some headers contain additional key-value pair information. For example, `Link` headers contain a link and additional parameters:
```http
<https://example.com/front.jpeg>; rel="front"; type="image/jpeg"
```
Use `GuzzleHttp\Psr7\Header::parse()` to parse these headers.
```php
use GuzzleHttp\Psr7\Header;
use GuzzleHttp\Psr7\Request;
$request = new Request('GET', '/', [
'Link' => '<https://example.com/front.jpeg>; rel="front"; type="image/jpeg"',
]);
$parsed = Header::parse($request->getHeader('Link'));
var_export($parsed);
```
This outputs:
```php
array (
0 =>
array (
0 => '<https://example.com/front.jpeg>',
'rel' => 'front',
'type' => 'image/jpeg',
),
)
```
The result contains key-value pairs. Header values that have no key are indexed numerically, while header parts that form a key-value pair are added with their parameter name.
## Body
Request and response bodies are `Psr\Http\Message\StreamInterface` instances. Streams are used for both uploading data and downloading data.
```php
use GuzzleHttp\Psr7\Response;
$response = new Response(200, [], 'response body');
echo $response->getBody();
// response body
```
The body can be cast to a string, or you can read bytes from the stream as needed.
```php
$body = $response->getBody();
echo $body->read(4);
$body->seek(0);
echo $body->getContents();
```
For more stream creation and decorator examples, see [Streams and Decorators](streams-and-decorators.md).
## Uploaded Files
Uploaded files are represented by `Psr\Http\Message\UploadedFileInterface` instances. This package provides `GuzzleHttp\Psr7\UploadedFile`, which can wrap a local file path, PHP stream resource, or PSR-7 stream.
```php
use GuzzleHttp\Psr7\UploadedFile;
use GuzzleHttp\Psr7\Utils;
$stream = Utils::streamFor('file contents');
$upload = new UploadedFile($stream, $stream->getSize(), UPLOAD_ERR_OK, 'example.txt', 'text/plain');
echo $upload->getClientFilename();
echo $upload->getClientMediaType();
echo $upload->getSize();
```
Call `getStream()` to read the uploaded content, or `moveTo()` to move or copy it to a target path. After `moveTo()` succeeds, `isMoved()` returns `true`, and calls that need the active upload stream will throw.
```php
$body = $upload->getStream();
echo $body->getContents();
$upload->moveTo('/path/to/target.txt');
var_export($upload->isMoved());
// true
```
If the upload error code is not `UPLOAD_ERR_OK`, the object still exposes `getError()`, `getSize()`, `getClientFilename()`, and `getClientMediaType()`, but `getStream()` and `moveTo()` throw because no successful upload content is available.
`ServerRequest::normalizeFiles()` converts a `$_FILES`-style array into a tree of uploaded file instances. It accepts simple file specs, nested PHP `$_FILES` shapes, existing `UploadedFileInterface` instances, and nested arrays of uploaded files.
```php
use GuzzleHttp\Psr7\ServerRequest;
$files = ServerRequest::normalizeFiles([
'avatar' => [
'tmp_name' => '/tmp/php123',
'size' => 1024,
'error' => UPLOAD_ERR_OK,
'name' => 'avatar.png',
'type' => 'image/png',
],
'photos' => [
'tmp_name' => [
'first' => '/tmp/php456',
],
'size' => [
'first' => 2048,
],
'error' => [
'first' => UPLOAD_ERR_OK,
],
'name' => [
'first' => 'photo.jpg',
],
'type' => [
'first' => 'image/jpeg',
],
],
]);
$request = (new ServerRequest('POST', '/upload'))->withUploadedFiles($files);
```
## HTTP Method Casing
HTTP method names are case-sensitive in PSR-7. Requests created explicitly with
`Request`, `ServerRequest`, `withMethod()`, `Message::parseRequest()`, or the
PSR-17 factories preserve the method string as provided. `ServerRequest::fromGlobals()`
normalizes `$_SERVER['REQUEST_METHOD']` to uppercase for compatibility when
hydrating requests from PHP server globals.
## Request Methods
When creating a request, provide the HTTP method you want to perform. You can specify any method, including custom methods that are not part of RFC 9110.
```php
use GuzzleHttp\Psr7\Request;
$request = new Request('MOVE', 'https://example.com/resource');
echo $request->getMethod();
// MOVE
```
## Request URI
The request URI is represented by a `Psr\Http\Message\UriInterface` object. This package provides an implementation through `GuzzleHttp\Psr7\Uri`.
When creating a request, you can provide the URI as a string or as a `UriInterface` instance.
```php
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Uri;
$request = new Request('GET', new Uri('https://example.com/users?id=123'));
```
### Scheme
The [scheme](https://datatracker.ietf.org/doc/html/rfc3986#section-3.1) specifies the protocol. For HTTP requests, this is usually `http` or `https`.
```php
$request = new Request('GET', 'https://example.com');
echo $request->getUri()->getScheme();
// https
```
### Host
The host is accessible from the URI and is also represented by the `Host` header.
```php
$request = new Request('GET', 'https://example.com');
echo $request->getUri()->getHost();
// example.com
echo $request->getHeaderLine('Host');
// example.com
```
### Port
No port is necessary for the default `http` and `https` ports.
```php
$request = new Request('GET', 'https://example.com:8443');
echo $request->getUri()->getPort();
// 8443
```
### Path
The request path is accessible through the URI object.
```php
$request = new Request('GET', 'https://example.com/users/123');
echo $request->getUri()->getPath();
// /users/123
```
Characters that are not allowed in a URI path are percent-encoded according to [RFC 3986 section 3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3).
### Query String
The query string is accessible through the URI object.
```php
$request = new Request('GET', 'https://example.com/?foo=bar');
echo $request->getUri()->getQuery();
// foo=bar
```
Characters that are not allowed in a URI query are percent-encoded according to [RFC 3986 section 3.4](https://datatracker.ietf.org/doc/html/rfc3986#section-3.4).
## Response Status
Responses expose the status code, reason phrase, and protocol version.
```php
use GuzzleHttp\Psr7\Response;
$response = new Response(200, [], 'OK');
echo $response->getStatusCode();
// 200
echo $response->getReasonPhrase();
// OK
echo $response->getProtocolVersion();
// 1.1
```
## Related
- [Streams and Decorators](streams-and-decorators.md)
- [URI Helpers](uri-helpers.md)
- [Message Helpers](message-helpers.md)
- [Header and Query Helpers](header-and-query-helpers.md)
- [PSR-17 Factories](psr-17-factories.md)

View File

@@ -0,0 +1,134 @@
# Stream Helpers
This page covers `GuzzleHttp\Psr7\Utils` helper methods for creating, copying,
hashing, reading, and safely opening PSR-7 streams. For stream implementations
and decorators, see [Streams and Decorators](streams-and-decorators.md).
## `GuzzleHttp\Psr7\Utils::copyToStream`
`public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): int`
Copy the contents of a stream into another stream until the given number of
bytes have been read, returning the number of bytes copied as an `int`. On
32-bit PHP, an unbounded copy larger than `PHP_INT_MAX` bytes cannot be
represented by that return type. 64-bit PHP is not affected.
The destination must accept writes that make positive progress. Streams that
return 0 as a backpressure or drop signal (a `BufferStream` at its high water
mark, or a full `DroppingStream`) will cause this method to throw. For full
copies, use a normal writable stream such as a file or `php://temp` stream.
Throws `GuzzleHttp\Psr7\Exception\TimeoutException` when PHP-style timeout
metadata can be detected after a source read or destination write cannot make
progress.
## `GuzzleHttp\Psr7\Utils::copyToString`
`public static function copyToString(StreamInterface $stream, int $maxLen = -1): string`
Copy the contents of a stream into a string until the given number of bytes have
been read.
Throws `GuzzleHttp\Psr7\Exception\TimeoutException` when PHP-style timeout
metadata can be detected after a stream read cannot make progress.
## `GuzzleHttp\Psr7\Utils::hash`
`public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string`
Calculate a hash of a stream.
This method reads the entire stream to calculate a rolling hash, based on PHP's
`hash_init` functions.
Throws `GuzzleHttp\Psr7\Exception\TimeoutException` when PHP-style timeout
metadata can be detected after a stream read cannot make progress.
## `GuzzleHttp\Psr7\Utils::readLine`
`public static function readLine(StreamInterface $stream, ?int $maxLength = null): string`
Read a line from the stream up to the maximum allowed buffer length.
Throws `GuzzleHttp\Psr7\Exception\TimeoutException` when PHP-style timeout
metadata can be detected after a stream read cannot make progress.
## `GuzzleHttp\Psr7\Utils::streamFor`
`public static function streamFor(resource|string|null|StreamInterface|callable|\Iterator|\Stringable $resource = '', array $options = []): StreamInterface`
Create a new stream based on the input type.
Options are provided as an associative array that can contain the following
keys:
- metadata: Array of custom metadata.
- size: Size of the stream.
This method accepts the following `$resource` types:
- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
- `string`: Creates a stream object that uses the given string as the contents.
- `resource`: Creates a stream object that wraps the given PHP stream resource.
- `Iterator`: If the provided value implements `Iterator`, then a read-only
stream object will be created that wraps the given iterable. Each time the
stream is read from, data from the iterator will fill a buffer and will be
continuously called until the buffer is equal to the requested read size.
Yielded strings, integers, finite floats, booleans, `null`, and stringable
objects are converted to string chunks; non-finite floats and other values
throw `UnexpectedValueException` when the stream is read. Values that
stringify to an empty string are skipped while the iterator advances.
Subsequent read calls will first read from the buffer and then call `next` on
the underlying iterator until it is exhausted.
- `object` with `__toString()`: If the object has the `__toString()` method, the
object will be cast to a string and then a stream will be returned that uses
the string value.
- `NULL`: When `null` is passed, an empty stream object is returned.
- `callable`: When a callable array, closure, or invokable object is passed and
no earlier resource or object rule applies, a read-only stream object will be
created that invokes the given callable. The callable is invoked with the
suggested number of bytes to read. The callable can return fewer or more bytes
than requested, but MUST return a non-empty string to provide data and MUST
return `false` or `null` when there is no more data to return. Any additional
bytes will be buffered and used in subsequent reads. String inputs are always
treated as string bodies, even when they name callable functions.
```php
$stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
$stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));
$generator = function ($bytes) {
for ($i = 0; $i < $bytes; $i++) {
yield ' ';
}
};
$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
```
## `GuzzleHttp\Psr7\Utils::tryFopen`
`public static function tryFopen(string $filename, string $mode): resource`
Safely opens a PHP stream resource using a filename.
When `fopen()` fails, PHP normally raises a warning. This function adds an error
handler that checks for errors and throws an exception instead.
## `GuzzleHttp\Psr7\Utils::tryGetContents`
`public static function tryGetContents(resource $stream): string`
Safely gets the contents of a given stream.
When `stream_get_contents()` fails, PHP normally raises a warning. This function
adds an error handler that checks for errors and throws an exception instead.
Throws `GuzzleHttp\Psr7\Exception\TimeoutException` when PHP-style timeout
metadata can be detected after the stream read cannot make progress.
## Related
- [Streams and Decorators](streams-and-decorators.md)
- [PSR-17 Factories](psr-17-factories.md)
- [Message Helpers](message-helpers.md)

View File

@@ -0,0 +1,387 @@
# Streams and Decorators
PSR-7 request and response bodies are streams. This page covers stream creation, cursor and I/O behavior, built-in stream decorators, and wrapping PSR-7 streams as PHP resources.
Streams allow HTTP messages to represent small strings, large files, generated data, remote resources, and other body sources through a common interface.
The PSR-7 `Psr\Http\Message\StreamInterface` exposes methods that let consumers read, write, seek, and inspect body data without requiring the entire body to be loaded into memory.
Streams expose their capabilities using `isReadable()`, `isWritable()`, and `isSeekable()`. These methods help collaborators determine whether a stream supports the operations they need.
## Creating Streams
Use `GuzzleHttp\Psr7\Utils::streamFor()` to create streams from common PHP values. It accepts strings, resources returned from `fopen()`, objects that implement `__toString()`, iterators, callable arrays, closures, invokable objects, and existing `Psr\Http\Message\StreamInterface` instances.
Strings and `null` are stored in `php://temp` streams. PHP keeps `php://temp` data in memory until the stream exceeds 2 MB, then spills to a temporary file on disk. Non-string scalars such as integers, floats, and booleans are rejected; cast them to strings first.
Callable sources receive a suggested read length, may return fewer or more bytes, and end the stream by returning `false` or `null`. Strings remain literal body contents, even when they name a callable.
```php
use GuzzleHttp\Psr7\Utils;
$stream = Utils::streamFor('string data');
echo $stream;
// string data
echo $stream->read(3);
// str
echo $stream->getContents();
// ing data
var_export($stream->eof());
// true
var_export($stream->tell());
// 11
```
You can create streams from iterators. The iterator can yield any number of bytes per iteration. Any excess bytes returned by the iterator that were not requested by a stream consumer will be buffered until a subsequent read.
```php
use GuzzleHttp\Psr7\Utils;
$generator = function ($bytes) {
for ($i = 0; $i < $bytes; $i++) {
yield '.';
}
};
$stream = Utils::streamFor($generator(1024));
echo $stream->read(3);
// ...
```
## Metadata
Streams expose stream metadata through `getMetadata()`. This method provides the data returned by PHP's [stream_get_meta_data()](https://www.php.net/manual/en/function.stream-get-meta-data.php), and can optionally expose custom metadata.
```php
use GuzzleHttp\Psr7\Utils;
$resource = Utils::tryFopen('/path/to/file', 'r');
$stream = Utils::streamFor($resource);
echo $stream->getMetadata('uri');
// /path/to/file
var_export($stream->isReadable());
// true
var_export($stream->isWritable());
// false
var_export($stream->isSeekable());
// true
```
## AppendStream
`GuzzleHttp\Psr7\AppendStream`
Reads from multiple streams, one after the other.
```php
use GuzzleHttp\Psr7;
$a = Psr7\Utils::streamFor('abc, ');
$b = Psr7\Utils::streamFor('123.');
$composed = new Psr7\AppendStream([$a, $b]);
$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));
echo $composed; // abc, 123. Above all listen to me.
```
## BufferStream
`GuzzleHttp\Psr7\BufferStream`
Provides a buffer stream that can be written to fill a buffer, then read
from it to remove bytes from the buffer.
This stream returns a "hwm" metadata value that tells upstream consumers
what the configured high water mark of the stream is, or the maximum
preferred size of the buffer.
```php
use GuzzleHttp\Psr7;
// When the buffer reaches or exceeds 1024 bytes, it will begin returning 0 to
// writes. This is an indication that writers should slow down.
$buffer = new Psr7\BufferStream(1024);
```
## CachingStream
The CachingStream is used to allow seeking over previously read bytes on
non-seekable streams. This can be useful when transferring a non-seekable
entity body fails due to needing to rewind the stream (for example, resulting
from a redirect). Data that is read from the remote stream will be buffered in
a PHP temp stream so that previously read bytes are cached first in memory,
then on disk.
```php
use GuzzleHttp\Psr7;
$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
$stream = new Psr7\CachingStream($original);
$stream->read(1024);
echo $stream->tell();
// 1024
$stream->seek(0);
echo $stream->tell();
// 0
```
By default the bytes are cached in a `php://temp` stream. You can supply your own
cache target as the second constructor argument, but it is used as a random-access
byte buffer to replay the remote stream, so it must be readable, writable, and
seekable, report an accurate position and size, and store writes losslessly. Lossy
or non-seekable streams such as `BufferStream` and `DroppingStream` are not valid
targets.
## DroppingStream
`GuzzleHttp\Psr7\DroppingStream`
Stream decorator that begins dropping data once the size of the underlying
stream becomes too full.
```php
use GuzzleHttp\Psr7;
// Create an empty stream
$stream = Psr7\Utils::streamFor();
// Start dropping data when the stream has more than 10 bytes
$dropping = new Psr7\DroppingStream($stream, 10);
$dropping->write('01234567890123456789');
echo $stream; // 0123456789
```
## FnStream
`GuzzleHttp\Psr7\FnStream`
Compose stream implementations based on a hash of callables.
Allows for easy testing and extension of a provided stream without needing
to create a concrete class for a simple extension point.
```php
use GuzzleHttp\Psr7;
$stream = Psr7\Utils::streamFor('hi');
$fnStream = Psr7\FnStream::decorate($stream, [
'rewind' => function () use ($stream) {
echo 'About to rewind - ';
$stream->rewind();
echo 'rewound!';
}
]);
$fnStream->rewind();
// Outputs: About to rewind - rewound!
```
## InflateStream
`GuzzleHttp\Psr7\InflateStream`
Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.
This stream decorator converts the provided stream to a PHP stream resource,
appends the zlib.inflate filter, and wraps the filtered resource as a stream.
Closing an `InflateStream` also closes the compressed source stream it decorates; `detach()` leaves the source stream open.
## LazyOpenStream
`GuzzleHttp\Psr7\LazyOpenStream`
Lazily reads from or writes to a file that is opened only after an I/O operation
takes place on the stream.
```php
use GuzzleHttp\Psr7;
$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
// The file has not yet been opened...
echo $stream->read(10);
// The file is opened and read from only when needed.
```
## LimitStream
`GuzzleHttp\Psr7\LimitStream`
LimitStream can be used to read a subset or slice of an existing stream object.
This can be useful for breaking a large file into smaller pieces to be sent in
chunks (e.g. Amazon S3's multipart upload API).
```php
use GuzzleHttp\Psr7;
$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
echo $original->getSize();
// >>> 1048576
// Limit the size of the body to 1024 bytes and start reading from byte 2048
$stream = new Psr7\LimitStream($original, 1024, 2048);
echo $stream->getSize();
// >>> 1024
echo $stream->tell();
// >>> 0
```
## MultipartStream
`GuzzleHttp\Psr7\MultipartStream`
A stream that returns bytes for a streaming multipart or multipart/form-data
body when read.
Each multipart element must contain a `name` and `contents` key. `contents` may
be any non-array value accepted by `GuzzleHttp\Psr7\Utils::streamFor()`,
including closures and invokable objects. Array contents are recursively
expanded into nested form fields.
## NoSeekStream
`GuzzleHttp\Psr7\NoSeekStream`
NoSeekStream wraps a stream and does not allow seeking.
```php
use GuzzleHttp\Psr7;
$original = Psr7\Utils::streamFor('foo');
$noSeek = new Psr7\NoSeekStream($original);
echo $noSeek->read(3);
// foo
var_export($noSeek->isSeekable());
// false
try {
$noSeek->seek(0);
} catch (\RuntimeException $e) {
echo $e->getMessage();
// Cannot seek a NoSeekStream
}
```
## PumpStream
`GuzzleHttp\Psr7\PumpStream`
Provides a read-only stream that pumps data from a PHP callable.
When invoking the provided callable, the PumpStream will pass the suggested
number of bytes to read to the callable. The callable can choose to ignore
this value and return fewer or more bytes than requested. Any extra data
returned by the provided callable is buffered internally until drained using
the `read()` method of the PumpStream. The provided callable MUST return a
non-empty string to provide data, and MUST return false or null when there is
no more data to read. Returning an empty string causes a RuntimeException
because it cannot satisfy a positive-length read.
Userland callables that declare no parameters are tolerated by PHP, but
length-aware callables remain the recommended formal shape.
## Implementing Stream Decorators
Creating a stream decorator is very easy thanks to the
`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
stream. Just `use` the `StreamDecoratorTrait` and implement your custom
methods.
For example, let's say we wanted to call a specific function each time the last
byte is read from a stream. This could be implemented by overriding the
`read()` method.
```php
use Psr\Http\Message\StreamInterface;
use GuzzleHttp\Psr7\StreamDecoratorTrait;
class EofCallbackStream implements StreamInterface
{
use StreamDecoratorTrait;
private $callback;
private $stream;
public function __construct(StreamInterface $stream, callable $cb)
{
$this->stream = $stream;
$this->callback = $cb;
}
public function read(int $length): string
{
$result = $this->stream->read($length);
// Invoke the callback when EOF is hit.
if ($this->eof()) {
($this->callback)();
}
return $result;
}
}
```
This decorator could be added to any existing stream and used like so:
```php
use GuzzleHttp\Psr7;
$original = Psr7\Utils::streamFor('foo');
$eofStream = new EofCallbackStream($original, function () {
echo 'EOF!';
});
$eofStream->read(2);
$eofStream->read(1);
// echoes "EOF!"
$eofStream->seek(0);
$eofStream->read(3);
// echoes "EOF!"
```
## PHP StreamWrapper
You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
PSR-7 stream as a PHP stream resource.
Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
stream from a PSR-7 stream.
```php
use GuzzleHttp\Psr7\StreamWrapper;
$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
$resource = StreamWrapper::getResource($stream);
echo fread($resource, 6); // outputs hello!
```
## Related
- [PSR-7 Messages](psr-7-messages.md)
- [Stream Helpers](stream-helpers.md)
- [PSR-17 Factories](psr-17-factories.md)
- [URI Helpers](uri-helpers.md)

View File

@@ -0,0 +1,61 @@
# URI and MIME Helpers
This page covers small helper methods for redacting URI user info, converting
values into URI objects, and resolving MIME types from filenames or extensions.
For URI resolution, normalization, and comparison helpers, see
[URI Helpers](uri-helpers.md).
## `GuzzleHttp\Psr7\Utils::redactUserInfo`
`public static function redactUserInfo(UriInterface $uri): UriInterface`
Redact the user info part of a URI.
Returns the URI with the whole userinfo component replaced by `***` when one
is present, so neither the username nor the password survives into logs and
diagnostics. A URI without userinfo is returned unchanged.
## `GuzzleHttp\Psr7\Utils::redactUserInfoInString`
`public static function redactUserInfoInString(string $subject, string $uri): string`
Redacts the userinfo of a raw URI string wherever it appears in a subject
string.
The needle is taken verbatim from the raw URI rather than from parsed
components, so credentials that URI normalization would rewrite, such as raw
control bytes or unencoded reserved characters, are still found in text that
embeds the URI exactly as given, for example transport error messages. A URI
without `://` is treated as authority-form: a host and port with optional
userinfo.
A URI that does not parse has no trustworthy authority boundary, so everything
between any scheme and its last `@` is redacted as a safe-side fallback.
## `GuzzleHttp\Psr7\Utils::uriFor`
`public static function uriFor(string|UriInterface $uri): UriInterface`
Returns a `UriInterface` for the given value.
This function accepts a string or `UriInterface` and returns a `UriInterface`
for the given value. If the value is already a `UriInterface`, it is returned
as-is.
## `GuzzleHttp\Psr7\MimeType::fromFilename`
`public static function fromFilename(string $filename): string|null`
Determines the MIME type of a file by looking at its extension.
## `GuzzleHttp\Psr7\MimeType::fromExtension`
`public static function fromExtension(string $extension): string|null`
Maps a file extension to a MIME type.
## Related
- [URI Helpers](uri-helpers.md)
- [Header and Query Helpers](header-and-query-helpers.md)
- [Stream Helpers](stream-helpers.md)

View File

@@ -0,0 +1,436 @@
# 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](https://datatracker.ietf.org/doc/html/rfc3986#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](https://datatracker.ietf.org/doc/html/rfc3986). 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](https://datatracker.ietf.org/doc/html/rfc3986#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](https://datatracker.ietf.org/doc/html/rfc3986#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](https://datatracker.ietf.org/doc/html/rfc5952#section-4)
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](https://datatracker.ietf.org/doc/html/rfc5952#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](https://datatracker.ietf.org/doc/html/rfc3986#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`](https://www.php.net/manual/en/function.parse-url.php) 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](https://datatracker.ietf.org/doc/html/rfc3986#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](https://datatracker.ietf.org/doc/html/rfc3986#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()`:
```php
(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.
```php
$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.
```php
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](https://datatracker.ietf.org/doc/html/rfc3986#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%b1b``http://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.org``http://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/myfile``file:///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.html``http://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.html``http://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.
## Related
- [PSR-7 Messages](psr-7-messages.md)
- [Streams and Decorators](streams-and-decorators.md)
- [URI and MIME Helpers](uri-and-mime-helpers.md)
- [Header and Query Helpers](header-and-query-helpers.md)